考慮這個場景,我們在設計數據庫表時,往往會加一個擴展字段列,可以是varchar類型。程序中可以將各種擴展字段放入一個map結構中,再序列化爲string,存入數據庫。下面以json作爲序列化方式來看一個例子:
比如下面Data類中的ext字段:
public class Data {
private long id;
private String name;
private Map<String, Object> ext;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, Object> getExt() {
return ext;
}
public void setExt(Map<String, Object> ext) {
this.ext = ext;
}
}
下面通過程序往ext裏放入long類型的擴展字段:
Data data = new Data();
data.setId(1);
data.setName("d1");
Map<String, Object> ext = new HashMap<>();
ext.put("duration", 128);
data.setExt(ext);
String json = mapper.writeValueAsString(data);
System.out.println(json);
然後再讀出來:
Data data = mapper.readValue(json, Data.class);
long duration = (long) data.getExt().get("duration");
System.out.println(duration);
這時會拋一個ClassCastException。
這是因爲我們設計的擴展字段是string,object類型的,所以反序列化時,map的key的實際類型由jackson自行推斷,因爲是數字,所以jackson會轉爲int,如果溢出則轉爲long,上面的case沒有溢出,所以實際類型是int。這時我們強轉爲long自然就報錯了。
存入一個long,讀到一個int???這確實是一個坑。。。
怎麼處理比較好?
首先要明確一點,如果反序列化爲Map<String, Object>,那麼key的實際類型是由jackson決定的,我們在強轉時一定要判斷實際類型。
對於上面的例子,我們可以用instanceOf先做判斷,然後轉爲number類處理:
Data data = mapper.readValue(json, Data.class);
Object duration = data.getExt().get("duration");
if (duration instanceof Number) {
long value = ((Number) duration).longValue();
}