Java Hibernate:避免对集合进行隐式初始化

Java Hibernate:避免对集合进行隐式初始化,java,hibernate,jpa,collections,jackson,Java,Hibernate,Jpa,Collections,Jackson,我们使用实体类有两个目的: 作为数据库模型,即Hibernate@Entity 作为数据模型作为JSON发送到前端 假设一个@实体有许多庞大的集合,例如: @Entity @Table(name = "customer") public class Customer { @Id private Integer id; @Column(name = "name") private String name; @OneToMany(mappedBy = "

我们使用实体类有两个目的:

  • 作为数据库模型,即Hibernate
    @Entity
  • 作为数据模型作为JSON发送到前端
  • 假设一个
    @实体
    有许多庞大的集合,例如:

    @Entity
    @Table(name = "customer")
    public class Customer {
    
        @Id
        private Integer id;
    
        @Column(name = "name")
        private String name;
    
        @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        private List<Order> orders;
    
        @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        private List<Counterparty> counterparties;
    
        /* ... a lot of other properties, including collections ... */
    
    }
    
    @实体
    @表(name=“客户”)
    公共类客户{
    @身份证
    私有整数id;
    @列(name=“name”)
    私有字符串名称;
    @OneToMany(mappedBy=“customer”,fetch=FetchType.LAZY,cascade=CascadeType.ALL)
    私人名单订单;
    @OneToMany(mappedBy=“customer”,fetch=FetchType.LAZY,cascade=CascadeType.ALL)
    私人上市交易对手;
    /*…许多其他属性,包括集合*/
    }
    
    现在,我们以两种主要方式与客户合作:

  • 获取单个客户进行查看/编辑,并填写所有集合
  • 获取客户列表,仅显示其“核心”属性;我们不需要加载它的任何子集合,因为我们不使用它们,它们会生成很多额外的查询
  • 我的首选解决方案是显式初始化案例1的集合,并保持它们
    null
    或案例2的集合为空

    问题是,当Jackson将一个对象序列化为JSON时,它会覆盖所有属性,包括集合属性,因此它们必须被初始化。添加
    @JsonIgnore
    不是一个选项(对于案例1,我们需要这些集合),添加
    @Transient
    使其远离休眠也不是一个选项(编辑后我们必须能够存储集合)

    当然,另一种选择是,创建一个不同的
    客户
    模型,不使用集合,并将其用于场景2,但这意味着维护同一实体的两个品种,我希望避免这种情况


    如何禁用Hibernate对这些集合的隐式加载,使它们只进行显式初始化(例如通过
    Hibernate.initialize(customer.orders)
    ),同时保留在需要时持久化它们的可能性?

    以下只是一种可能的解决方案,取决于您的环境

    FasterXML项目中有一个名为“jackson数据类型hibernate”的项目。您可以在其中找到它,它允许定义自定义序列化程序,包括禁用惰性字段。(我认为这是这个序列化程序的默认设置)

    代码可能如下所示:

    ObjectMapper mapper = new ObjectMapper();
    Hibernate4Module hibernateModule = new Hibernate4Module();
    mapper.registerModule(hibernateModule);
    

    在Hibernate中没有办法做到这一点

    备选方案:

  • 使用DTO。这样做的好处是可以根据使用结果JSON的客户机的确切需要定制要序列化的对象。此外,域模型(Hibernate实体)与序列化逻辑分离,允许两者独立发展
  • 使用

  • 最终,我以“DTO方式”解决了这个问题,但开销最小,为每个属性添加了一个替代getter,如下所示:

    @Entity
    @Table(name = "customer")
    public class Customer {
    
        ...
    
        @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        @JsonIgnore
        private List<Order> orders;
    
        /* "Normal" getter to be used in the back-end */ 
        @JsonIgnore // <-- Important as it hides it from Jackson
        public List<Order> getOrders() {
            return orders;
        }
    
        /* Non-initializing getter for serialization for the front-end */ 
        public List<Order> getOrdersNonInit() {
            return Hibernate.isInitialized(orders) ? orders : null;
        }
    
        ...
    
    }
    
    @实体
    @表(name=“客户”)
    公共类客户{
    ...
    @OneToMany(mappedBy=“customer”,fetch=FetchType.LAZY,cascade=CascadeType.ALL)
    @杰索尼奥雷
    私人名单订单;
    /*后端要使用的“普通”getter*/
    
    @JsonIgnore//可能是解决这个问题的一个好方法,但是它真的很难理解如何使用它,因为它没有任何文档或示例,而且Java文档非常稀少。最后我放弃了它,创建了替代的非初始化getter。除了链接的依赖项之外,这些代码就是您所需要的一切。只需序列化即可具有此映射器的对象。此映射器的默认行为应该是不序列化惰性集合。