GVKun编程网logo

JPA实体何时以及为何应实现Serializable接口?(jpa的实现)

14

本文将为您提供关于JPA实体何时以及为何应实现Serializable接口?的详细介绍,我们还将为您解释jpa的实现的相关知识,同时,我们还将为您提供关于hibernate中联合主键要同时重写hash

本文将为您提供关于JPA实体何时以及为何应实现Serializable接口?的详细介绍,我们还将为您解释jpa的实现的相关知识,同时,我们还将为您提供关于hibernate中联合主键要同时重写hashCode()和equals()方法和实现Serializable接口、Java 基础 (十一)--Serializable 和 Externalizable 接口实现序列化、java.io.NotSerializableException 即使该类实现了 Serializable、java中Serializable接口作用详解的实用信息。

本文目录一览:

JPA实体何时以及为何应实现Serializable接口?(jpa的实现)

JPA实体何时以及为何应实现Serializable接口?(jpa的实现)

问题在标题中。下面我仅描述了我的一些想法和发现。

当我有一个非常简单的域模型(3个表没有任何关系)时,我所有的实体都没有实现Serializable。

但是,当域模型变得更加复杂时,我遇到了RuntimeException,它表示我的一个实体没有实现Serializable。

我使用Hibernate作为JPA实现。

我想知道:

  1. 它是特定于供应商的要求/行为吗?
  2. 我的可序列化实体会怎样?它们应该可序列化以存储或传输吗?
  3. 何时需要使我的实体可序列化?

答案1

小编典典

如果您混合使用HQL和本机SQL查询,通常会发生这种情况。在HQL中,Hibernate将您传递的类型映射到数据库可以理解的任何类型。运行本机SQL时,必须自己进行映射。如果您不这样做,则默认映射是序列化参数并将其发送到数据库(以希望它能理解它)。

hibernate中联合主键要同时重写hashCode()和equals()方法和实现Serializable接口

hibernate中联合主键要同时重写hashCode()和equals()方法和实现Serializable接口

关于联合主键

联合主键为什么要重写equals方法和hashCode方法,是为了保证唯一性

1、在数据库保证唯一性是使用的联合主键
2、把一系列的对象放到内存的时候,为了区分同名对象,数据库是使用联合主键来区分,内存中也是用相应的方式来区分,所以重写equals和 hashCode方法,而且,逻辑不能乱写,hashCode不能使用父类里面的,否则两个同样的对象就视为同一对象,在equals方法中,从内存的角 度讲, 只有联合主键中的每一个都相同,才叫相同,才应该返回true


当内容要放进hash表里面的时候,首先判断hash码是否相同,如果相同,则放在同一位置,每个位置往往装的是一个链表,链表上每个对象都是hash码相同的对象,所以在查找hash表的时候直接找到hash码对应的位置,然后进行equals方法进行判断,

所以,总结,当很多对象被放进hash表的时候,相同的hash码的对象放在同一个位置,查找的时候先找到对应的位置的链表,然后进行equals,找到对应的对象

Java 基础 (十一)--Serializable 和 Externalizable 接口实现序列化

Java 基础 (十一)--Serializable 和 Externalizable 接口实现序列化

  序列化在日常开发中经常用到,特别是涉及到网络传输的时候,例如调用第三方接口,通过一个约定好的实体进行传输,这时你必须实现序列

化,这些都是大家都了解的内容,所以文章也会讲一下序列化的高级内容。

序列化与反序列化简单认知:

  我们知道,对象在不具有可达性的时候,会被 GC,这些对象都是保存在堆中,而现实中,我们可能需要将对象进行持久化,并且在需要的时候

进行读取转换,这就是序列化的工作。

1、序列化: 

  将一个对象转换成字节流或者说是字节数组,并且可以存储或传输的形式的过程。

  存储:可以把一个对象存储到文件、数据库等

  网络传输:可以转化成字节或 XML 进行网络传输

2、反序列化:

  和序列化是一个相反的过程,在需要的时候,把字节数组转化成对象。

序列化广泛应用于远程调用等所有涉及网络传输的地方

序列化相关接口:

  Serializable、Externalizable、ObjectOutput、ObjectInput、ObjectOutputStream、ObjectInputStream

Serializable 接口:

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Student implements Serializable {

    private int id;
    private String name;
    private int sex;
    private transient String addr;
} 
public static void main(String[] args) throws Exception{
	Student student = new Student(1001, "sam", 1, "SH");
	File file = new File("D:\\a.txt");
	FileOutputStream fileOutputStream = new FileOutputStream(file);
	ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
	outputStream.writeObject(student);
	outputStream.close();

	ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
	Student student1 = (Student)inputStream.readObject();
	System.out.println(student1.toString());
	inputStream.close();
}

