简体   繁体   English

Aerospike - 如何在 Java 中反序列化包含对象的地图?

[英]Aerospike - How to deserialize a map containing an object in Java?

I am having problem loading the map from Aerospike DB.我在从 Aerospike DB 加载地图时遇到问题。 When I fetch the record and try to print it, I'm getting the error below当我获取记录并尝试打印它时,出现以下错误

Main主要的

Key key = new Key( "test", "users", 2 );
Map<Integer, Widgets> map = new HashMap<>();
map.put(1, new Widgets(2, 2));
map.put(2, new Widgets(3, 0));
Bin bin = new Bin("widgets", map);
client.put( policy, key, bin );
Record record = client.get(policy, key); // using same key for testing
map = (Map<Integer, Widgets>) record.getMap("widgets"); // here, I do get a map back... but its serialized 
map.forEach( (k,v) -> System.out.println(k)); <------- ERROR

Error错误

Exception in thread "main" java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Integer

Data stored in Aerospike存储在 Aerospike 中的数据

|                    | 2  | MAP('{1:AC ED 00 05 73 72 00 07 57 69 64 67 65 74 73 6F F3 7E F4 7F CD 1C 92 02 00 02 49 00 0A 63 6C 69 63 6B 43 6F 75 6E 74 49 00 09 76 69 65 77 43 6F 75 6E 74 78 70 00 00 00 02 00 00 00 02, 2:AC ED 00 05 73 72 00 07 57 69 64 67 65 74 73 6F F3 7E F4 7F  |
+--------------------+----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.098 secs)

Widgets class小部件类

public class Widgets implements Serializable{
    private int viewCount;
    private int clickCount;
    // getters, setters and constructors here
}

Data is getting stored in the database without a problem, but it is being stored as byte array.数据存储在数据库中没有问题,但它被存储为字节数组。 I'm having trouble deserializing it.我在反序列化它时遇到了麻烦。

EDIT :编辑

When I try to print the map, I do get an output, but when I try to use foreach, it shows an error当我尝试打印地图时,我确实得到了一个输出,但是当我尝试使用 foreach 时,它显示了一个错误

System.out.println(map); // works fine

OUTPUT输出

{1=Widgets@7e774085, 2=Widgets@3f8f9dd6} 

Aerospike always stores integer types (short, int, long, etc) as 64-bin longs in the database. Aerospike 总是将整数类型(short、int、long 等)作为 64-bin long 存储在数据库中。 If you insert a shorter numeric type, it will automatically upcast it to a long.如果插入较短的数字类型,它会自动将其向上转换为 long。 This is to support languages which do not have shorter numeric types.这是为了支持没有较短数字类型的语言。

So when you retrieve your map, your map keys will be returned as longs.因此,当您检索地图时,您的地图键将作为 long 返回。 Hence, this code should replace your retrieval code:因此,此代码应替换您的检索代码:

        Record record = client.get(null, key); // using same key for testing
        Map<Long, Widgets> map2 = (Map<Long, Widgets>) record.getMap("widgets"); // here, I do get a map back... but its serialized 
        map2.forEach( (k,v) -> System.out.println(k));

Note: You do correctly mention that the Widgets() classes will be stored in Aerospike as a Java serialized object.注意:您正确地提到 Widgets() 类将作为 Java 序列化对象存储在 Aerospike 中。 This is probably not desirable.这可能是不可取的。 If, at some point in the future you change technology to something else, your data will not be readable.如果在未来的某个时候您将技术更改为其他技术,您的数据将无法读取。 Aerospike is language agnostic so you can write your data in Java and read it back in C for example. Aerospike 与语言无关,因此您可以用 Java 编写数据,然后用 C 读回。 By storing Java serialized objects you're preventing this ability.通过存储 Java 序列化对象,您可以阻止这种能力。

You can see this in your AQL (note the 0xaced Java magic number):您可以在 AQL 中看到这一点(注意 0xaced Java 幻数):

aql> select * from test.users
*************************** 1. row ***************************
widgets: MAP('{1:AC ED 00 05 73 72 00 34 63 6F 6D 2E 74 69 6D 2E...

Typically it's better to either use Spring Data or to serialize them yourself.通常最好使用Spring Data或自己序列化它们。 For example, you could change your code to be:例如,您可以将代码更改为:

package com.aerospike.play;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Bin;
import com.aerospike.client.Key;
import com.aerospike.client.Record;

public class StackOverflowQuestion {
    public static class Widgets implements Serializable{
        private static final String VIEW_FIELD = "view";
        private static final String CLICK_FIELD = "click";

        private int viewCount;
        private int clickCount;

        public Widgets(int viewCount, int clickCount) {
                this.viewCount = viewCount;
                this.clickCount = clickCount;
        }
        public int getViewCount() {
            return viewCount;
        }
        public int getClickCount() {
            return clickCount;
        }

        @Override
        public String toString() {
                return String.format("{view: %d, count: %s}", viewCount, clickCount);
        }

        public Map<String, Object> asMap() {
                Map<String, Object> values = new HashMap<>();
                values.put(VIEW_FIELD, this.viewCount);
                values.put(CLICK_FIELD, this.clickCount);
                return values;
        }

        public static Widgets fromMap(Map<String, Object> map) {
                return new Widgets((int)(long)map.get(VIEW_FIELD), (int)(long)map.get(CLICK_FIELD));
        }
    }

    public static void main(String[] args) {
        AerospikeClient client = new AerospikeClient("172.28.128.4", 3000);
        Key key = new Key( "test", "users", 2 );
        Map<Integer, Map<String, Object>> map = new HashMap<>();
        map.put(1, new Widgets(2, 2).asMap());
        map.put(2, new Widgets(3, 0).asMap());
        Bin bin = new Bin("widgets", map);
        client.put( null, key, bin );

        Record record = client.get(null, key); // using same key for testing
        Map<Long, Map<String, Object>> map2 = (Map<Long, Map<String, Object>>) record.getMap("widgets"); 
        map2.forEach( (k,v) -> {
            Widgets w = Widgets.fromMap(v);
            System.out.printf("%d -> %s\n", k, w);
        });

        client.close();
    }
}

In this case the output is what you would expect:在这种情况下,输出是您所期望的:

1 -> {view: 2, count: 2}
2 -> {view: 3, count: 0}

But the data stored in the database is using native types:但是存储在数据库中的数据使用的是原生类型:

aql> select * from test.users
*************************** 1. row ***************************
widgets: MAP('{1:{"view":2, "click":2}, 2:{"view":3, "click":0}}')

As a side note, there are more efficient ways of storing this data such as a list, but this representation is more illustrative.作为旁注,有更有效的方法来存储这些数据,例如列表,但这种表示方式更具说明性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM