何时使用EntityManager.find()与EntityManager.getReference()

我遇到了一种情况(我认为这很奇怪,但可能很正常),我使用EntityManager.getReference(LObj.getClass(),LObj.getId())获取数据库实体,然后将返回的对象传递给坚持在另一张桌子上。

所以stream量基本上是这样的:

 class TFacade {

   createT(FObj,AObj){
     T TObj = new T();
     TObj.setF(FObj);
     TObj.setA(AObj);
     ...
     EntityManager.persist(TObj);
     ...
     L LObj = A.getL();
     FObj.setL(LObj);
     FFacade.editF(FObj);
   }
 }

 @ TransactionAttributeType.REQUIRES_NEW
 class FFacade {

   editF(FObj){
     L LObj = FObj.getL();
     LObj = EntityManager.getReference(LObj.getClass(),LObj.getId());
     ...
     EntityManager.merge(FObj);
     ...
     FLHFacade.create(FObj,LObj);
   }
 }

 @ TransactionAttributeType.REQUIRED
 FLHFacade类{

   createFLH(FObj,LObj){
     FLH FLHObj = new FLH();
     FLHObj.setF(FObj);
     FLHObj.setL(LObj);
     ....
     EntityManager.persist(FLHObj);
     ...
   }
 }

我得到以下exception“java.lang.IllegalArgumentException:未知实体:com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0”

看了一会儿之后,我终于明白,这是因为我正在使用EntityManager.getReference()方法获取上述exception,因为方法正在返回代理。

这让我想知道什么时候使用EntityManager.getReference()方法而不是EntityManager.find()方法是可取的

EntityManager.getReference()抛出一个EntityNotFoundException,如果它不能find正在被search的实体,这本身是非常方便的。 如果EntityManager.find()方法找不到实体,则只返回null。

关于事务边界,对我来说听起来像你需要使用find()方法,然后将新发现的实体传递给新的事务。 如果你使用getReference()方法,那么你可能最终会出现类似于我的情况,上面的例外。

我通常使用getReference方法,当我不需要访问数据库状态(我的意思是getter方法)。 只是改变状态(我是指setter方法)。 正如你应该知道的,getReference返回一个代理对象,它使用了一个强大的function,叫做自动脏检查。 假设如下

public class Person { private String name; private Integer age; } public class PersonServiceImpl implements PersonService { public void changeAge(Integer personId, Integer newAge) { Person person = em.getReference(Person.class, personId); // person is a proxy person.setAge(newAge); } } 

如果我调用find方法,JPA提供程序将在幕后调用

 SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ? UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

如果我调用getReference方法,JPA提供者将在幕后调用

 UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

你知道为什么?

当你调用getReference时,你会得到一个代理对象。 像这样的东西(JPA提供者负责实现这个代理)

 public class PersonProxy { // JPA provider sets up this field when you call getReference private Integer personId; private String query = "UPDATE PERSON SET "; private boolean stateChanged = false; public void setAge(Integer newAge) { stateChanged = true; query += query + "AGE = " + newAge; } } 

所以在事务提交之前,JPA提供者会看到stateChanged标志以更新OR NOT person实体。 如果更新语句后没有更新行,则JPA提供程序将根据JPA规范抛出EntityNotFoundException。

问候,

由于引用是“托pipe”的,但不是水合的,它也可以允许你通过ID删除一个实体,而不需要先把它加载到内存中。

由于无法删除非托pipe实体,因此使用find(…)或createQuery(…)加载所有字段只是简单的愚蠢,只能立即删除它。

 MyLargeObject myObject = em.getReference(MyLargeObject.class, objectId); em.remove(myObject);