news 2026/6/10 17:36:41

JPA多對多關係時 JSON 序列化解决方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JPA多對多關係時 JSON 序列化解决方案

前言

在 JPA 中處理 多對多 (Many-to-Many) 關係,不使用 @ManyToMany 註解方式,而是將這個關係拆解為兩個一對多的單向關係,並為中間表創建一個獨立的Entity.

代碼如下:

@Entity @Data @NoArgsConstructor @AllArgsConstructor @Builder @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "username", nullable = false) private String username; @Column(name = "password", nullable = false) private String password; @Column(name = "first_name", nullable = false) private String firstName; @Column(name = "last_name") private String lastName; @Column(name = "email", nullable = false, unique = true) private String email; @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private Set<UserRole> roles = new HashSet<>(); public User(String username, String password, String firstName, String lastName, String email) { this.username = username; this.password = password; this.firstName = firstName; this.lastName = lastName; this.email = email; } }
@Entity @Data @NoArgsConstructor @AllArgsConstructor @Builder @Table(name = "roles") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(length = 20) private String name; public Role(String name) { this.name = name; } @OneToMany(mappedBy = "role", cascade = CascadeType.ALL, orphanRemoval = true) private Set<UserRole> userRoles = new HashSet<>(); }
@Data @Embeddable public class UserRoleId implements Serializable { // 與 UserRole.java 中 @MapsId 的名稱一致 @Column(name = "user_id") private Long userId; @Column(name = "role_id") private Long roleId; public UserRoleId() { } public UserRoleId(Long userId, Long roleId) { this.userId = userId; this.roleId = roleId; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserRoleId that = (UserRoleId) o; return Objects.equals(userId, that.userId) && Objects.equals(roleId, that.roleId); } @Override public int hashCode() { return Objects.hash(userId, roleId); } }
@Entity @Data @NoArgsConstructor @AllArgsConstructor @Builder @Table(name = "users_roles") public class UserRole implements Serializable { // ID @EmbeddedId private UserRoleId id; // 關係到 User, userId 對映到 UserRoleId 中的 userId @ManyToOne(fetch = FetchType.LAZY) @MapsId("userId") @JoinColumn(name = "user_id") private User user; // 關係到 Role @ManyToOne(fetch = FetchType.LAZY) @MapsId("roleId") @JoinColumn(name = "role_id") private Role role; @Column(name = "assigned_at") private LocalDateTime assignedAt; }

當我們 序列化 User 實例時,Jackson 會拋出JsonMappingException異常

顯示Exception如下:

原因:

這個錯誤發生在 Jackson 嘗試將您的 JPA 實體 User 序列化為 JSON 字串時,Jackson 序列化器仍然發現了一個循環

無限遞歸序列化錯誤原因:

循環序列化的多對多結構 User <-> UserRole <-> Role

