Java Apache Ignite:反序列化后在Hashmap中找不到对象
我对ApacheIgnite序列化/反序列化有一个问题,与字段反序列化的顺序有关。我需要在Ignite缓存中放置一个“B”的实例,如下所示:Java Apache Ignite:反序列化后在Hashmap中找不到对象,java,serialization,hashmap,ignite,binary-serialization,Java,Serialization,Hashmap,Ignite,Binary Serialization,我对ApacheIgnite序列化/反序列化有一个问题,与字段反序列化的顺序有关。我需要在Ignite缓存中放置一个“B”的实例,如下所示: public class A { private final String name; public A(String name) { this.name = name; } public String getName() { return name; } } public c
public class A {
private final String name;
public A(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class B extends A {
private Map<B, String> mapOfB;
public B(String name) {
super(name);
mapOfB = new HashMap<>();
}
public void addB(B newB, String someString) {
mapOfB.put(newB, someString);
}
public Map<B, String> getMap() {
return mapOfB;
}
@Override
public boolean equals(Object obj) {
if( obj != null && obj instanceof B) {
if(this.getName() == null && ((B) obj).getName() == null && this == obj) {
return true;
} else if(this.getName().equals(((B) obj).getName())) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return this.getName()==null? System.identityHashCode(this):this.getName().hashCode();
}
}
公共A类{
私有最终字符串名;
公用A(字符串名称){
this.name=名称;
}
公共字符串getName(){
返回名称;
}
}
公共类B扩展了A{
私人地图;
公共B(字符串名称){
超级(姓名);
mapOfB=新的HashMap();
}
public void addB(B newB,String someString){
mapOfB.put(newB,someString);
}
公共地图getMap(){
返回mapOfB;
}
@凌驾
公共布尔等于(对象obj){
if(obj!=null&&obj实例B){
if(this.getName()==null&&((B)obj).getName()==null&&this==obj){
返回true;
}else if(this.getName().equals((B)obj.getName())){
返回true;
}
}
返回false;
}
@凌驾
公共int hashCode(){
返回this.getName()==null?System.identityHashCode(this):this.getName().hashCode();
}
}
如果运行以下代码,则会出现错误:
public static void main(String[] args) {
// write your code here
B b1 = new B("first");
b1.addB(b1, "some first string");
B b2 = new B("second");
b1.addB(b2, "some second string");
// init Ignite configuration
// force java.util.Hashtable to be binary serialized,
// it prevents infinite recursion and other problems
// occurring with the Optimized Serializer
IgniteConfiguration cfg = new IgniteConfiguration();
BinaryConfiguration binConf = new BinaryConfiguration();
Collection<String> binClassNames = new LinkedList<>();
binClassNames.add("java.util.Hashtable");
binConf.setClassNames(binClassNames);
cfg.setBinaryConfiguration(binConf);
Ignition.start(cfg);
// put b1 in cache
IgniteCache cache = Ignition.ignite().getOrCreateCache("MyCache");
cache.put(b1.hashCode(), b1);
//get b1 from cache
B b1FromCache= (B) cache.get(b1.hashCode());
// print map values
System.out.println("b1 map value: " + b1.getMap().get(b1));
System.out.println("b1 from cache map value: " + b1FromCache.getMap().get(b1));
}
publicstaticvoidmain(字符串[]args){
//在这里编写代码
B b1=新的B(“第一”);
b1.addB(b1,“某些首字符串”);
B b2=新的B(“第二”);
b1.addB(b2,“第二个字符串”);
//初始点火配置
//强制对java.util.Hashtable进行二进制序列化,
//它可以防止无限递归和其他问题
//与优化的序列化程序一起发生
IgniteConfiguration cfg=新IgniteConfiguration();
BinaryConfiguration binConf=新的BinaryConfiguration();
集合binClassNames=newLinkedList();
add(“java.util.Hashtable”);
binConf.setClassNames(binClassNames);
setBinaryConfiguration(binConf);
点火起动(cfg);
//将b1放入缓存
IgniteCache cache=Ignition.ignite().getOrCreateCache(“MyCache”);
cache.put(b1.hashCode(),b1);
//从缓存中获取b1
b1FromCache=(B)cache.get(b1.hashCode());
//打印地图值
System.out.println(“b1映射值:+b1.getMap().get(b1));
System.out.println(“b1来自缓存映射值:+b1FromCache.getMap().get(b1));
}
输出为
b1映射值:一些第一个字符串
b1来自缓存映射值:null
问题是子级的字段在父级的字段之前反序列化,因此当Ignite反序列化B时,它首先创建一个空B对象(带有null“name”和“mapOfB”),然后尝试反序列化mapOfB。它创建哈希表,然后反序列化它包含的每个对象以填充它
对于上面示例中的b2,没有问题,因为反序列化b2时还不存在对b2的引用,因此创建一个新的b2对象,填充b2字段(包括“name”字段),然后使用正确的哈希将其添加到Hashmap中
但对于b1,反序列化已启动,因此该对象已存在于Ignit的反序列化对象映射中,但名称为空(b1正在进行反序列化),并且使用此空名称计算哈希代码。哈希表将b1与当时计算的哈希代码放在一起,因此当我们试图在最后的映射中找到具有非空名称的b1时,将找不到它
我无法更改A和B类以及这些对象的创建方式(Hashmap是如何填充的,…),因此必须通过更改序列化来解决。有没有一个简单的方法可以做到这一点
备注:实际代码要比这复杂得多,实际B和Hashmap之间可能有类。关于这种行为的原因,您是正确的
mapOfB
字段在name
字段之前反序列化,并且hashCode()
取决于该名称。而B
的字段在您将其作为键放入映射后会发生更改,因此它是hashCode
更改
我建议您更改数据模型,但由于您不能,这里有另一个选项OptimizedMarshaller
似乎对映射没有问题,因为它使用简单的Java序列化。但是您将无法使用BinaryObject抽象和其他一些特性。
以下是如何启用OptimizedMarshaller:
OptimizedMarshaller封送器=新的OptimizedMarshaller();
塞特马歇尔勒(马歇尔勒);
如果存储的值未实现可序列化接口,则可能需要对其进行适当配置:
marshaller.setRequireSerializable(false);
但是请注意,禁用
requireSerializable
标志可能会以负面的方式影响序列化性能。可以工作的是类似于b1.addB(新的B(“第一”),“一些第一个字符串”)
但这是非常脆弱的,并且只会因为hasCode
和equals
是如何实现的B
不应用作映射的键,因为它不是不可变的。谢谢。我同意这是丑陋的,但我不能改变这个代码。我也同意您的解决方案可以工作,但也不能这样做,因为我甚至无法控制hashMap的填充方式。我将编辑文本以添加此内容,再次感谢。感谢您的回答。您实际上不需要强制使用OptimizedMarshaller;由于Hashmap实现了Serializable,它默认由OptimizedMarshaller使用所有包含的元素进行序列化。但是,这会导致无限递归,因为HashMap包含b1,b1包含HashMap。。。而且OptimizedMarshaller不能阻止无限递归,这就是为什么不能在这里使用它。为了防止这种情况发生,我们必须强制Hashmap(在完整应用程序的许多其他java.util类中)进行二进制序列化