DM原生JDBC,查询结果用Jackson序列化,字段为TEXT类型且存的json字符串时,报错“Infinite recursion“

原生JDBC,查询结果用Jackson序列化,字段为TEXT类型且存的json字符串时,报错"Infinite recursion"

【DM版本】:
DM Database Server 64 V8
DB Version: 0x7000c
DM JDBC DRIVER:
<dependency> <groupId>com.dameng</groupId> <artifactId>DmJdbcDriver18</artifactId> <version>8.1.2.192</version> </dependency>

【操作系统】:CentOS 7
【CPU】: GenuineIntel
【问题描述】*:
现在需要把查询结果序列化,其他数据库如mysql的驱动都没问题,用达梦驱动时,无法把TEXT类型的值正确序列化,ResultSet调用getObject时,返回的DmdbNClob对象。

image.png

报错日志:

Infinite recursion (StackOverflowError) (through reference chain: dm.jdbc.desc.EPGroup["props"]->dm.jdbc.desc.DmProperties["properties"]->java.util.Properties["epgroup"]->dm.jdbc.desc.EPGroup["props"]->dm.jdbc.desc.DmProperties["properties"]->java.util.Properties["epgroup"]->dm.jdbc.desc.EPGroup["props"]->dm.jdbc.desc.DmProperties["properties"]

你把TEXT定义成varchar(2000),一般connection__params不会太长

image.png

或者在实体类把数据库类型为text的字段定义为String也可以解决

<think>我们正在解决Spring Boot中JSON序列化出现的无限递归导致的StackOverflowError问题。 根据引用内容,这个问题通常发生在具有双向关联的实体类之间(例如父子关系、一对多双向关联), 当JSON序列化工具(如Jackson)尝试序列化一个对象,它会递归地访问所有关联对象,而关联对象又反向引用原始对象, 导致无限循环,最终抛出StackOverflowError。 解决方案: 1. 使用@JsonIgnore注解:在不需要序列化的属性上添加该注解,简单粗暴地忽略该属性,但可能丢失部分数据。 2. 使用@JsonBackReference和@JsonManagedReference注解(配对使用):这是专门为解决双向关联中的无限递归问题而设计的。 根据引用[2][^2]和引用[3][^3]: - @JsonBackReference: 标注在“子”类中引用“父”类的属性上(即反向引用端),在序列化该属性会被忽略。 - @JsonManagedReference: 标注在“父”类中引用“子”类的属性上(即正向引用端),该属性会被正常序列化。 注意:这两个注解通常成对使用,以确保序列化和反序列化的正确性。 示例: 假设有两个实体类:Order(订单)和OrderItem(订单项),它们之间是一对多的双向关联。 在Order类中: ```java @Entity public class Order { @Id private Long id; // 其他字段... @OneToMany(mappedBy = "order") @JsonManagedReference // 表示这是正向引用,会被序列化 private List<OrderItem> items; // getters and setters } ``` 在OrderItem类中: ```java @Entity public class OrderItem { @Id private Long id; // 其他字段... @ManyToOne @JoinColumn(name = "order_id") @JsonBackReference // 表示这是反向引用,序列化忽略 private Order order; // getters and setters } ``` 这样,在序列化Order,会包含其OrderItem列表(因为@JsonManagedReference),而每个OrderItem在序列化不会再去序列化其Order属性(因为@JsonBackReference),从而避免了无限递归。 3. 使用@JsonIdentityInfo注解:这个注解可以在序列化为对象生成一个唯一标识(如id),当同一个对象再次出现,只输出这个标识,而不是再次展开。这也可以解决无限递归问题,但序列化后的数据结构会有所不同(包含一个id引用)。 示例: ```java @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Order { // ... } ``` 4. 使用DTO(数据传输对象)模式:不直接序列化实体对象,而是转换为DTO对象,在DTO中只包含需要序列化字段,避免循环引用。 5. 在查询使用懒加载,并避免在事务外访问懒加载属性(因为此会触发额外的查询,可能导致LazyInitializationException)。但这种方法并不能直接解决序列化循环,通常需要结合上述注解或DTO使用。 根据引用[4][^4]中的分析,问题出现在HmAppProductMgEntity和HmMoneyInRecEntity之间的双向关联。因此,我们可以使用上述方法之一来解决。 推荐方法:使用@JsonBackReference和@JsonManagedReference。 步骤: a) 确定两个实体之间的关联属性。 b) 在“父”方(例如HmAppProductMgEntity)的关联属性(如hmMoneyInRecEntityList)上添加@JsonManagedReference。 c) 在“子”方(例如HmMoneyInRecEntity)的关联属性(如hmAppProductEntity)上添加@JsonBackReference。 注意:父子关系是根据业务逻辑来确定的,通常“一”的一方为父,“多”的一方为子。 如果不想修改实体类(或者无法修改),还可以通过配置ObjectMapper来忽略循环引用,但这样可能会导致数据不完整(因为遇到循环会停止序列化该属性): ```java // 在Spring Boot中配置ObjectMapper @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); // 以下配置可以解决循环引用,但会丢失部分数据(遇到循环引用用引用id代替) mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 使用以下配置,当检测到循环引用,会通过引用(通常是对象的id)来表示,而不是展开对象 mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); // 使用Hibernate5Module来支持Hibernate的懒加载和代理对象 mapper.registerModule(new Hibernate5Module()); return mapper; } } ``` 但是,使用@JsonBackReference和@JsonManagedReference是更精确的控制方式。 总结: - 优先考虑使用@JsonBackReference和@JsonManagedReference注解来打破双向关联的循环。 - 如果实体类不能修改,则考虑配置ObjectMapper或使用DTO。 请根据实际情况选择解决方案。 §§相关问题§§ 1. 在Spring Boot中如何配置Jackson的ObjectMapper? 2. @JsonIgnore和@JsonBackReference有什么区别? 3. 如何解决Hibernate懒加载与JSON序列化的冲突?</think>### 解决 Spring Boot JSON 序列化无限递归导致的 StackOverflowError 在 Spring Boot 中,当实体类在**双向关联关系**(如父子关系、一对多/多对一关系)JSON 序列化可能引发无限递归错误: ```log Could not write JSON: Infinite recursion (StackOverflowError) ``` 这是因为序列化会循环访问关联对象,最终导致栈溢出。以下是完整解决方案: --- #### 1. 问题根源分析 当两个实体互相引用(如 `Order` ↔ `OrderItem`),Jackson 序列化过程: 1. 序列化 `Order` ➔ 访问其 `orderItems` 属性 2. 序列化 `OrderItem` ➔ 访问其 `order` 属性 3. 回到步骤 1,形成无限循环[^4] --- #### 2. 解决方案(3 种方法) ##### 方法 1:使用 `@JsonIgnore`(简单场景) ```java @Entity public class OrderItem { @ManyToOne @JoinColumn(name = "order_id") @JsonIgnore // 忽略反向引用[^2] private Order order; } ``` - **优点**:简单直接 - **缺点**:完全丢失关联数据,反序列化无法恢复关系 ##### 方法 2:使用 `@JsonManagedReference` & `@JsonBackReference`(推荐) ```java // 父实体(一方) @Entity public class Order { @OneToMany(mappedBy = "order") @JsonManagedReference // 标记正向引用(会被序列化)[^2][^3] private List<OrderItem> orderItems; } // 子实体(多方) @Entity public class OrderItem { @ManyToOne @JoinColumn(name = "order_id") @JsonBackReference // 标记反向引用(序列化忽略)[^2][^3] private Order order; } ``` - **序列化效果**: ```json { "id": 1, "orderItems": [ // @JsonManagedReference 属性正常显示 { "id": 101, /* 不含order属性 */ }, { "id": 102 } ] } ``` - **反序列化**:能自动重建双向关系[^3] ##### 方法 3:使用 `@JsonIdentityInfo`(复杂关联) ```java @JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class, property = "id" // 用id作为唯一标识 ) @Entity public class Order { @OneToMany(mappedBy = "order") private List<OrderItem> orderItems; } @Entity public class OrderItem { @ManyToOne @JoinColumn(name = "order_id") private Order order; } ``` - **序列化效果**: ```json // 首次出现完整对象 { "id": 1, "orderItems": [ { "id": 101, "order": 1 }, // 再次出现只显示id { "id": 102, "order": 1 } ] } ``` --- #### 3. 其他注意事项 1. **懒加载问题**: 如果使用 Hibernate 懒加载,确保在事务范围内完成序列化,或添加 `jackson-datatype-hibernate5` 模块: ```java @Bean public Module hibernateModule() { return new Hibernate5Module(); } ``` 2. **DTO 模式**: 使用 DTO 代替实体直接返回,切断循环引用: ```java public class OrderDTO { private Long id; private List<OrderItemDTO> items; // 不包含反向引用 } ``` 3. **全局配置**(不推荐): 在 `application.properties` 中忽略循环引用: ```properties spring.jackson.serialization.fail-on-empty-beans=false ``` ⚠️ 可能导致数据不完整 --- #### 总结 | **方案** | **适用场景** | **数据完整性** | |---------------------------|----------------------------------|----------------| | `@JsonIgnore` | 不需要反向引用的简单场景 | ❌ | | `@JsonManaged/BackReference` | 标准双向关联(推荐) | ✔️ | | `@JsonIdentityInfo` | 多层级复杂关联 | ✔️ | | **DTO 模式** | 需要高度定制返回结构的场景 | ✔️ | 优先使用 `@JsonManagedReference` 和 `@JsonBackReference` 组合,这是 Spring Data JPA 中处理双向关联的标准方案[^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值