  1. Jackson 序列化 User。
  2. 在序列化 User 的屬性時,遇到 roles 集合 (Set<UserRole>)。
  3. 序列化 UserRole 時,遇到 User 實體 (@ManyToOne private User user;)。
  4. Jackson 再次嘗試序列化這個 User 物件,回到步驟 1,形成無限循環。

註: Jackson 預設的最大遞歸深度是 1000 層,當達到這個限制時,它會拋出這個錯誤以避免堆棧溢出(StackOverflowError)。

任務

針對 User 序列化為 JSON 字串時,Jackson JSON 的無限遞迴問題,提出處理雙向關係的方法

處理動作

步驟一. 首先建立一個測試案例測試:

@Transactional @SpringBootTest public class UserRoleRelationshipTest { @Test void testReadUserRoleRelationship() { try { List<User> users = userRepository.findAll(); // 獲取所有用戶 System.out.println("****** 獲取所有用戶: ******"); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); String jsonArray = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(users); System.out.println(jsonArray); } catch (JsonProcessingException e) { e.printStackTrace(); } }

步驟二. 預備測試資料,已存 DB

測試用

Table users

步驟三. 實作方案

方案一:使用@JsonIgnore

不想序列化某個屬性,使用@JsonIgnore註解來忽略關係中的某個屬性

選項1

public class User { . . . @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) @JsonIgnore private Set<UserRole> roles = new HashSet<>(); . . . }

測試結果

% mvn test

選項2

public class UserRole implements Serializable { . . . @ManyToOne(fetch = FetchType.LAZY) @MapsId("userId") // 映射到 UserRoleId 中的 userId @JoinColumn(name = "user_id") @JsonIgnore private User user; @ManyToOne(fetch = FetchType.LAZY) @MapsId("roleId") // 映射到 UserRoleId 中的 roleId @JoinColumn(name = "role_id") @JsonIgnore private Role role; . . . }

測試結果

% mvn test

方案二:使用@JsonManagedReferences@JsonBackReferences

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) @JsonManagedReference private Set<UserRole> roles = new HashSet<>(); @OneToMany(mappedBy = "role", cascade = CascadeType.ALL, orphanRemoval = true) @JsonManagedReference private Set<UserRole> userRoles = new HashSet<>(); @ManyToOne(fetch = FetchType.LAZY) @MapsId("userId") // 映射到 UserRoleId 中的 userId 屬性 @JoinColumn(name = "user_id") @JsonBackReference private User user; @ManyToOne(fetch = FetchType.LAZY) @MapsId("roleId") // 映射到 UserRoleId 中的 roleId 屬性 @JoinColumn(name = "role_id") @JsonBackReference private Role role;

執行測試結果:

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 15:51:11

2025 洗衣液行业投资逻辑:政策驱动绿色转型,细分赛道暗藏机遇

2025 洗衣液行业投资逻辑&#xff1a;政策驱动绿色转型&#xff0c;细分赛道暗藏机遇 中国洗涤用品工业协会 2025 年数据显示&#xff0c;国内洗衣液市场规模已突破 580 亿元&#xff0c;年复合增长率维持在 6.2% 左右&#xff0c;预计 2030 年将进一步突破 760 亿元。作为刚需…

作者头像 李华
网站建设 2026/6/10 15:42:40

3、开源软件项目剖析与Linux安装准备

开源软件项目剖析与Linux安装准备 开源软件项目的魅力与Linux的诞生 在很多人眼中,Linux就像是一个奇异的突变体,难以想象一个如此复杂且依赖严谨规范的计算机操作系统,竟由全球各地松散的计算机极客志愿者们共同开发。但实际上,开源方式能创造出卓越的软件,往往源于人类…

作者头像 李华
网站建设 2026/6/10 3:06:37

TG买卖60W公民信息,有期徒刑三年无缓刑!

案情回溯罗某某&#xff0c;男&#xff0c;汉族&#xff0c;大学专科文化&#xff0c;户籍所在地江西省吉安市。从2024年3月份开始&#xff0c;罗某某通过翻墙到外网软件telegram上购买、翻墙到外网软件telegram上或在微信群聊里非法下载留存、利用爬虫脚本爬取职称网站上的职称…

作者头像 李华
网站建设 2026/6/10 16:52:47

一个人腰痛的原因是?

以下内容均为真实。 有一位 121212 岁半同学&#xff0c;我们给这位同学取一个化名&#xff0c;就叫小红吧。 小红说&#xff0c;她半年前臀部就特别痛&#xff0c;因为这一点&#xff0c;附近的腰部也很疼&#xff0c;为了写文章&#xff0c;我采访了小红&#xff0c;这是我 3…

作者头像 李华
网站建设 2026/6/9 22:41:46

SG-TCP232-110(单通道串口服务器)特点与功能介绍

一、核心定位与工作模式SG-TCP232-110 是工业级串口转网口协议转换设备&#xff0c;核心功能是实现串口数据与以太网数据的双向透明传输&#xff0c;解决普通串口设备的联网问题。设备集成 RS232 与 RS485 双串口&#xff08;内部连通&#xff0c;仅可单口工作&#xff09;和百…

作者头像 李华
网站建设 2026/6/10 5:18:16

终极合同模板解决方案:快速生成专业商业合同

终极合同模板解决方案&#xff1a;快速生成专业商业合同 【免费下载链接】Contract-Templates 项目地址: https://gitcode.com/gh_mirrors/co/Contract-Templates 还在为每次签订商业合同而头疼吗&#xff1f;Contract-Templates 开源项目为您提供了一套完整的合同模板…

作者头像 李华