[关闭]
@PheonixHkbxoic 2017-11-14T09:51:11.000000Z 字数 3101 阅读 2423

Hibernate,json序列化死循环问题

by cn.pheker

Java


1.死循环,无限递归

json序列化的对象中存在双向引用会导致的无限递归(infinite recursion)问题。
通常是在Hibernate对一或者多对多关系映射的时候发生的。
如果controller返回json对象,会发现,返回的json对象默认会把所有相关联的对象都加载出来,序列化成json数据,特别要命的是如果hibernate配置的是双向关联,还存在循环引用加载的问题。

2.解决办法

2.1 在不想序列化的属性上添加注解 @Transient,(但此属性json中会显示为null)

2.2 在类上添加注解@JsonIgnoreProperties(value = "users") users为你不想序列化的属性

  1. @Entity
  2. @Table(name="xl_role")
  3. @JsonIgnoreProperties(value = "users")
  4. public class XRole implements Serializable {
  5. @Id
  6. private Integer id;
  7. private String name;
  8. //关联
  9. //用户 <==> 角色
  10. @ManyToMany(fetch = FetchType.LAZY)
  11. @JoinTable(name = "xl_role_user",
  12. joinColumns = {
  13. @JoinColumn(name = "roleId")
  14. },
  15. inverseJoinColumns = {
  16. @JoinColumn(name = "userId")
  17. }
  18. )
  19. // @Transient
  20. private List<XUser> users;
  21. //角色 <==> 权限
  22. @ManyToMany(fetch = FetchType.EAGER)
  23. @JoinTable(name = "xl_role_permission",
  24. joinColumns = {
  25. @JoinColumn(name = "roleId")
  26. },
  27. inverseJoinColumns = {
  28. @JoinColumn(name = "permissionId")
  29. }
  30. )
  31. private List<XPermission> permissions;

2.3 使用@JsonBackReference标记在有多对一或者多对多关系的属性上即可解决这个问题,

  1. @JsonBackReference
  2. @ManyToOne(fetch = FetchType.LAZY)
  3. @JoinColumn(name = "market_id")
  4. private Market market;
  5. ps:
  6. @JsonManagedReference:标记的属性会被序列化,序列化时属性默认都是会被序列化的。反序列(deserialization,即json数据转换为对象)时,如果没有@JsonManagedReference,则不会自动注入@JsonBackReference标注的属性。
  7. @JsonIgnore:直接忽略某个属性,以断开无限递归,序列化或反序列化均忽略。当然如果标注在getset方法中,则可以分开控制,序列化对应的是get方法,反序列化对应的是set方法。在父子关系中,当反序列化时,@JsonIgnore不会自动注入被忽略的属性值(父或子),这是它跟@JsonBackReference@JsonManagedReference最大的区别.

2.4 利用Jackson官网开发的插件,并重新实现自己的MappingJackson2HttpMessageConverter

  1. <dependency>
  2. <groupId>com.fasterxml.jackson.datatype</groupId>
  3. <artifactId>jackson-datatype-hibernate5</artifactId>
  4. <version>2.9.0.pr4</version>
  5. </dependency>
  1. @Bean
  2. public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
  3. MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
  4. ObjectMapper objectMapper = jsonConverter.getObjectMapper();
  5. objectMapper.registerModule(new Hibernate5Module());
  6. return jsonConverter;
  7. }
  1. 按上面两步配置之后,默认转化出来的json数据不会加载任何懒加载的数据,而你需要对象下面的那个子对象数据,就需要自己在方法里显式的调用一下,如调用size()方法,这样就实现了我们自己的按需加载。如需要加载project项目下的模块
  2. @RequestMapping(value = "/getAll")
  3. @ResponseBody
  4. public Map getAll(){
  5. Map<String, List<Project>> map = new HashMap<>();
  6. List<Project> projects = projectService.getAll();
  7. projects.forEach(project -> System.out.println(project.getModules().size()));
  8. map.put("data",projects);
  9. return map;
  10. }

这样做,即避免了循环引用的问题,也达到了按需所取的目的.

实例如下:(注意要设置成懒加载)

  1. @GetMapping("/public/api/common/xuser")
  2. public Result<?> count() {
  3. List<XUser> xUsers = xUserService.findAll();
  4. //主动触发懒加载
  5. for (XUser xUser :
  6. xUsers) {
  7. List<XRole> xRoles = xUser.getRoles();
  8. for (XRole xRole :
  9. xRoles) {
  10. xRole.getPermissions().size();
  11. //不能再查users,否则还是会死循环问题
  12. // xRole.getUsers().size();
  13. }
  14. }
  15. return ResultUtil.success(xUsers);
  16. }

2.5 其它的一些办法,如加filter等,请自行百度

三、相关参考链接

@JsonBackReference

@JsonIgnore

Jackson官方插件

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注