【问题标题】:Apache Ignite: Object cannot be found in Hashmap after deserializationApache Ignite:反序列化后在 Hashmap 中找不到对象
【发布时间】:2017-11-24 13:18:50
【问题描述】:

我在 Apache Ignite 序列化/反序列化方面遇到了与字段反序列化顺序相关的问题。我需要在 Ignite 缓存中放置一个“B”实例,如下所示:

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();
    }
}

如果我运行以下代码:

    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));
    }

输出是

b1 映射值:一些第一个字符串

b1 来自缓存映射值:null

问题是子字段在父字段之前被反序列化,所以当 Ignite 反序列化 B 时,它首先创建一个空的 B 对象(带有 null “name”和“mapOfB”),然后它尝试反序列化 mapOfB .它创建 Hashtable,然后反序列化它包含的每个对象以填充它。

对于上例中的b2没有问题,因为在反序列化时还没有对b2的引用,所以创建了一个新的b2对象,填充了b2字段(包括“name”字段),然后添加到具有正确哈希的 Hashmap。

对于 b1 但是反序列化开始了,因此该对象已经存在于 Ignit 的反序列化对象映射中,但具有空名称(b1 的反序列化正在进行中),并且具有使用此空名称计算的 hashCode。 Hashtable 将 b1 与当时计算的 hashCode 放在一起,因此当我们尝试在最后的 map 中找到具有非 null name 的 b1 时,它无法找到。

我无法更改 A 和 B 类以及创建这些对象的方式(如何填充 Hashmap,...),因此必须通过更改序列化来解决。有没有一种简单的方法可以做到这一点?

备注:实际代码比这复杂得多,实际 B 和 Hashmap 之间有可能的类。

【问题讨论】:

  • b1.addB(new B("first"), "some first string") 这样可以工作的东西,但这是超级不稳定的,并且仅因为hasCodeequals 的实现方式而起作用... B 不应用作一张地图,因为它不是不可变的。
  • 谢谢。我同意它很难看,但我无法更改此代码。我也同意您的解决方案可以工作,但也不能这样做,因为我什至无法控制 hashMap 的填充方式。我将编辑文本以添加此内容,再次感谢。

标签: java serialization hashmap ignite binary-serialization


【解决方案1】:

您对这种行为的原因是正确的。 mapOfB 字段在 name 字段之前反序列化,hashCode() 取决于该名称。而B的字段在你把它作为key放入map后会发生变化,所以是hashCode变化。

我建议您更改您的数据模型,但既然您不能,这里有另一个选择...OptimizedMarshaller 似乎对 Maps 没有问题,因为它使用简单的 Java 序列化。但是您将无法使用 BinaryObject 抽象和其他一些功能。 以下是启用 OptimizedMarshaller 的方法:

OptimizedMarshaller marshaller = new OptimizedMarshaller();
cfg.setMarshaller(marshaller);

如果你存储的值没有实现Serializable接口,那么你可能需要适当地配置它:

marshaller.setRequireSerializable(false);

但请注意,禁用 requireSerializable 标志可能会对序列化性能产生负面影响。

【讨论】:

  • 感谢您的回答。您实际上不需要强制执行 OptimizedMarshaller;由于 Hashmap 实现了 Serializable,它默认由 OptimizedMarshaller 序列化所有包含的元素。然而,这会导致无限递归,因为 HashMap 包含 b1 包含 Hashmap ......并且 OptimizedMarshaller 不会阻止无限递归,这就是它不能在这里使用的原因。为了防止这种情况,我们必须强制对 Hashmap 进行二进制序列化(在整个应用程序的许多其他 java.util 类中)。
  • 如果B类没有实现Serializable,你只需要设置requireSerializable为false,否则可以正常工作。我没有遇到无限递归的任何问题,为您的示例配置了 OptimizedMarshaller。如果我完全删除 BinaryConfiguration,它也可以正常工作。您使用哪个版本的 Ignite?你能提供一个导致问题和异常的复制器吗?作为一个单独的 SO 问题可能会更好。
  • 您是对的,您的解决方案有效。非常感谢你!我使用 Ignite 2.2.0。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 2016-08-20
  • 2014-02-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多