Sib*_*Ter 98 java persistence jpa java-ee
我遇到过一种情况(我觉得很奇怪,但可能很正常),我使用EntityManager.getReference(LObj.getClass(),LObj.getId())获取数据库实体,然后将返回的对象传递给坚持在另一张桌子上.
所以基本上流程是这样的:
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
class FLHFacade{
createFLH(FObj, LObj){
FLH FLHObj = new FLH();
FLHObj.setF(FObj);
FLHObj.setL(LObj);
....
EntityManager.persist(FLHObj);
...
}
}
我得到以下异常"java.lang.IllegalArgumentException:未知实体:com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0"
在查看了一段时间之后,我终于想通了,因为我正在使用EntityManager.getReference()方法,因为该方法正在返回代理,所以我得到了上述异常.
这让我想知道,何时建议使用EntityManager.getReference()方法而不是EntityManager.find()方法?
如果EntityManager.getReference()无法找到正在搜索的实体本身非常方便,则会抛出EntityNotFoundException.如果EntityManager.find()方法无法找到实体,则它仅返回null.
关于事务边界,听起来像你需要在将新发现的实体传递给新事务之前使用find()方法.如果你使用getReference()方法,那么你可能会遇到类似我的情况,但有上述异常.
Art*_*ald 146
当我不需要访问数据库状态时,我通常使用getReference方法(我的意思是getter方法).只是改变状态(我的意思是setter方法).您应该知道,getReference返回一个代理对象,该对象使用称为自动脏检查的强大功能.假设如下
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);
}
}
Run Code Online (Sandbox Code Playgroud)
如果我调用find方法,JPA提供商,在幕后,将调用
SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ?
UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?
Run Code Online (Sandbox Code Playgroud)
如果我调用getReference方法,JPA提供者,在幕后,将调用
UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?
Run Code Online (Sandbox Code Playgroud)
你知道为什么???
当您调用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;
}
}
Run Code Online (Sandbox Code Playgroud)
因此,在事务提交之前,JPA提供程序将看到stateChanged标志以更新OR NOT person实体.如果在update语句之后没有更新行,则JPA提供程序将根据JPA规范抛出EntityNotFoundException.
问候,
Vla*_*cea 14
正如我在本文中解释的那样,假设您有一个父Post实体和一个子实现PostComment,如下图所示:
如果您find在尝试设置@ManyToOne post关联时调用:
PostComment comment = new PostComment();
comment.setReview("Just awesome!");
Post post = entityManager.find(Post.class, 1L);
comment.setPost(post);
entityManager.persist(comment);
Run Code Online (Sandbox Code Playgroud)
Hibernate将执行以下语句:
SELECT p.id AS id1_0_0_,
p.title AS title2_0_0_
FROM post p
WHERE p.id = 1
INSERT INTO post_comment (post_id, review, id)
VALUES (1, 'Just awesome!', 1)
Run Code Online (Sandbox Code Playgroud)
这次SELECT查询没用,因为我们不需要获取Post实体.我们只想设置底层的post_id外键列.
现在,如果您使用getReference:
PostComment comment = new PostComment();
comment.setReview("Just awesome!");
Post post = entityManager.getReference(Post.class, 1L);
comment.setPost(post);
entityManager.persist(comment);
Run Code Online (Sandbox Code Playgroud)
这次,Hibernate将只发出INSERT语句:
INSERT INTO post_comment (post_id, review, id)
VALUES (1, 'Just awesome!', 1)
Run Code Online (Sandbox Code Playgroud)
与之不同find,getReference唯一返回仅具有标识符集的实体代理.如果访问代理,只要EntityManager仍处于打开状态,就会触发关联的SQL语句.
但是,在这种情况下,我们不需要访问实体代理.我们只想将外键传播到基础表记录,因此加载代理就足以满足此用例.
加载代理时,您需要知道,如果在关闭EntityManager后尝试访问代理引用,则可能抛出LazyInitializationException.有关处理的更多详细信息LazyInitializationException,请查看此文章.
dav*_*xxx 10
这让我想知道,什么时候最好使用 EntityManager.getReference() 方法而不是 EntityManager.find() 方法?
EntityManager.getReference()确实是一种容易出错的方法,并且客户端代码需要使用它的情况确实很少。
就个人而言,我从来不需要使用它。
我不同意接受的答案,特别是:
如果我调用find方法,幕后的 JPA 提供者将调用
Run Code Online (Sandbox Code Playgroud)SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ? UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?如果我调用getReference方法,幕后的 JPA 提供者将调用
Run Code Online (Sandbox Code Playgroud)UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?
这不是我在 Hibernate 5 中得到的行为,并且 javadocgetReference()没有说这样的话:
获取一个实例,其状态可能会被延迟获取。如果请求的实例在数据库中不存在,则在第一次访问实例状态时抛出 EntityNotFoundException。(当调用 getReference 时,持久性提供程序运行时被允许抛出 EntityNotFoundException。)应用程序不应期望实例状态在分离时可用,除非它在实体管理器打开时被应用程序访问。
EntityManager.getReference() 在两种情况下不使用查询来检索实体:
1) 如果实体存储在 Persistence 上下文中,那就是一级缓存。
并且此行为并非特定于EntityManager.getReference(),EntityManager.find()如果实体存储在 Persistence 上下文中,
也将节省查询以检索实体。
您可以使用任何示例检查第一点。
您还可以依赖实际的 Hibernate 实现。
确实,EntityManager.getReference()依赖类的createProxyIfNecessary()方法org.hibernate.event.internal.DefaultLoadEventListener来加载实体。
这是它的实现:
private Object createProxyIfNecessary(
final LoadEvent event,
final EntityPersister persister,
final EntityKey keyToLoad,
final LoadEventListener.LoadType options,
final PersistenceContext persistenceContext) {
Object existing = persistenceContext.getEntity( keyToLoad );
if ( existing != null ) {
// return existing object or initialized proxy (unless deleted)
if ( traceEnabled ) {
LOG.trace( "Entity found in session cache" );
}
if ( options.isCheckDeleted() ) {
EntityEntry entry = persistenceContext.getEntry( existing );
Status status = entry.getStatus();
if ( status == Status.DELETED || status == Status.GONE ) {
return null;
}
}
return existing;
}
if ( traceEnabled ) {
LOG.trace( "Creating new proxy for entity" );
}
// return new uninitialized proxy
Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );
persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( keyToLoad );
persistenceContext.addProxy( keyToLoad, proxy );
return proxy;
}
Run Code Online (Sandbox Code Playgroud)
有趣的部分是:
Object existing = persistenceContext.getEntity( keyToLoad );
Run Code Online (Sandbox Code Playgroud)
2)如果我们没有有效地操作实体,就会与javadoc的懒惰获取相呼应。
实际上,为了确保实体的有效加载,需要对其调用方法。
那么增益将与我们想要加载一个实体而不需要使用它的场景有关?在应用程序框架中,这种需求确实不常见,此外,getReference()如果您阅读下一部分,这种行为也非常具有误导性。
在开销方面,getReference()并不比find()上一点中讨论的要好。
那么为什么要使用一个或另一个呢?
调用getReference()可能会返回一个延迟获取的实体。
在这里,延迟获取不是指实体的关系,而是实体本身。
这意味着如果我们调用getReference()然后 Persistence 上下文关闭,则实体可能永远不会加载,因此结果真的不可预测。例如,如果代理对象被序列化,您可以获得null作为序列化结果的引用,或者如果在代理对象上调用了一个方法,LazyInitializationException则会引发诸如此类的异常。
这意味着抛出EntityNotFoundException它是getReference()用于处理数据库中不存在的实例的主要原因,因为在实体不存在时可能永远不会执行错误情况。
EntityManager.find()EntityNotFoundException如果找不到实体,则没有投掷的野心。它的行为既简单又清晰。您永远不会感到惊讶,因为它始终返回一个已加载的实体或null(如果未找到该实体),但永远不会返回可能无法有效加载的代理形状下的实体。
所以EntityManager.find()在大多数情况下应该受到青睐。
因为引用是"托管的",但不是水合的,它还允许您通过ID删除实体,而无需先将其加载到内存中.
由于无法删除非托管实体,使用find(...)或createQuery(...)加载所有字段只是为了立即删除它.
MyLargeObject myObject = em.getReference(MyLargeObject.class, objectId);
em.remove(myObject);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
125722 次 |
| 最近记录: |