Man*_*dan 135 jpa spring-data spring-data-jpa
我有一个用例,它调用以下内容:
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl getUserControlById(Integer id){
return this.userControlRepository.getOne(id);
}
Run Code Online (Sandbox Code Playgroud)
观察@Transactional
has Propagation.REQUIRES_NEW,存储库使用getOne.当我运行该应用程序时,收到以下错误消息:
Exception in thread "main" org.hibernate.LazyInitializationException:
could not initialize proxy - no Session
...
Run Code Online (Sandbox Code Playgroud)
但是,如果我改变getOne(id)
的findOne(id)
一切工作正常.
顺便说一下,就在用例调用getUserControlById方法之前,它已经调用了insertUserControl方法
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl insertUserControl(UserControl userControl) {
return this.userControlRepository.save(userControl);
}
Run Code Online (Sandbox Code Playgroud)
两种方法都是Propagation.REQUIRES_NEW,因为我正在进行简单的审计控制.
我使用该getOne
方法,因为它是在JpaRepository接口中定义的,我的Repository接口从那里扩展,我当然正在使用JPA.
该JpaRepository接口扩展自CrudRepository.该findOne(id)
方法定义于CrudRepository
.
我的问题是:
getOne(id)
方法?getOne(id)
方法?我正在使用其他存储库并且所有使用该getOne(id)
方法并且所有工作正常,只有当我使用Propagation.REQUIRES_NEW它失败时.
根据getOne API:
返回对具有给定标识符的实体的引用.
根据findOne API:
按ID查找实体.
3)什么时候应该使用这种findOne(id)
方法?
4)建议使用哪种方法?
提前致谢.
Mar*_*lmo 115
基本的区别是getOne
延迟加载而findOne
不是.
请考虑以下示例:
public static String NON_EXISTING_ID = -1;
...
MyEntity getEnt = myEntityRepository.getOne(NON_EXISTING_ID);
MyEntity findEnt = myEntityRepository.findOne(NON_EXISTING_ID);
if(findEnt != null) {
findEnt.getText(); // findEnt is null - this code is not executed
}
if(getEnt != null) {
getEnt.getText(); // Throws exception - no data found, BUT getEnt is not null!!!
}
Run Code Online (Sandbox Code Playgroud)
dav*_*xxx 100
T findOne(ID id)
(旧API中的Optional<T> findById(ID id)
名称)/ (新API中的名称)依赖于EntityManager.find()
执行实体急切加载.
T getOne(ID id)
依赖于EntityManager.getReference()
执行实体延迟加载.因此,为了确保实体的有效加载,需要在其上调用方法.
findOne()/findById()
使用起来比使用起来更清晰,更简单getOne()
.
因此,在很大部分的情况下,倾向于findOne()/findById()
过getOne()
.
至少,2.0
版本,Spring-Data-Jpa
修改findOne()
.
以前,它在CrudRepository
界面中定义为:
T findOne(ID primaryKey);
Run Code Online (Sandbox Code Playgroud)
现在,findOne()
您将找到的单个方法CrudRepository
是在QueryByExampleExecutor
接口中定义的方法:
<S extends T> Optional<S> findOne(Example<S> example);
Run Code Online (Sandbox Code Playgroud)
最后通过接口SimpleJpaRepository
的默认实现来CrudRepository
实现.
此方法是通过示例搜索查询,您不希望将其作为替换.
实际上,具有相同行为的方法仍然存在于新API中,但方法名称已更改.
它是从更名findOne()
到findById()
的CrudRepository
接口:
Optional<T> findById(ID id);
Run Code Online (Sandbox Code Playgroud)
现在它返回一个Optional
.哪个不是很难预防NullPointerException
.
所以,实际的选择现在介于Optional<T> findById(ID id)
和之间T getOne(ID id)
.
1)Optional<T> findById(ID id)
javadoc声明它:
按ID查找实体.
在我们研究实现时,我们可以看到它依赖于EntityManager.find()
执行检索:
public Optional<T> findById(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
Class<T> domainType = getDomainClass();
if (metadata == null) {
return Optional.ofNullable(em.find(domainType, id));
}
LockModeType type = metadata.getLockModeType();
Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();
return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));
}
Run Code Online (Sandbox Code Playgroud)
这em.find()
是一个EntityManager
声明为的方法:
public <T> T find(Class<T> entityClass, Object primaryKey,
Map<String, Object> properties);
Run Code Online (Sandbox Code Playgroud)
它的javadoc说:
使用指定的属性按主键查找
因此,预计检索加载的实体.
2)虽然T getOne(ID id)
javadoc声明(重点是我的):
返回对具有给定标识符的实体的引用.
实际上,参考术语实际上是板,而JPA API没有指定任何getOne()
方法.
因此,了解Spring包装器的作用最好的方法是查看实现:
@Override
public T getOne(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
return em.getReference(getDomainClass(), id);
}
Run Code Online (Sandbox Code Playgroud)
这em.getReference()
是一个EntityManager
声明为的方法:
public <T> T getReference(Class<T> entityClass,
Object primaryKey);
Run Code Online (Sandbox Code Playgroud)
幸运的是,EntityManager
javadoc更好地定义了它的意图(重点是我的):
获取一个实例,其状态可能会被懒惰地取出.如果数据库中不存在请求的实例,则在首次访问实例状态时将引发EntityNotFoundException .(在调用getReference时,允许持久性提供程序运行时抛出EntityNotFoundException.)除非在实体管理器打开时应用程序访问实例状态,否则应用程序不应期望实例状态在分离时可用.
因此,调用getOne()
可能会返回一个延迟获取的实体.
这里,延迟提取不是指实体的关系,而是实体本身.
这意味着如果我们调用getOne()
然后关闭Persistence上下文,则实体可能永远不会被加载,因此结果实际上是不可预测的.
例如,如果代理对象是序列化的,则可以将null
引用作为序列化结果获取,或者如果在代理对象上调用方法,LazyInitializationException
则会引发异常,例如抛出.
因此,在这种情况下,抛出EntityNotFoundException
这是getOne()
用于处理数据库中不存在的实例的主要原因,因为在实体不存在时可能永远不会执行错误情况.
在任何情况下,为了确保其加载,您必须在会话打开时操纵实体.您可以通过调用实体上的任何方法来完成此操作.
或者更好的替代用途findById(ID id)
而不是.
为了完成,Spring-Data-JPA开发人员有两个问题:
为什么没有更清晰的文档getOne()
?实体延迟加载实际上不是一个细节.
为什么需要介绍getOne()
包装EM.getReference()
?
为什么不简单地坚持包裹的方法:getReference()
?这种EM方法非常特别,同时getOne()
传达了如此简单的处理.
Don*_*ler 71
1.为什么getOne(id)方法失败?
请参阅文档中的此部分.覆盖已就位的事务可能会导致问题.但是,如果没有更多信息,这个很难回答.
2.什么时候应该使用getOne(id)方法?
如果不深入研究Spring Data JPA的内部结构,差异似乎在于用于检索实体的机制.
如果你看的JavaDoc为getOne(ID)
下另请参见:
See Also:
EntityManager.getReference(Class, Object)
Run Code Online (Sandbox Code Playgroud)
似乎这个方法只是委托给JPA实体管理器的实现.
但是,文档中findOne(ID)
没有提到这一点.
线索也在存储库的名称中.
JpaRepository
是特定于JPA的,因此如果需要,可以将调用委托给实体管理器.
CrudRepository
与所使用的持久性技术无关.看这里.它被用作多种持久性技术的标记接口,如JPA,Neo4J等.
因此,对于您的用例,这两种方法并没有真正的"差异",它只是findOne(ID)
比更专业的更通用getOne(ID)
.您使用哪一个取决于您和您的项目,但我会亲自坚持,findOne(ID)
因为它使您的代码更少的实现特定,并打开大门移动到像MongoDB等未来没有太多重构:)
归档时间: |
|
查看次数: |
146457 次 |
最近记录: |