结果:

Student(id=1001, name=sam, sex=1, addr=null)

  我们通过 Binary Viewer 查看这个二进制文件 a.txt,下面二进制内容解释参考自:https://www.cnblogs.com/xrq730/p/4821958.html

第一部分:序列化文件头

  1、AC ED:STREAM_MAGIC 序列化协议

  2、00 05:STREAM_VERSION 序列化协议版本

  3、73:TC_OBJECT 声明这是一个新的对象

第二部分:序列化的类的描述,在这里是 Student 类

  1、72:TC_CLASSDESC 声明这里开始一个新的 class

  2、00 18: 十进制的 24,表示 class 名字的长度是 24 个字节

  3、63 6F 6D ... 6E 74 20:表示的是 “com.it.exception.Student” 这一串字符,可以数一下确实是 24 个字节

  4、00 00 00 00 00 00 00 01:SerialVersion,序列化 ID,1

  5、02:标记号,声明该对象支持序列化

  6、00 03:该类所包含的域的个数为 3 个

第三部分:是对象中各个属性项的描述

  1、49:int 类型

  2、00 02:十进制的 2,表示字段长度

  3、69 64:表示字段 id

  4、49:int 类型

省略了 sex、name 属性,可以自行查看

  5、74:TC_STRING,代表一个 new String,用 String 来引用对象

第四部分:该对象父类的信息,如果没有父类就没有这部分。有父类和第 2 部分差不多

  1、00 12:十进制的 18,表示父类的长度

  2、4C 6A 61 ... 6E 67 3B:“L/java/lang/String;” 表示的是父类属性

  3、78:TC_ENDBLOCKDATA,对象块结束的标志

  4、70:TC_NULL,说明没有其他超类的标志

第五部分:输出对象的属性项的实际值,如果属性项是一个对象,这里还将序列化这个对象,规则和第 2 部分一样

  1、00 03:十进制的 3,属性的长度

  2、73 61 6D:字符串 "sam",name 的属性值

以上是二进制文件的解析,可以得出结论:

  1、序列化之后保存的是对象的信息

  2、被声明为 transient 的属性不会被序列化,这就是 transient 关键字的作用,addr 字段并没有保存

所以,我们得出结论,static 字段也不会被序列化,因为 static 变量属于类的

Externalizable 接口:

public interface Externalizable extends java.io.Serializable {

    void writeExternal(ObjectOutput out) throws IOException;
        restored cannot be found.

    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

使用样例:

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Student implements Externalizable{

    private static final long serialVersionUID = 1L;

    private int id;
    private String name;
    private int sex;

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(id);
        out.writeObject(name);
        out.writeObject(sex);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        id = (Integer)in.readObject();
        name = (String)in.readObject();
        sex = (Integer)in.readObject();
    }
}
public class Test {

    public static void main(String[] args) throws Exception{
        Student student = new Student(1001, "sam", 1);
        File file = new File("D:\\a.txt");
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
        outputStream.writeObject(student);
        outputStream.close();

        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
        Student student1 = (Student)inputStream.readObject();
        System.out.println(student1.toString());
        inputStream.close();
    }
}

结果:

Student(id=1001, name=sam, sex=1)

从结果看 Externalizable 接口同样可以实现序列化和反序列化,但是有些地方不太一样,需要注意

  1、相关类必须有默认构造器,否则会抛出异常,是因为在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被

保存对象的字段的值分别填充到新对象中。

  2、需要重写 writeExternal 和 readExternal 方法,去控制序列化,并且写入字段顺序和读取顺序要保持一致,写入和读取支持多种类型,不必

一定使用 object,这样不用类型转换

自定义序列化:

  我们使用序列化的时候,一般情况都是使用默认的方式,而如果在一些特殊场景下我们需要进行特殊处理,例如字段加密,因为序列化是不安

全的。

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Student implements Serializable{

    private static final long serialVersionUID = 1L;

    private int id;
    private String name;
    private int sex;

    private void writeObject(ObjectOutputStream outputStream) throws Exception {
        outputStream.defaultWriteObject();
        outputStream.writeBoolean(true);
    }

    private void readObject(ObjectInputStream inputStream) throws Exception {
        inputStream.defaultReadObject();
        boolean flag = inputStream.readBoolean();
        System.out.println("flag: " + flag);
    }
}

