gpt4 book ai didi

java - Jackson + Hibernate = 很多问题

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:09:36 27 4
gpt4 key购买 nike

这是我的情况:我想使用 Jackson 和 Hibernate 构建一个简单的 CRUD 网络服务。似乎是 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<Service> services;
}

class Service {
@Id
long id;

@ManyToMany(fetch = FetchType.EAGER, mappedBy = "services")
Set<Doctor> doctors;
}

一个简单的数据模型。我们有一个简单的要求:在 web 服务上,当我们获取服务对象时,我们应该获取关联的医生。当我们得到医生时,我们应该得到相关的服务。我们使用惰性是因为[在此处插入理由]。

现在让我们来服务它:

@Path("/list")
@POST
@Produces(MediaType.APPLICATION_JSON)
@Transactional
public JsonResponse<List<Doctor>> list() {
return JsonResponse.success(doctorCrudRepo.findAll());
}

忽略 JsonResponse 对象(现在只是一个方便的黑盒)并假设 doctorCrudRepo 是 CrudRepository 的有效实例。

Storm 开始了:

failed to lazily initialize a collection of role: Doctor.services, could not initialize proxy - no Session (through reference chain: ...)

好的,那么 Lazy 就不起作用了。很简单。让它变得渴望。

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 端点有效——它正在获取服务。服务端点不起作用 - 它没有获得医生(空集)。它可能基于哪个引用定义了连接表。这又不符合要求。

Contestant #6 : 没有。

其他一些错误但值得一提的解决方案:

  1. 为 json 序列化创建一组新的未被 hibernate 包装的对象,然后将属性复制到 Controller 中。这是很多额外的工作。在任何地方强制使用这种模式都违背了使用 Hibernate 的目的。

  2. 加载 Doctor 后,遍历每个服务并将 service.doctors 设置为 null,以防止进一步延迟加载。我正在努力建立一套最佳实践,而不是提出骇人听闻的变通办法。

那么...什么是正确解决方案?我可以遵循什么样的模式看起来很干净并且让我以使用 Hibernate 和 Jackson 为荣?还是这种技术组合如此不相容以提出新的范例?

最佳答案

首先,关于您的声明“...将属性复制到 Controller 中。这是很多额外的工作。在任何地方强制使用这种模式都违背了使用 Hibernate 的目的。”:

这并没有违背使用 Hibernate 的初衷。 ORM 的创建是为了消除将从 JDBC 接收到的数据库行转换为 POJO 的必要性。 Hibernate 的延迟加载目的是在不需要出色性能或能够缓存实体时消除将自定义查询写入 RDBMS 的冗余工作。

问题不在于 Hibernate&Jackson,而在于您尝试将仪器用于某个目的这一事实,它从来都不是设计的目的。

我猜你的项目会增长(通常他们都会)。如果这是真的,那么你将不得不分开 layers总有一天,越早越好。所以我建议你坚持“错误的解决方案#1”(创建一个 DTO)。你可以使用类似 ModelMapper 的东西以防止手写实体到 DTO 转换逻辑。

还要考虑到如果没有 DTO,您的项目可能会变得难以维护:

  • 数据模型会不断发展,您将始终需要根据变化更新您的前端。
  • 数据模型可能包含一些字段,您可能希望在发送给用户时忽略这些字段(例如用户的密码字段)。您始终可以创建额外的实体,但它们将需要额外的 DAO 等。
  • 有一天,您可能需要向用户返回由某些实体组成的数据。你可以写一个新的 JPQL,比如 SELECT new ComplexObject(entity1, entity2, entity3) ... ,但这比调用少数服务的方法并将结果组合到 DTO 中要困难得多。

关于java - Jackson + Hibernate = 很多问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30689697/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com