Java 当对象具有不同的serialVersionUID时,如何反序列化保留在db中的对象
我的客户端有一个oracle数据库,一个对象通过objOutStream.writeObject持久化为blob字段,该对象现在有一个不同的Java 当对象具有不同的serialVersionUID时,如何反序列化保留在db中的对象,java,serialization,serialversionuid,Java,Serialization,Serialversionuid,我的客户端有一个oracle数据库,一个对象通过objOutStream.writeObject持久化为blob字段,该对象现在有一个不同的serialVersionUID(即使该对象没有更改,可能是不同的jvm版本),当他们尝试反序列化时,会引发异常: java.io.InvalidClassException: CommissionResult; local class incompatible: stream classdesc serialVersionUID = 845204088
serialVersionUID
(即使该对象没有更改,可能是不同的jvm版本),当他们尝试反序列化时,会引发异常:
java.io.InvalidClassException: CommissionResult; local class incompatible:
stream classdesc serialVersionUID = 8452040881660460728,
local class serialVersionUID = -5239021592691549158
他们从一开始就没有为serialVersionUID
分配固定值,所以现在发生了一些变化,引发了异常。现在他们不想丢失任何数据,要做到这一点,我认为最好的方法是读取对象,反序列化它们,然后通过XMLEncoder再次持久化它们,以避免将来出现类似当前“类不兼容”错误的错误
显然,该对象的serialVersionUID
持久化有两个不同的值,因此我想读取数据,尝试使用一个值,如果失败,则尝试使用另一个值,为此,我尝试使用
. 我已经能够更改该值,但问题是如何在类上激活更改,以便在对其进行反序列化时,objInpStr.readObject()
使用我的特定serializedVersionId
获取该类的修改版本。我制作了一个测试类来模拟真实的环境,我取了一个对象(它的属性是具有不同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 file,但它没有失败,因此这意味着ObjectInputStream.readObject
未被激活
使用我的预订
类的修改版本
有什么想法吗?提前谢谢
!!!!!!!!!!!!!更新:
好的,可以重新定义resultClassDescriptor来覆盖流serialVersionUID,但是,发生了一些奇怪的事情,正如我前面所说的,似乎有
类的两个版本是否持久化,serialVersionUID为-5239021592691549158L的对象和845204081660460728L值的其他对象最后一个值为
如果我没有为本地类指定值,则生成一个
-如果我没有为serialVersionUID指定一个值,那么将使用默认值(845204081660460728L),但不可能对该对象进行反序列化
如果具有另一个值,则抛出一个错误,指出属性属于其他类型
-如果我指定值-52390215926991549158L,那么类将使用该值持久化
已成功反序列化,但未成功反序列化其他类型的相同错误
这是错误跟踪:
可能致命的反序列化操作。java.io.InvalidClassException:重写序列化类版本不匹配:本地SerialVersionId=-5239021592691549158 stream SerialVersionId=845204081660460728
java.lang.ClassCastException:无法将java.util.HashMap的实例分配给com.posadas.ic.rules.common.CommissionRules.CommissionResult.statusCode字段,该字段的类型为com.posadas.ic.rules.common.CommissionRules.CommissionResult
抛出此错误时,如果更改,则类的值为-52390215926911549158
845204081660460728的值表示类已成功反序列化,那么,会发生什么情况?为什么那个错误试图为错误的类施放
谢谢我可能遗漏了一些东西,但听起来你想做一些比必要的更复杂的事情。如果: (a) 您使用当前的类定义(即源代码)并将其串行UID硬编码为旧的(或旧的)类定义,然后使用该类定义反序列化序列化的实例 (b) 在您正在读取的字节流中,在包装ObjectInputStream之前,是否用新的串行UID替换旧的UID 好的,只是澄清一下(b)。例如,如果我有一个像这样的小类:
public static class MyClass implements Serializable {
static final long serialVersionUID = 0x1122334455667788L;
private int myField = 0xff;
}
ACED000573720011746573742E546573 ’..sr..test.Tes
74244D79436C61737311223344556677 t$MyClass."3DUfw
880200014900076D794669656C647870 ?...I..myFieldxp
000000FF ...ÿ
然后,当数据序列化时,它看起来如下所示:
public static class MyClass implements Serializable {
static final long serialVersionUID = 0x1122334455667788L;
private int myField = 0xff;
}
ACED000573720011746573742E546573 ’..sr..test.Tes
74244D79436C61737311223344556677 t$MyClass."3DUfw
880200014900076D794669656C647870 ?...I..myFieldxp
000000FF ...ÿ
每行16个字节,每个字节2个十六进制数字。如果仔细观察,在第二行中,9个字节(18位),您将看到串行版本ID开始(1122…)。
因此,在我们这里的数据中(您的数据略有不同),串行版本ID的偏移量是16+9=25(或十六进制中的0x19)。因此,在我开始反序列化之前,如果我想将这个串行版本ID更改为其他内容,那么我需要在偏移量25处写入我的新编号:
byte[] bytes = ... serialised data ...
ByteBuffer bb = ByteBuffer.wrap(bytes);
bb.putLong(25, newSerialVersionUID);
然后我就照常进行:
ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(bytes));
MyClass obj = (MyClass) oin.readObject();
如果数据库中存储了该类的多个版本,那么在一次过程中将它们反序列化并升级为一致的序列化格式可能会非常棘手 如果可能的话,您可以使用一列更改表,以标记序列化对象是否已被处理。然后对每个
serialVersionUID
传递表,在表中尝试处理尚未处理的任何对象。如果更新程序遇到无法处理的序列化对象,您可以捕获InvalidClassException
并继续下一条记录,记下版本号,以便再次通过
这有点乏味,但非常简单
Java序列化有一些非常好的特性来支持类的演化。然而,你必须意识到你在做什么。可能所有对象实际上都有相同的数据,但没有注意维护版本ID
在将所有对象更新到同一版本后,可以继续使用序列化。在向类中添加新字段时要小心,这些字段的默认值(布尔值为false、对象为null、整数为零等)是有意义的。您应该能够绕过