测试代码不变

flag: true
Student(id=1001, name=sam, sex=1)

从代码上看和 Externalizable 接口几乎一样的,通过 writeObject () 和 readObject () 实现自定义的过程

原因:

  虚拟机会首先试图调用对象里的 writeObject () 和 readObject (),进行用户自定义的序列化和反序列化。如果没有这样的方法,那么默认调用的

是 ObjectOutputStream 的 defaultWriteObject 以及 ObjectInputStream 的 defaultReadObject 方法

我们在查看 jdk 集合的源码中可以看到,ArrayList、HashMap 等在实现序列化的时候,都是自定义 writeObject () 和 readObject () 的

PS:虚拟机通过反射来调用 writeObject () 和 readObject ()

ArrayList 序列化:

public class ArrayList<E> extends AbstractList<E>
		implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

	transient Object[] elementData; //通过数组保存集合数据

	private void readObject(java.io.ObjectInputStream s)
			throws java.io.IOException, ClassNotFoundException {
		elementData = EMPTY_ELEMENTDATA;

		s.defaultReadObject();

		s.readInt(); // ignored

		if (size > 0) {
			ensureCapacityInternal(size);

			Object[] a = elementData;
			// Read in all elements in the proper order.
			for (int i=0; i<size; i++) {
				a[i] = s.readObject();
			}
		}
	}
	private void writeObject(java.io.ObjectOutputStream s)
			throws java.io.IOException{
		int expectedModCount = modCount;
		s.defaultWriteObject();

		// Write out size as capacity for behavioural compatibility with clone()
		s.writeInt(size);

		// Write out all elements in the proper order.
		for (int i=0; i<size; i++) {
			s.writeObject(elementData[i]);
		}

		if (modCount != expectedModCount) {
			throw new ConcurrentModificationException();
		}
	}
}

ArrayList 这样实现的目的:

  ArrayList 是动态数组,数组中的数据在达到阀值就会扩容,如果数组扩容后长度设为 100,而里面只存放了 10 条数据,那就会序列化 90 个

null 元素。为了保证在序列化的时候不会将这么多 null 同时进行序列化,ArrayList 把元素数组设置为 transient,然后通过遍历数组讲数据进行

序列化和反序列化

总结:

  1、在 Java 中,只要一个类实现了 Serializable 接口,那么它就可以被序列化

  2、通过ObjectOutputStreamObjectInputStream对对象进行序列化及反序列化

  3、当父类继承 Serializable 接口,所有子类都可以被序列化

  4、子类实现了 Serializable 接口,如果想要父类的属性也能实现序列化,必须父类也实现 Serializable 接口,否则父类中的属性不能序列

化(不报错,数据丢失),但是在子类中属性仍能正确序列化

  5、如果序列化的属性是对象,则这个对象也必须实现 Serializable 接口,否则会报错  

  6、序列化并不保存静态变量

  7、反序列化能否成功,要求:①. 类路径相同,②. 序列化 ID 保持一致 (serialVersionUID),否则无法成功

  8、反序列化时,如果对象的属性有修改或删减,则修改的部分属性会丢失,但不会报错

  5、序列化数据如果比较敏感,可以采用加密的方式,增加一定安全性

 

内容参考:

  https://www.cnblogs.com/xrq730/p/4821958.html

  http://www.importnew.com/18024.html

  https://www.ibm.com/developerworks/cn/java/j-lo-serial/

java.io.NotSerializableException 即使该类实现了 Serializable

java.io.NotSerializableException 即使该类实现了 Serializable

如何解决java.io.NotSerializableException 即使该类实现了 Serializable?

我正在构建一个 music.player 并将我的音乐库存储在 HashMap 中。用户应能够添加和删除歌曲。我想在程序重新启动时保存这个 HashMap。 但是我是否遇到过这个警告:

Exception in thread "main" java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: musicplayer.song

研究表明我必须在我的 Song 类中实现可序列化接口。我做了,但仍然有这个警告。 我的歌曲课:

package musicplayer;
//Song-Klasse,speichert alle Attribute und Methoden eines Songs. Funktioniert soweit
import java.io.File;
import java.io.IOException;
import java.io.Serializable;

public class Song implements Serializable {
    private static final long serialVersionUID = 4390482518182625971L;
    //Attribute
    File file;
    Clip clip;
    String string;
    //...

MusicDaten - 课堂

package musicplayer;


public class MusicDaten implements Serializable {
    
