达梦数据库大文本序列化层级太深报错解决方案"

admin 2026-10-16 09:24:21 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

达梦数据库大文本序列化层级太深报错解决方案

原创

知码乎
知码乎

知码乎

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 对象特性 + 框架序列化机制导致。看似正常的写法,可能换个数据库就有问题了。

仅此记录,避免后续踩坑。


达梦数据库大文本序列化层级太深报错解决方案" 网络安全文章

达梦数据库大文本序列化层级太深报错解决方案"

本文针对达梦数据库大文本序列化层级过深导致的报错问题,分析了原因并提供了五种解决方案,包括将大文本字段转成String、自定义Jackson序列化器、实体直接用String接收、使用@JsonIgnore临时忽略和SQL层面直接转换。文章强调了解决此类问题并非数据库SQL问题,而是Java对象JSON序列化问题。
评论:0   参与:  0