JBoss EAP 6
Hibernate 4
我有一个带有Web浏览器客户端的J2EE应用程序。 (Apache点击) 内部业务逻辑和客户端都使用相同的实体对象。
我想将实体中的所有关系设置为延迟加载。这样我的表现很好。
但是当使用客户端中的实体(即apache click的服务器端代码)时,我需要很多关系才能加载。客户端代码通过会话bean访问后端。
所以我有几种方法可以解决这个问题:
创建每个JPA实体中的两个,一个具有预先加载,一个具有延迟加载。然后使用客户端中急切加载的那个,以及服务器中延迟加载的那个。大多数服务器逻辑都在一个事务中,所以延迟加载就好了。
使所有关系延迟加载。从客户端访问实体时,请确保存在事务。 (@TransactionAttribute(TransactionAttributeType.REQUIRED)) 并编写对必要字段的访问权限,以便在会话bean调用后可以访问它们。 但这意味着我必须在不需要时启动事务,即如果我只获取某些对象。我必须维护更多代码。而且我必须确切地知道客户需要什么样的关系。
创建一个继承层次结构,其中我有一个超级实体,然后是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;
最佳做法是什么......或者还有其他好方法可以做到这一点。
基本上问题是我想在不同的场景中使用相同的实体。有时我需要急切加载,有时我需要延迟加载。
答案 0 :(得分:1)
我建议您只创建一个具有 lazy 关系的JPA实体,当您需要急切加载时,其中一些创建一个使用JPQL(HQL)的服务做一些FETCH技巧。这个想法是一个JPA实体和许多服务。
答案 1 :(得分:1)
我已经在JPA 2中编程了一段时间了,我可以说现在有一些我几乎总是适用的书面规则:
此规则适用于99%的项目。我认为这是最好的做法,因为我的个人经验和我一直在做的一些研究。
注意:我必须说我在Lazy Inicialization上不使用JOIN FETCH,而是编写预取方法。例如:
@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()将在hibernate中失败,原因是由于缺乏获取MultipleBags的能力而没有为你的OneToMany关系编制索引符号。
您可以随时将集合的类型更改为java.util.set,以便获取多个行李,但这会带来新的情况,而不是订购集合和HashCode()方法将无法正常工作,因此您必须在Children Classes中@Override它,如果您使用JAVAFX TableView将模型绑定到Items,您将无法绑定集合Set Type到TableView的Item属性,而不是直接在至少