    private static Map<String,Song> all; //= new HashMap<String,Song>();
    private File file = new File("C://Users//ThinkPad T450s//git//testproject//musicplayer//SongInfo.ser");
    
// ...

    public MusicDaten() throws ClassNotFoundException,IOException {
        this.setSavedSongs();
    }
    
    
    public void setSavedSongs() throws IOException,ClassNotFoundException  { //initialisziert HashMap mit den gespeicherten Songs
        FileInputStream fileIn = new FileInputStream(file);
        ObjectInputStream in = new ObjectInputStream(fileIn);
        all = (HashMap<String,Song>) in.readobject();
        in.close();
        fileIn.close();
    }
    public void save() throws IOException {   //Speicher HashMap
        FileOutputStream fileOut = new FileOutputStream(file);
        ObjectOutputStream out = new ObjectOutputStream(fileOut);
        out.writeObject(all);
        out.close();
        fileOut.close();
        System.out.println("Songinfo saved");
    }

谢谢你的帮助。 (我已经编辑了这个问题,因为之前还不太清楚)

解决方法

实施 Serializable 是不够的。

如果您尝试序列化一个对象,它的所有非瞬态属性也会被序列化。如果这些属性中的任何一个不是 Serializable,它将不起作用。

在您的情况下,Song 包含类型为 File 的属性,而 File 不可序列化。使用 Clip,您会遇到同样的问题。

为了解决这个问题,您可以进行自定义序列化。

查看the docs of Serializable,您可以找到:

在序列化和反序列化过程中需要特殊处理的类必须实现具有以下确切签名的特殊方法:

 private void writeObject(java.io.ObjectOutputStream out)
     throws IOException
 private void readObject(java.io.ObjectInputStream in)
     throws IOException,ClassNotFoundException;
 private void readObjectNoData()
       throws ObjectStreamException;

writeObject 方法负责为其特定类写入对象的状态,以便相应的 readObject 方法可以恢复它。保存对象字段的默认机制可以通过调用 out.defaultWriteObject 来调用。该方法不需要关心属于它的超类或子类的状态。通过使用 writeObject 方法或使用 DataOutput 支持的原始数据类型的方法将各个字段写入 ObjectOutputStream 来保存状态。

readObject 方法负责从流中读取并恢复类字段。它可以调用 in.defaultReadObject 来调用默认机制来恢复对象的非静态和非瞬态字段。

这意味着您可以创建方法 writeObjectreadObject,您可以在其中指定如何(反)序列化对象。

如果要保留支持序列化的属性的默认(反)序列化,可以将所有不支持序列化的字段标记为transient,并在{{ 1}}/out.defaultWriteObject 方法。

标记属性 in.defaultReadObject 意味着序列化忽略它。然后您可以使用您的自定义逻辑。


请注意,序列化会带来一些问题,您可能不想使用它。

一方面,如果您对不受信任的数据进行反序列化,可能会导致严重的拒绝服务甚至远程代码执行漏洞。这也在 the docs of Serializable 中注明:

警告:不可信数据的反序列化本质上是危险的,应该避免。应根据 Secure Coding Guidelines for Java SE 的“序列化和反序列化”部分仔细验证不受信任的数据。 Serialization Filtering 描述了防御性使用串行过滤器的最佳做法。

序列化的另一个问题是,它会将您的应用程序绑定到固定格式,并且如果您在最初创建应用程序时没有仔细考虑,则在更新应用程序时很难与旧的序列化数据兼容。

有关这方面的更多信息,您可能需要考虑阅读 writeObject 一书。

java中Serializable接口作用详解

java中Serializable接口作用详解

这篇文章主要为大家详细介绍了java中Serializable接口作用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文为大家解析java中Serializable接口的作用,具体内容如下

