当前位置: 首页 > 知识库问答 >
问题:

Jackson Hibernate=很多问题

桂德义
2023-03-14

所以我的情况是:我想使用Jackson和Hibernate构建一个简单的CRUD Web服务。似乎是一个完美的Spring启动工作。因此,我们有以下几点:

(请注意,我正在压缩代码,使其无法编译)

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服务上,当我们得到服务对象时,我们应该得到相关的医生。当我们得到医生时,我们应该得到相关的服务。我们使用lazy是因为[insert justification here]。

所以现在让我们为它服务:

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

修饰一下JsonContent对象(现在只是一个方便的黑盒),让我们假设doctorCrudRepo是CrudRepository的有效实例。

火Storm开始了:

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

让我们看看其他人说了什么:

参赛者#1:解决方案不相关,因为它们适用于一对多,而不是多对多,所以我仍然得到StackOverflow错误。

选手#2:和以前一样,还是一对多,还是堆积如山。

参赛者#3:相同(从来没有人使用多对多???)

参赛者#4:我不能使用@JsonIgnore,因为这意味着它永远不会被序列化。所以它不符合要求。

选手5:乍一看,它似乎很好用!然而,只有医生endpoint起作用——它正在获得服务。服务endpoint不工作-无法获取医生(空集)。它可能基于定义联接表的引用。这又不符合要求了。

6号参赛者:没有。

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

>

加载医生后,循环每个服务,并将service.doctors设置为空,以防止进一步的延迟加载。我正在努力建立一套最佳实践,而不是想出愚蠢的变通办法。

所以正确的解决方案是什么?使用Hibernate和Jackson,我可以遵循什么样的模式,看起来很干净,让我感到自豪?或者这种技术的结合如此不相容,以至于提出了一种新的范式?

共有2个答案

乜飞航
2023-03-14

我找到了一个看似优雅的解决方案

>

  • 使用OpenEntityManagerInViewFilter。似乎是不赞成的(可能是出于安全原因,但我没有看到任何令人信服的理由不使用它)。使用起来很简单,只需定义一个bean:

    @Component
    public class ViewSessionFilter extends OpenEntityManagerInViewFilter {
    }
    

    对所有引用使用LAZY。这就是我想要开始的,这一点尤其重要,因为我的数据有很多引用,我的服务很小。

    使用@JsonView。请参阅这篇有用的文章。

    首先,弄清楚观点会是什么(一个给医生,一个给病人)

    public interface Views {
        public static interface Public {}
        public static interface Doctors extends Public {}
        public static interface Services extends Public {}
    }
    

    从医生视图看,您将看到服务。

    @Entity
    @Table(name = "doctor")
    public class Doctor {
    
        @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        @JoinTable(name = "doctor_service", joinColumns = { @JoinColumn(name = "doctor_id", nullable = false) },
                inverseJoinColumns = { @JoinColumn(name = "service_id", nullable = false) })
        @JsonView(Views.Doctors.class)
        private Set<Service> services;
    }
    

    从服务的角度来看,你会看到医生。

    @Entity
    @Table(name = "service")
    public class Service {
    
        @ManyToMany(fetch = FetchType.LAZY, mappedBy = "services")
        @JsonView(Views.Services.class)
        private Set<Doctor> doctors;
    
    }
    

    然后将视图分配给服务终结点。

    @Component
    @Path("/doctor")
    public class DoctorController {
    
        @Autowired
        DoctorCrudRepo doctorCrudRepo;
    
        @Path("/list")
        @POST
        @Produces(MediaType.APPLICATION_JSON)
        @JsonView(Views.Doctors.class)
        public JsonResponse<List<Doctor>> list() {
            return JsonResponse.success(OpsidUtils.iterableToList(doctorCrudRepo.findAll()));
        }
    
    }
    

    完全适用于一个简单的CRUD应用程序。我甚至认为它会很好地扩展到更大、更复杂的应用程序。但它需要小心维护。

  • 田远
    2023-03-14

    首先,关于您的语句“…将属性复制到控制器中。这是一项大量的额外工作。在任何地方强制使用此模式都会破坏使用Hibernate的目的。”

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

    问题不在于Hibernate

    我猜你的项目会有增长的趋势(通常都是这样)。如果这是真的,那么有一天你将不得不分离层,而且越早越好。因此,我建议您坚持使用“错误的解决方案1”(创建DTO)。您可以使用ModelMapper之类的工具来防止将实体手工写入DTO转换逻辑。

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

    • 数据模型将不断发展,您将始终需要用更改来更新前端
     类似资料:
    • 尽管已经建立了许多L4关系,并且它们大部分时间都在工作。我在挣扎,不知为什么,我看不出哪里出了问题。这几乎可以肯定是显而易见的。 关系是一个任务有一个主题,主题有许多任务。 任务模型 主题模型 任务控制器 当我添加($task)时,关系数组为空。 对象(任务)#688(21){[“受保护的”:受保护的]=

    • 问题内容: 对于一个项目,我们有一堆始终具有相同结构且未链接在一起的数据。有两种保存数据的方法: 为每个池创建一个新的数据库(约15-25个表) 在一个数据库中创建所有表,并根据表名称更改池。 对于MySQL来说,哪一个更容易和更快地处理? 编辑: 我对数据库设计没有兴趣,只是对两种可能性中的哪一种更快感到兴趣。 编辑2: 我将尝试使其更加清晰。如前所述,我们将获得数据,其中一些日期很少会属于不同

    • 使用指南 - 疑难问题 - 数据准确性问题 - 选择一整年,发现消费很多,访问次数很少 统计中的消费指标是从推广团队获取的,而访问数据需要安装代码之后才能记录。 如果客户不是整年都安装了统计,而是只安装了一个月,那就只有这一个月详细的数据,但是却有客户一整年的消费。

    • 问题:当我引用实体集合字段作为HQL语句的一部分时,HQL查询没有返回任何结果。它适用于一个HQL投影,例如: categoryTypes是IncidentEntity类字段之一(它是定义为多个联接的集合,如下所示)。这很好,但当我尝试引用另一个映射为多个联接的投影集合时,问题就出现了。 一旦我这样做,我就得到一个空的集合。这意味着hibernate生成的SQL查询不会返回任何内容。我已经通过在S

    • 问题内容: 如何获得比Long.MAX_VALUE大的整数? 我希望此方法返回: 问题答案: 该方法无法返回。这是该 点 的。如果它的名字是… false,那 真是 令人困惑。然后应该只调用它,并且合理地使用零个字。只需使用Android的isUserAGoat,您就可以滚动自己的始终返回的函数。 请注意,内存中的字节数固定。 从Oracle: long:long数据类型是64位带符号的二进制补码

    • 问题内容: 我正在尝试将2.5GB的txt文件读入我的应用程序。我正在运行Win7 x64,并且有43GB的可用内存(64GB中的可用空间)。我尝试使用-Xmx -XX:MaxParmSize -XX:ParmSize等。这些都不影响错误。我还能尝试什么?因为我当然有足够的可用堆空间,所以这个错误似乎很奇怪。 我在跑步 非常感谢。 =============答案============== 好,我