使用Gson转换实体时触发Java运行时异常的解决方案咨询
问题分析
- 核心异常原因:
java.sql.Clob是接口类型,Gson默认无法实例化接口——它需要具体类的无参构造来创建对象,而接口没有构造方法,因此反序列化时抛出无法调用无参构造的异常。另外,Hibernate返回的Clob通常是代理对象,直接序列化会丢失有效数据,也无法通过Gson重建。 - 实体类低级错误:两个实体类都存在setter方法名错误,导致属性无法正确赋值:
TemplateData中,setTemplateData方法被错误命名为setTemplateIdTemplateAuditData中,setTemplateAuditId方法被错误命名为setTemplateId,且setTemplateData也被错误命名为setTemplateId
这些错误会导致Gson在反序列化时无法正确注入属性值,即使解决了Clob的问题,对象属性也会赋值失败。
解决方案
步骤1:修正实体类的setter方法
先修复所有命名错误的setter,确保属性名和setter对应:
修复后的TemplateData
@Entity @Table(name="Template") public class TemplateData implements Serializable { @Column(name="template_id") public Integer templateId; @Lob @Column(name="template_Data") public Clob templateData; @Lob @Column(name="template_Total") public Clob templateTotal; public Integer getTemplateId(){ return templateId; } public void setTemplateId(Integer templateId){ this.templateId = templateId; } public Clob getTemplateData(){ return templateData; } // 修正方法名 public void setTemplateData(Clob templateData){ this.templateData = templateData; } public Clob getTemplateTotal(){ return templateTotal; } public void setTemplateTotal(Clob templateTotal){ this.templateTotal = templateTotal; } }
修复后的TemplateAuditData
@Entity @Table(name="Template_Audit") public class TemplateAuditData implements Serializable { @Column(name="template_Audit_id") public Integer templateAudtId; @Column(name="template_id") public Integer templateId; @Lob @Column(name="template_Data") public Clob templateData; @Lob @Column(name="template_Total") public Clob templateTotal; public Integer getTemplateAuditId(){ return templateAudtId; } // 修正方法名 public void setTemplateAuditId(Integer templateAudtId){ this.templateAudtId = templateAudtId; } public Integer getTemplateId(){ return templateId; } public void setTemplateId(Integer templateId){ this.templateId = templateId; } public Clob getTemplateData(){ return templateData; } // 修正方法名 public void setTemplateData(Clob templateData){ this.templateData = templateData; } public Clob getTemplateTotal(){ return templateTotal; } public void setTemplateTotal(Clob templateTotal){ this.templateTotal = templateTotal; } }
步骤2:解决Clob的序列化/反序列化问题
推荐两种方案,根据场景选择:
方案A:将Clob替换为String(更简单)
Clob本质存储的是文本内容,直接在实体类中用String代替Clob,Hibernate依然可以将String映射到CLOB类型,只需修改字段注解:
// 替换TemplateData和TemplateAuditData中的Clob字段 @Lob @Column(name="template_Data") public String templateData; @Lob @Column(name="template_Total") public String templateTotal; // 对应的getter/setter也改成String类型 public String getTemplateData(){ return templateData; } public void setTemplateData(String templateData){ this.templateData = templateData; }
这样Gson可以直接处理String类型,无需额外配置,序列化和反序列化都会正常工作。
方案B:给Gson注册Clob的TypeAdapter(保留Clob类型)
如果必须保留Clob类型,需要给Gson自定义TypeAdapter来处理Clob的序列化和反序列化:
// 自定义Clob类型适配器 public class ClobTypeAdapter extends TypeAdapter<Clob> { @Override public void write(JsonWriter out, Clob value) throws IOException { if (value == null) { out.nullValue(); return; } try { // 将Clob转为字符串写入JSON Reader reader = value.getCharacterStream(); out.value(new BufferedReader(reader).lines().collect(Collectors.joining("\n"))); reader.close(); } catch (SQLException e) { throw new IOException("Failed to read Clob", e); } } @Override public Clob read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } String content = in.nextString(); try { // 创建SerialClob实例(Clob的一个具体实现) return new javax.sql.rowset.serial.SerialClob(content.toCharArray()); } catch (SQLException e) { throw new IOException("Failed to create Clob", e); } } }
然后创建Gson实例时注册这个适配器:
Gson gson = new GsonBuilder() .registerTypeAdapter(Clob.class, new ClobTypeAdapter()) .create(); // 现在可以正常转换 TemplateAuditData tad = gson.fromJson(gson.toJson(td), new TypeToken<TemplateAuditData>(){}.getType());
注意事项
- 若使用Hibernate,方案A的String映射CLOB完全兼容,无需额外配置;
- 方案B中的
SerialClob是JDK提供的Clob实现,确保项目依赖包含javax.sql.rowset相关包; - 避免直接序列化Hibernate代理对象,最好先调用一次getter触发懒加载,将代理对象转为普通对象再进行Gson转换。
内容的提问来源于stack exchange,提问作者kavya
相关产品推荐
相关产品推荐

