所以这是我的情况:我想使用Jackson和Hibernate构建一个简单的CRUD Web服务.对于Spring Boot来说似乎是一项完美的工作.因此,我们有以下内容:
(请注意,我正在压缩代码,因此它不可编译)
class Doctor {
@Id
long id;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "doctor_service", joinColumns = { @JoinColumn(name = "doctor_id", nullable = false) }, inverseJoinColumns = { @JoinColumn(name = "service_id", nullable = false) })
Set services;
}
class Service {
@Id
long id;
@ManyToMany(fetch = FetchType.EAGER, mappedBy = "services")
Set doctors;
}
一个简单的数据模型.我们有一个简单的要求:在Web服务上,当我们获得Service对象时,我们应该获得关联的Doctors.当我们获得医生时,我们应该获得相关的服务.我们之所以懒惰是因为[在此处插入理由].
现在,让我们为它服务:
@Path("/list")
@POST
@Produces(MediaType.APPLICATION_JSON)
@Transactional
public JsonResponse> list() {
return JsonResponse.success(doctorCrudRepo.findAll());
}
对JsonResponse对象(目前仅是便利黑匣子)的外观,并假设doctorCrudRepo是CrudRepository的有效实例.
风暴开始了:
failed to lazily initialize a collection of role: Doctor.services, could not initialize proxy - no Session (through reference chain: ...)
好的,这样懒惰就不起作用了.很简单.只是让它渴望.
Caused by: java.lang.StackOverflowError: null
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:455)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:367)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:655)
... 1011 common frames omitted
因此,让我们看看其他人说了什么:
Contestant #1:解决方案不相关,因为它们适用于一对多而不是多对多,因此我仍然会遇到StackOverflowError.
Contestant #2:与以前相同,仍然是一对多,仍然是StackOverflow.
Contestant #3:相同(没有人曾经使用过多对多???)
Contestant #4:我不能使用@JsonIgnore,因为这意味着它将永远不会序列化.因此,它不符合要求.
Contestant #5:乍一看,它看起来还不错!但是,只有Doctor端点有效-它正在获得服务.服务端点不起作用-它没有得到Doctors(空集).它可能基于哪个引用定义了联接表.这又不符合要求.
其他一些错误但值得一提的解决方案:
>为json序列化创建一个新的对象集,这些对象不被冬眠包装,然后将属性复制到控制器中.这是很多额外的工作.在任何地方强制使用此模式都无法使用Hibernate的目的.
>加载Doctor之后,遍历每个Service并将service.doctors设置为null,以防止进一步的延迟加载.我正在尝试建立一套最佳实践,而不是提出一些棘手的解决方法.
那么…什么是正确的解决方案?我可以遵循哪种模式看起来很干净,让我为使用Hibernate和Jackson感到自豪?还是这种技术的组合如此不兼容以至于无法提出新的范例?