@PheonixHkbxoic
2017-11-14T09:51:11.000000Z
字数 3101
阅读 2423
Java
json序列化的对象中存在双向引用会导致的无限递归(infinite recursion)问题。
通常是在Hibernate对一或者多对多关系映射的时候发生的。
如果controller返回json对象,会发现,返回的json对象默认会把所有相关联的对象都加载出来,序列化成json数据,特别要命的是如果hibernate配置的是双向关联,还存在循环引用加载的问题。
@Entity
@Table(name="xl_role")
@JsonIgnoreProperties(value = "users")
public class XRole implements Serializable {
@Id
private Integer id;
private String name;
//关联
//用户 <==> 角色
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "xl_role_user",
joinColumns = {
@JoinColumn(name = "roleId")
},
inverseJoinColumns = {
@JoinColumn(name = "userId")
}
)
// @Transient
private List<XUser> users;
//角色 <==> 权限
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "xl_role_permission",
joinColumns = {
@JoinColumn(name = "roleId")
},
inverseJoinColumns = {
@JoinColumn(name = "permissionId")
}
)
private List<XPermission> permissions;
@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "market_id")
private Market market;
ps:
@JsonManagedReference:标记的属性会被序列化,序列化时属性默认都是会被序列化的。反序列(deserialization,即json数据转换为对象)时,如果没有@JsonManagedReference,则不会自动注入@JsonBackReference标注的属性。
@JsonIgnore:直接忽略某个属性,以断开无限递归,序列化或反序列化均忽略。当然如果标注在get、set方法中,则可以分开控制,序列化对应的是get方法,反序列化对应的是set方法。在父子关系中,当反序列化时,@JsonIgnore不会自动注入被忽略的属性值(父或子),这是它跟@JsonBackReference和@JsonManagedReference最大的区别.
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.9.0.pr4</version>
</dependency>
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = jsonConverter.getObjectMapper();
objectMapper.registerModule(new Hibernate5Module());
return jsonConverter;
}
按上面两步配置之后,默认转化出来的json数据不会加载任何懒加载的数据,而你需要对象下面的那个子对象数据,就需要自己在方法里显式的调用一下,如调用size()方法,这样就实现了我们自己的按需加载。如需要加载project项目下的模块
@RequestMapping(value = "/getAll")
@ResponseBody
public Map getAll(){
Map<String, List<Project>> map = new HashMap<>();
List<Project> projects = projectService.getAll();
projects.forEach(project -> System.out.println(project.getModules().size()));
map.put("data",projects);
return map;
}
这样做,即避免了循环引用的问题,也达到了按需所取的目的.
实例如下:(注意要设置成懒加载)
@GetMapping("/public/api/common/xuser")
public Result<?> count() {
List<XUser> xUsers = xUserService.findAll();
//主动触发懒加载
for (XUser xUser :
xUsers) {
List<XRole> xRoles = xUser.getRoles();
for (XRole xRole :
xRoles) {
xRole.getPermissions().size();
//不能再查users,否则还是会死循环问题
// xRole.getUsers().size();
}
}
return ResultUtil.success(xUsers);
}