私のクライアントには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つの異なる値があるので、データを読みたいのですが、1つの値で試して、失敗した場合は他の値で試してください。そうするために、をserialVersionUID
使用してクラスの を
変更しようとしましたASM API . 値を変更することはできましたが、問題はクラスの変更をアクティブにする方法です。そのため、シリアル化解除されたときにobjInpStr.readObject()
、変更したバージョンのクラスを特定の .xml で取得しますserializedVersionUID
。実際の環境をシミュレートするテスト クラスを作成しました。オブジェクトを取得します (別のserialVersionUID
問題を持つオブジェクトをプロパティとして持っています)。オブジェクト名はReservation
プロパティです
CommissionResult
。
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
ObjectInputStream.readObject
Reservation
何か案は?前もって感謝します。
!!!!!!!!!!!!!アップデート:
わかりました、resultClassDescriptor を再定義してストリーム serialVersionUID をオーバーライドすることは可能ですが、前に言ったように、クラスの 2 つのバージョンが永続化されているように見える奇妙なことが起こります。 value は、ローカル クラスに値を指定しない場合に生成されるものです。
-serialVersionUID の値を指定しない場合、デフォルト値 (8452040881660460728L) が使用されますが、他の値を持つオブジェクトをシリアル化解除することはできません。プロパティが他のプロパティであるというエラーがスローされます。タイプ。
-値 -5239021592691549158L を指定した場合、その値で永続化されたクラスは正常に逆シリアル化されますが、他の型は同じエラーになります。
これはエラートレースです:
潜在的に致命的な逆シリアル化操作。java.io.InvalidClassException: シリアライズされたクラス バージョンの不一致を上書きしています: ローカル serialVersionUID = -5239021592691549158 ストリーム serialVersionUID = 8452040881660460728 com.posadas.ic.rules.common.commisionRules.CommissionResult のインスタンス内のタイプ java.lang.String の CommissionResult.statusCode
このエラーがスローされたとき、クラスの値は -5239021592691549158 でした。値を 8452040881660460728 に変更すると、クラスは正常に逆シリアル化されます。間違ったクラスにキャストしようとするエラーはなぜですか?
ありがとう