 1.(serializable)主要支持对象的回复,所以可以用来保存当前的程序系统状态,远程方法调用RMI(远程机器必须含有必要的.class文件,否则将掷出classNotFound   Exception),但是因为它将对象数据自动全部保存,你根本无法插手,因此对于一些敏感字段(如:password)存在安全问题。但相应有很多解决的方法,例如可以在敏感字段的声明中使用transient关键字,或者去继承externalizable接口,自己来实现readExternal()和writerExternal()方法,再或者继承serializable接口,但提供private   void   writeObject(ObjectOutputStream   s)等方法...   ...但注意static   成员的保存仍需要你的介入。

2.1.网络传输   

2.数据库持久   

3.把对象保存为文件形式,以便以后还原 

Object serialization的定义:

Object serialization 允许你将实现了Serializable接口的对象转换为字节序列,这些字节序列可以被完全存储以备以后重新生成原来的对象。serialization不但可以在本机做,而且可以经由网络操作(RMI)。这个好处是很大的----因为它自动屏蔽了操作系统的差异,字节顺序(用Unix下的c开发过网络编程的人应该知道这个概念)等。比如,在Window平台生成一个对象并序列化之,然后通过网络传到一台Unix机器上,然后可以在这台Unix机器上正确地重构这个对象。Object serialization主要用来支持2种主要的特性:1、Java的RMI(remote method invocation).RMI允许象在本机上一样操作远程机器上的对象。当发送消息给远程对象时,就需要用到serializaiton机制来发送参数和接收返回直。2、Java的JavaBeans. Bean的状态信息通常是在设计时配置的。Bean的状态信息必须被存起来,以便当程序运行时能恢复这些状态信息。这也需要serializaiton机制。二、sakulagi和rollingpig说的持久化我也说一下。我觉得你们说的应该是英文里的persistence.但是Java语言里现在只支持lightweight persistence,就是轻量级持久化,这是通过serialization机制来实现的。persistence是指一个对象的生命周期不由程序是否执行来决定,即使是在程序终止时这个对象也存在。它把一个serializable的对象写到磁盘(本机或其他机器上的非RAM存储器),并在程序重新调用时再读取对象到通常的RAM存储器。为什么说Java的serialization机制实现的是lightweight persistence?因为你必须显式的序列化和反序列化程序里的对象;而不是直接由一个关键词来定义一个对象是序列化的然后由系统做相应的处理。下面是关于序列化的一个实例:程序名称:SerializationDemo.java程序主题:实现对象的序列化和反序列化程序说明:该程序由实例化一个MyClass类的对象开始,该对象有三个实例变量,类型分别为String、int、double,是希望存储和恢复的信息。代码内容import java.io.*; public class SerializationDemo{ public static void main(String args[]){ //Object serialization try{ MyClass object1=new MyClass("Hello",-7,2.7e10); System.out.println("object1:"+object1); FileOutputStream fos=new FileOutputStream("serial"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(object1); oos.flush(); oos.close(); } catch(Exception e){ System.out.println("Exception during serialization:"+e); System.exit(0); } //Object deserialization try{ MyClass object2; FileInputStream fis=new FileInputStream("serial"); ObjectInputStream ois=new ObjectInputStream(fis); object2=(MyClass)ois.readobject(); ois.close(); System.out.println("object2:"+object2); } catch(Exception e){ System.out.println("Exception during deserialization:"+e); System.exit(0); } } } class MyClass implements Serializable{ String s; int i; double d; public MyClass(String s,int i,double d){ this.s=s; this.i=i; this.d=d; } public String toString(){ return "s="+s+";i="+i+";d="+d; } }程序运行结果:object1和object2的实例变量是一样的,输出如下:[code:1:a55efb5f91]object1:s=Hello;i=-7;d=2.7E10object2:s=Hello;i=-7;d=2.7E10 一个小例子:import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * @author Yan Chenyang * * Todo 要更改此生成的类型注释的模板,请转至 * 窗口 - 首选项 - Java - 代码样式 - 代码模板 */ public class JFunction implements Serializable{ private double[][] bounds; private int vnum; private double result; private String funcname; public JFunction(){ super(); this.bounds=null; this.vnum=0; this.result=0.0; this.funcname=null; } public JFunction(double[][] bounds){ super(); this.bounds=bounds; this.vnum=bounds[0].length; this.result=0.0; this.funcname="Function 1"; } public double Func(double[] var){ result=0.0; for(int i=0;i序列化 用在 对象编码成字节流及从字节流编码重构对象。   序列化 为远程通信提供了标准的wire-level协议。   要使类的实例实现序列化,只要在它的声明中加入implements     java.io.Serializable   但是却由一些隐患   1.实现了序列化后,一旦发布,讲削弱改变类实现的灵活性。   2.增加了bug和安全漏洞的的可能性   3.当你的新版本发布时增加了相关的测试上的问题。   类应尽可能少的实现Serializable,接口也应该少去扩展它。以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小编。

关于JPA实体何时以及为何应实现Serializable接口?jpa的实现的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于hibernate中联合主键要同时重写hashCode()和equals()方法和实现Serializable接口、Java 基础 (十一)--Serializable 和 Externalizable 接口实现序列化、java.io.NotSerializableException 即使该类实现了 Serializable、java中Serializable接口作用详解等相关内容,可以在本站寻找。

本文标签: