【问题标题】:JPA - Eager - Lazy best practiceJPA - 渴望 - 懒惰的最佳实践
【发布时间】:2014-02-11 23:32:50
【问题描述】:

JBoss EAP 6
休眠 4

我有一个带有 Web 浏览器客户端的 J2EE 应用程序。 (阿帕奇点击) 内部业务逻辑和客户端都使用相同的实体对象。

我想将实体中的所有关系设置为延迟加载。这样我就有了很好的表现。

但是当使用客户端中的实体(即 apache click 的服务器端代码)时,我需要大量的关系来预先加载。客户端代码通过会话 bean 访问后端。

所以我有两种方法可以解决这个问题:

  1. 为每个 JPA 实体创建 2 个,一个用于预加载,一个用于延迟加载。然后在客户端使用预加载的,在服务器中使用延迟加载的。大多数服务器逻辑将在事务中,因此延迟加载在这里很好。

  2. 使所有关系延迟加载。从客户端访问实体时,请确保存在事务。 (@TransactionAttribute(TransactionAttributeType.REQUIRED)) 并对必要字段的访问进行编码,以便在会话 bean 调用后可以访问它们。 但这意味着我必须在不需要时启动事务,即如果我只得到一些对象。而且我必须维护更多代码。而且我必须确切地知道客户需要什么关系。

  3. 创建一个继承层次结构,其中我有一个超级实体,然后是 2 个子实体,一个具有延迟加载的对象关系,一个只有值,没有对象。即:

超级

    @MappedSuperclass
    public class SuperOrder {

    @Id
    @Column(name = "id")
    @GeneratedValue(.....)
    private Long id;

    @Column(name = "invoice", length = 100)
    private String invoice;

孩子 1

    @Entity
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @Table(name = "testorder")
    @SequenceGenerator(....)
    public class Order extends SuperOrder {

    @ManyToOne(targetEntity = PrintCustomerEnt.class, fetch = FetchType.EAGER, optional = true)
    @JoinColumn(name = "print_customer_id", nullable = true)
    @ForeignKey(name = "fk_print_customer")
    @Valid
    private PrintCustomerEnt printCustomer;

    public PrintCustomerEnt getPrintCustomer() {
        return printCustomer;
    }

    public void setPrintCustomer(final PrintCustomerEnt printCustomer) {
        this.printCustomer = printCustomer;
    }

    }

孩子 2

    @Entity
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @Table(name = "testorder")
    @SequenceGenerator(...)
    public class LazyOrder extends SuperOrder {

    @Transient
    private String printCustomerName;

    @Column(name = "print_customer_id", nullable = true)
    private Long printCustomerId;

最佳做法是什么……或者有其他好的方法可以做到这一点。

基本上问题是我想在不同的场景中使用相同的实体。有时我需要预先加载,有时我需要延迟加载。

【问题讨论】:

  • 我投给#2。 #1 和 #3 将是未来的大难题
  • 我做了类似于你的 #1 和 #3 的事情,它对我来说没问题。不同之处在于,我只是没有较小实体的属性,而不是瞬态或懒惰。所有实体最终都会被转换为模型,因此模型在来自较小实体时将只有一个空值。是的,它变得有点让人头疼,因为它是一个具有许多用例的复杂实体,但它仍然是合理的。我希望 JPA 有一个更好的解决方案,比如 C# 的实体框架......为此,您只需添加 .Include(o => o.printCustomerName) 它将包含它

标签: java hibernate jakarta-ee jpa


【解决方案1】:

我建议您只创建一个具有 惰性 关系的 JPA 实体,当您需要热切地加载时,其中一些创建一个使用 JPQL(HQL) 的服务做一些 FETCH 技巧。这个想法是一个 JPA 实体和许多服务。

【讨论】:

    【解决方案2】:

    我在 JPA 2 中编程已经有一段时间了,我可以说现在有几条我几乎总是适用的书面规则:

    1. 对所有 OneToMany、ManyToMany 关系使用 LAZY 初始化
    2. 对所有 OneToOne、ManyToOne 关系使用 EAGER Inicalization

    这条规则适用于我 99% 的项目。根据我的个人经验和我一直在做的一些研究,我认为这些是最佳做法。

    注意:我必须说我没有在延迟初始化上使用 JOIN FETCH,而是写了一个 Prefetch 方法。示例:

    @Entity
    class Entity{
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Basic(optional = false)
      private Integer id;
    
      @OneToMany(cascade = CascadeType.ALL, mappedBy = "mappedName",
            orphanRemoval = true)
      private List<Child1> collection1;
    
      @OneToMany(cascade = CascadeType.ALL, mappedBy = "mappedName",
            orphanRemoval = true)
      private List<Child2> collection2;  }
    

    然后我们有了控制器:

    class EntityController{
    
      public Entity findCompraFolioFull(Integer id) {
        EntityManager em = getEntityManager();
        try {
            Entity entity =  em.find(Entity.class, id);
            //Initialize Collections inside Transaccion, this prevents
            //LazyInizialization No Proxy Exception later in code when calling
            //hollow collections
            cp.getCollection().size();
            cp.getCollection().size();
            return cp;
        } finally {
            em.close();
        }
      } 
    }
    

    我不推荐 FETCH JOIN

     public Entity findEntityByJoinFetch(Integer id) {
        EntityManager em = getEntityManager();
        try {
            TypedQuery<Entity> tq = em.createQuery(
                    "SELECT e FROM Entity e\n"
                            + "LEFT JOIN FETCH e.collection1\n"
                            + "LEFT JOIN FETCH  e.collection2\n"
                            + "WHERE e.id = :id", Entity.class);
            tq.setParameter("id", id); 
            return tq.getSingleResult();
        } finally {
            em.close();
        }
    }
    

    我不推荐 Fetch Join Appoach 的原因:

    • 如果您的集合是 java.util.List 类型,则此 getSingleResult() 将在休眠状态下失败,因为在 OneToMany 关系上没有索引符号的情况下无法获取 MultipleBags。

    • 您始终可以将集合的类型更改为 java.util.set 以便获取多个包,但这会带来新的情况需要处理,集合不是有序的和 HashCode() 方法将无法正常工作,因此您必须在子类中 @Override 它,如果您使用 JAVAFX TableView 将模型绑定到项目,您将无法将集合设置为 TableView 的项目属性,而不是直接在至少。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-02
      • 2010-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-17
      • 2011-08-10
      • 2011-12-09
      相关资源
      最近更新 更多