Ran*_*Ran 7 java serialization hibernate
我的项目是一个使用Hibernate和服务器的java项目.EJB3Weblogic
为了方便起见(据我所知,这是典型的hibernate),一些实体包含循环依赖(父知道孩子,孩子知道父母).此外,对于某些子类 - hashCode()和equals()方法依赖于它们的父级(因为它是一个唯一的键).
在工作时我看到了一个奇怪的行为 - 从服务器返回到客户端的一些集合虽然包含正确的元素,但它们的行为却没有包含任何内容.例如,一个简单的测试,例如:set.contains(set.toArray()[0])返回false虽然hashCode()方法很好.
经过大量的调试后,我能够生成2个简单的类来重现问题(我可以向你保证hashCode(),这两个类中的函数都是自反的,传递的和对称的):
package test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
public class ClientTest implements Serializable {
public static void main(String[] args) throws Exception {
SerializableClass serializationTest = new SerializableClass();
FieldOfSerializableClass hashMember = new FieldOfSerializableClass();
hashMember.setParentLink(serializationTest);
serializationTest.setHashCodeField("Some string");
serializationTest
.setSomeSet(new HashSet<FieldOfSerializableClass>());
serializationTest.getSomeSet().add(hashMember);
System.out.println("Does it contain its member? (should return true!) "
+ serializationTest.getSomeSet().contains(hashMember));
new ObjectOutputStream(new FileOutputStream("temp"))
.writeObject(serializationTest);
SerializableClass testAfterDeserialize = (SerializableClass) new ObjectInputStream(
new FileInputStream(new File("temp"))).readObject();
System.out.println("Does it contain its member? (should return true!) "
+ testAfterDeserialize.getSomeSet().contains(hashMember));
for (Object o : testAfterDeserialize.getSomeSet()) {
System.out.println("Does it contain its member by equality? (should return true!) "+ o.equals(hashMember));
}
}
public static class SerializableClass implements Serializable {
private Set<FieldOfSerializableClass> mSomeSet;
private String mHashCodeField;
public void setSomeSet(Set<FieldOfSerializableClass> pSomeSet) {
mSomeSet = pSomeSet;
}
public Set<FieldOfSerializableClass> getSomeSet() {
return mSomeSet;
}
public void setHashCodeField(String pHashCodeField) {
mHashCodeField = pHashCodeField;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
System.out.println("In hashCode - value of mHashCodeField: "
+ mHashCodeField);
result = prime
* result
+ ((mHashCodeField == null) ? 0 : mHashCodeField.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SerializableClass other = (SerializableClass) obj;
if (mHashCodeField == null) {
if (other.mHashCodeField != null) {
return false;
}
} else if (!mHashCodeField.equals(other.mHashCodeField))
return false;
return true;
}
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
System.out.println("Just started serializing");
in.defaultReadObject();
System.out.println("Just finished serializing");
}
}
public static class FieldOfSerializableClass implements Serializable {
private SerializableClass mParentLink;
public void setParentLink(SerializableClass pParentLink) {
mParentLink = pParentLink;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((mParentLink == null) ? 0 : mParentLink.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
FieldOfSerializableClass other = (FieldOfSerializableClass) obj;
if (mParentLink == null) {
if (other.mParentLink != null) {
return false;
}
} else if (!mParentLink.equals(other.mParentLink))
return false;
return true;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这产生了以下输出:
In hashCode - value of mHashCodeField: Some string
In hashCode - value of mHashCodeField: Some string
Does it contain its member? (should return true!) true
Just started serializing
In hashCode - value of mHashCodeField: null
Just finished serializing
In hashCode - value of mHashCodeField: Some string
Does it contain its member? (should return true!) false
Does it contain its member by equality? (should return true!) true
这告诉我Java序列化对象的顺序是错误的!它开始序列化字符串之前的Set,从而导致上述问题.
在这种情况下我该怎么办?有没有选择(除了实现readResolve许多实体...)以指导java按特定顺序序列化一个类?此外,一个实体hashCode以其父母为基础是否从根本上是错误的?
编辑:一位同事提出了一个解决方案 - 因为我正在使用Hibernate,所以每个实体都有一个唯一的长ID.我知道Hibernate指定不在equals方法中使用此ID - 但是hashCode呢?使用此唯一ID作为哈希码似乎可以解决上述问题,同时将性能问题的风险降至最低.将ID用作哈希码是否还有其他影响?
第二次编辑:我去实现了我的部分解决方案(现在所有的enteties都使用了hashCode()函数的ID字段,并且不再继续使用其他enteties)但是,唉,序列化错误仍然困扰着我!下面是另一个序列化错误的示例代码.我认为发生了这种情况--ClassA开始反序列化,看到它有一个ClassB来反序列化,在它反序列化它的ID之前,它开始反序列化ClassB.B开始反序列化并且看到它有一组ClassA.ClassA实例是部分反序列化的,但即使ClassB将它添加到Set(使用ClassA的缺失ID),完成deserializning,然后ClassA完成并发生错误.
我该怎么做才能解决这个问题?!循环依赖是Hibernate中一个非常常用的实践,我不能接受它,我是唯一有这个问题的人.
另一种可能的解决方案是为hashCode设置一个专用变量(将由对象的ID计算)并确保(查看readObject和writeObject)它将在非常其他对象之前读取.你怎么看?这个解决方案有什么缺点吗?
示例代码:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
public class Test implements Serializable
{
public static void main(String[] args) throws Exception
{
ClassA aClass = new ClassA();
aClass.setId(Long.valueOf(321));
ClassB bClass = new ClassB();
bClass.setId(Long.valueOf(921));
Set<ClassA> set = new HashSet<ClassA>();
set.add(aClass);
bClass.setSetfield(set);
aClass.setBField(bClass);
Set<ClassA> goodClassA = aClass.getBField().getSetfield();
Set<ClassA> badClassA = serializeAndDeserialize(aClass).getBField().getSetfield();
System.out.println("Does it contain its member? (should return true!) " + goodClassA.contains(goodClassA.toArray()[0]));
System.out.println("Does it contain its member? (should return true!) " + badClassA.contains(badClassA.toArray()[0]));
}
public static ClassA serializeAndDeserialize(ClassA s) throws Exception
{
new ObjectOutputStream(new FileOutputStream(new File("temp"))).writeObject(s);
return (ClassA) new ObjectInputStream(new FileInputStream(new File("temp"))).readObject();
}
public static class ClassB implements Serializable
{
private Long mId;
private Set<ClassA> mSetfield = new HashSet<ClassA>();
public Long getmId() {
return mId;
}
public void setId(Long mId) {
this.mId = mId;
}
public Set<ClassA> getSetfield() {
return mSetfield;
}
public void setSetfield(Set<ClassA> mSetfield) {
this.mSetfield = mSetfield;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((mId == null) ? 0 : mId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ClassB other = (ClassB) obj;
if (mId == null) {
if (other.mId != null)
return false;
} else if (!mId.equals(other.mId))
return false;
return true;
}
}
public static class ClassA implements Serializable
{
private Long mId;
private ClassB mBField;
public Long getmId() {
return mId;
}
public void setId(Long mId) {
this.mId = mId;
}
public ClassB getBField() {
return mBField;
}
public void setBField(ClassB mBField) {
this.mBField = mBField;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((mId == null) ? 0 : mId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ClassA other = (ClassA) obj;
if (mId == null) {
if (other.mId != null)
return false;
} else if (!mId.equals(other.mId))
return false;
return true;
}
}
}
Run Code Online (Sandbox Code Playgroud)
equals 方法必须是自反、传递和对称的......
hashCode 方法必须具有以下属性:
hashCode的通用约定是:
每当在 Java 应用程序执行期间对同一对象多次调用 hashCode 方法时,只要对象上的 equals 比较中使用的信息没有被修改,hashCode 方法就必须始终返回相同的整数。从应用程序的一次执行到同一应用程序的另一次执行,该整数不需要保持一致。
如果根据 equals(Object) 方法两个对象相等,则对这两个对象调用 hashCode 方法必须产生相同的整数结果。
如果两个对象根据 equals(java.lang.Object) 方法不相等,则不要求对这两个对象中的每一个调用 hashCode 方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
在这里,看起来反序列化期间用于将条目放入集合中的 hashCode 与 contains() 期间计算的 hashCode 不同。顺便说一句,正如您注意到该条目位于Set 中一样,您只是无法通过它的 hashCode 访问它,如果您循环 Set 的内容,您将找到元素。
可能的解决方案:
[编辑]:看起来你并不孤单bug_id=4957674
| 归档时间: |
|
| 查看次数: |
1469 次 |
| 最近记录: |