我的客户端有一个 oracle 数据库,一个对象通过 objOutStream.writeObject 作为 blob 字段持久化,该对象现在有一个不同的 serialVersionUID
(即使该对象没有变化,可能是不同的 jvm版本)并且当他们尝试反序列化时抛出异常:
java.io.InvalidClassException: CommissionResult; local class incompatible:
stream classdesc serialVersionUID = 8452040881660460728,
local class serialVersionUID = -5239021592691549158
他们从一开始就没有为 serialVersionUID
分配固定值,所以现在某些事情发生了变化,抛出了异常。现在他们不想丢失任何数据,为此我认为最好的方法是读取对象,反序列化它们,然后通过 XMLEncoder 再次持久化它们以避免将来出现诸如当前“类不兼容”错误之类的错误。
显然,该对象的 serialVersionUID
有 2 个不同的值,所以我想读取数据,尝试使用一个值,如果失败则尝试使用另一个值,为此我我尝试使用以下方法更改类的 serialVersionUID
the ASM api .我已经能够更改该值,但问题是如何在类上激活更改,因此当它被反序列化时 objInpStr.readObject()
将我修改后的类版本与我的特定 serializedVersionUID
。我制作了一个测试类来模拟真实环境,我取了一个对象(它的属性是具有不同 serialVersionUID
问题的对象)对象名称是 Reservation
属性是
佣金结果
:
public class Reservation implements java.io.Serializable {
private CommissionResult commissionResult = null;
}
public class CommissionResult implements java.io.Serializable{
}
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.SerialVersionUIDAdder;
public class SerialVersionUIDRedefiner extends ClassLoader {
public void workWithFiles() {
try {
Reservation res = new Reservation();
FileOutputStream f = new FileOutputStream("/home/xabstract/tempo/res.ser");
ObjectOutputStream out = new ObjectOutputStream(f);
out.writeObject(res);
out.flush();
out.close();
ClassWriter cw = new ClassWriter(0);
ClassVisitor sv = new SerialVersionUIDAdder(cw); //assigns a real serialVersionUID
ClassVisitor ca = new MyOwnClassAdapter(sv); //asigns my specific serialVerionUID value
ClassReader cr=new ClassReader("Reservation");
cr.accept(ca, 0);
SerialVersionUIDRedefiner loader= new SerialVersionUIDRedefiner();
byte[] code = cw.toByteArray();
Class exampleClass = loader.defineClass("Reservation", code, 0, code.length); //at this point the class Reservation has an especific serialVersionUID value that I put with MyOwnClassAdapter
loader.resolveClass(exampleClass);
loader.loadClass("Reservation");
DeserializerThread dt=new DeserializerThread();
dt.setContextClassLoader(loader);
dt.run();
} catch (Exception e) {
e.printStackTrace();
}}
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class DeserializerThread extends Thread {
public void run() {
try {
FileInputStream f2;
f2 = new FileInputStream("/home/xabstract/tempo/res.ser");
ObjectInputStream in = new ObjectInputStream(f2);
Reservation c1 = (Reservation)in.readObject();
System.out.println(c1);
} catch (Exception e) {
e.printStackTrace();
}
stop();
}
}
MyOwnClassAdapter Relevant code:
public void visitEnd() {
// asign SVUID and add it to the class
try {
cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
"serialVersionUID",
"J",
null,
new Long(-11001));//computeSVUID()));
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException("Error while computing SVUID for x"
, e);
}
super.visitEnd();
}
测试应该失败并返回 java.io.InvalidClassException
“本地类不兼容”
因为我在保存文件后更改了 serialVersionUID
并使用新的读取
de 文件,但它没有失败,所以这意味着 ObjectInputStream.readObject
不是
使用我修改后的 Reservation
类。
有什么想法吗?提前致谢。
!!!!!!!!!!!!!更新:
好的,可以重新定义 resultClassDescriptor 来覆盖流 serialVersionUID,但是,有些奇怪的事情发生了,正如我之前所说的那样 是否保留了该类的 2 个版本,serialVersionUID = -5239021592691549158L 的对象和其他值为 8452040881660460728L 的对象最后一个值为 如果我没有为本地类指定值,则生成一个。
-如果我没有为 serialVersionUID 指定值,则使用默认值 (8452040881660460728L),但无法反序列化 具有其他值,则会抛出一个错误,指出属性属于其他类型。
-如果我指定值 -5239021592691549158L,那么类将保留该值 成功反序列化,但其他人没有,类型相同的错误。
这是错误跟踪:
可能致命的反序列化操作。 java.io.InvalidClassException:覆盖序列化类版本不匹配:本地 serialVersionUID = -5239021592691549158 流 serialVersionUID = 8452040881660460728 java.lang.ClassCastException:无法将 java.util.HashMap 的实例分配给 com.posadas.ic.rules 实例中类型为 java.lang.String 的字段 com.posadas.ic.rules.common.commisionRules.CommissionResult.statusCode。 common.commisionRules.CommissionResult
当抛出这个错误时,类的值为 -5239021592691549158,如果更改 8452040881660460728 类的值已成功反序列化,那么,会发生什么?为什么会尝试为错误的类转换错误?
谢谢
最佳答案
Jorge 我在 http://forums.sun.com/thread.jspa?threadID=518416 上找到了一个解决方案哪个有效。
在您的项目中创建以下类。无论您在何处创建 ObjectInputStream 的对象,都应改用 DecompressibleInputStream,它会使用新版本的 Id 类反序列化旧对象。
public class DecompressibleInputStream extends ObjectInputStream {
public DecompressibleInputStream(InputStream in) throws IOException {
super(in);
}
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); // initially streams descriptor
Class localClass = Class.forName(resultClassDescriptor.getName()); // the class in the local JVM that this descriptor represents.
if (localClass == null) {
System.out.println("No local class for " + resultClassDescriptor.getName());
return resultClassDescriptor;
}
ObjectStreamClass localClassDescriptor = ObjectStreamClass.lookup(localClass);
if (localClassDescriptor != null) { // only if class implements serializable
final long localSUID = localClassDescriptor.getSerialVersionUID();
final long streamSUID = resultClassDescriptor.getSerialVersionUID();
if (streamSUID != localSUID) { // check for serialVersionUID mismatch.
final StringBuffer s = new StringBuffer("Overriding serialized class version mismatch: ");
s.append("local serialVersionUID = ").append(localSUID);
s.append(" stream serialVersionUID = ").append(streamSUID);
Exception e = new InvalidClassException(s.toString());
System.out.println("Potentially Fatal Deserialization Operation. " + e);
resultClassDescriptor = localClassDescriptor; // Use local class descriptor for deserialization
}
}
return resultClassDescriptor;
}
}
关于java - 当对象具有不同的 serialVersionUID 时,如何反序列化持久保存在数据库中的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/795470/