JPA - Eager - Lazy最佳实践

4

JBoss EAP 6
Hibernate 4

我有一个Web浏览器客户端的J2EE应用程序。在内部业务逻辑和客户端中都使用相同的实体对象。

我希望将实体中的所有关系设置为延迟加载,以获得良好的性能。

但是当在客户端(即 Apache click 的服务器端代码)中使用这些实体时,我需要许多关系被急切地加载。客户端通过会话bean访问后端。

所以我有几种解决方法:

  1. 创建两个JPA实体,一个具有急切加载,一个具有延迟加载。然后在客户端中使用具有急切加载的实体,在服务器端使用具有延迟加载的实体。大部分服务器逻辑都在事务中,因此延迟加载在此处很好。

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

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

Super

    @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;

什么是最佳实践...或者说还有其他好的方法可以解决这个问题。

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


1
我的选择是第二个。第一个和第三个将来会带来很大的麻烦。 - Leo
我做过类似于你的#1和#3的事情,对我来说效果还不错。区别在于,我没有为较小的实体设置瞬态或惰性属性,所有实体最终都会被转换成模型,因此当从较小的实体传递时,模型将只有一个空值。是的,这变得有点头疼,因为它是一个具有许多用例的复杂实体,但仍然是合理的。我希望JPA有一个更好的解决方案,就像C#的Entity Framework一样...对于这个问题,您只需添加.Include(o => o.printCustomerName)即可包含它。 - Cameron Askew
2个回答

2

我已经使用JPA 2编程一段时间了,我可以说有几条写作规则我几乎总是遵循的:

  1. 在所有的OneToMany、ManyToMany关系上使用LAZY初始化
  2. 在所有的OneToOne、ManyToOne关系上使用EAGER初始化

这些规则适用于我99%的项目。我认为这些是最佳实践,基于我的个人经验和一些研究。

注意:我必须说我不在Lazy初始化上使用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方法的原因:

  • 如果您的集合类型是java.util.List,则由于缺乏在OneToMany关系上进行索引注释以获取MultipleBags的能力,Hibernate中的getSingleResult()将失败。

  • 您可以始终将集合类型更改为java.util.set,以便获取多个袋子,但这会带来新的处理情况,Set没有排序,HashCode()方法无法正常工作,因此您必须在Children类中@覆盖它,并且如果您正在使用JAVAFX TableView将模型绑定到Items,您将无法直接将集合Set类型绑定到TableView的Item属性。


1

我建议您创建一个JPA实体,其中包含延迟加载关系。当您需要立即加载其中的一些关系时,请创建一个服务,并使用JPQL(HQL)来执行FETCH技巧。这个想法是一个JPA实体和许多服务。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接