达梦数据库大文本序列化层级太深报错解决方案
原创
知码乎
知码乎
知码乎
2026年9月24日 17:37
山东
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
今天程序在达梦数据库部署后,接口返回JSON给前端时,报错Document nesting depth (1001) exceeds the maximum allowed (1000, fromStreamWriteConstraints.getMaxNestingDepth())其实就是序列化层级太深,导致序列化失败,但是查了一下,虽然我是个大文本的字段,但是实际存储的JSON数据很简单,后来查询资料才知道,达梦数据库大文本字段返回的数据并不是字符而是一个代理对象。
壹、报错原因
达梦数据库的 CLOB/TEXT 字段,JDBC 查询后返回的不是普通字符串,而是 DmdbNClob 代理对象。
这个对象内部持有大量嵌套属性:
- 数据库连接引用
- IO 流、Reader 流对象
- 多层代理包装链
当 Jackson 序列化返回前端时,会无限递归反射遍历内部属性,最终导致:
嵌套层级过深 / 栈溢出
✅ 核心结论:这不是数据库 SQL 问题,是 Java 对象 JSON 序列化的问题!
贰、几种解决方案
以下是几种推荐的解决方案,大家可以结合框架结合以及是否需要支持不同数据库按需选择,没有绝对的好坏。
方案一:将大文本字段转成String
此方案适合使用entityManager写sql并且需要兼容多种数据库的场景,封装一个公共方法,便利查询出来的列表,将Clob转成String
/**
* 将Clob转成String
*
* @param list 列表
* @return 结果
*/
public static List<Map<String, Object>> clob2String(List<Map<String, Object>> list) {
try {
for (Map<String, Object> map : list) {
for (String key : map.keySet()) {
if (map.get(key) instanceof Clob) {
if (map.get(key) == null) {
map.put(key, \"\");
} else {
Clob clob = (Clob) map.get(key);
map.put(key, clob.getSubString(1, (int) clob.length()));
}
}
}
}
return list;
} catch (Exception e) {
throw new BPICAException(e);
}
}
方案二:自定义 Jackson 序列化器
如果项目实体已经固定为 Clob/DmdbNClob 类型,无法修改字段,并且项目只需要支持达梦数据库,可以自定义序列化器,直接读取文本内容,不递归代理对象。
1. 全局通用 CLOB 序列化器
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
import java.io.BufferedReader;
import java.io.IOException;
import java.sql.Clob;
import java.sql.SQLException;
public class DmClobSerializer extends StdScalarSerializer<Clob> {
public DmClobSerializer() {
super(Clob.class);
}
@Override
public void serialize(Clob clob, JsonGenerator gen, SerializerProvider provider) throws IOException {
if (clob == null) {
gen.writeNull();
return;
}
// 读取 CLOB 原始文本,跳过代理对象嵌套属性
try (BufferedReader reader = new BufferedReader(clob.getCharacterStream())) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
gen.writeString(sb.toString());
} catch (SQLException e) {
throw new IOException(\"读取达梦CLOB大文本失败\", e);
}
}
}
2. 实体字段启用
@JsonSerialize(using = DmClobSerializer.class)
private Clob content;
方案三:实体直接用 String 接收
适合框架使用MyBatis的场景, MyBatis自带 ClobTypeHandler,可以自动将达梦 CLOB/TEXT 转为普通字符串,从根源杜绝代理对象序列化问题。
1. 实体类写法
@Data
public class DemoEntity {
private Long id;
// 数据库为 CLOB/TEXT 字段,Java 直接定义 String
private String content;
}
2. Mapper.xml 配置 TypeHandler
<result property=\"content\" column=\"content\"
jdbcType=\"CLOB\"
typeHandler=\"org.apache.ibatis.type.ClobTypeHandler\"/>
适配要求:达梦 JDBC 驱动升级至 \\8.1.4.48+\\,彻底解决老版本转换失败 bug。
方案四:@JsonIgnore 临时忽略
如果当前接口不需要返回大文本字段,可以直接忽略,快速解决报错,适合紧急上线场景。但是话说回来,序号不需要返回,可能大多数情况下我们也不需要查这个字段了。
@JsonIgnore
private Clob content;
方案五:SQL 层面直接转换
通过达梦内置函数 DBMS_LOB.SUBSTR,查询时直接将大文本转为字符串。缺点就是如果项目使用多种数据库,需要写分支,并且超常会被截断,所以只适合文本不是很大的场景。
注意限制:最大仅支持 32767 字符,超长会截断,只适合短大文本场景。
SELECT
id,
DBMS_LOB.SUBSTR(content,32767,1) AS content
FROM demo_table;
叁、写在最后
达梦、人大金仓等国产数据库,在大字段、兼容适配上有不少细节坑,大部分报错看似是数据库问题,实则是JDBC 对象特性 + 框架序列化机制导致。看似正常的写法,可能换个数据库就有问题了。
仅此记录,避免后续踩坑。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论