何时使用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);