我有一个用例,它调用以下内容:
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl getUserControlById(Integer id){
return this.userControlRepository.getOne(id);
}
注意到@transaction
具有propagation.requires_new,并且存储库使用了GetOne。当我运行应用程序时,我收到以下错误消息:
Exception in thread "main" org.hibernate.LazyInitializationException:
could not initialize proxy - no Session
...
但是,如果我将getOne(id)
改为findone(id)
,那么所有操作都很好。
顺便说一句,就在用例调用getUserControlById方法之前,它已经调用了insertUserControl方法
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl insertUserControl(UserControl userControl) {
return this.userControlRepository.save(userControl);
}
这两个方法都是propagation.requires_new,因为我正在做一个简单的审核控制。
我使用getOne
方法,因为它是在JPararePository接口中定义的,我的存储库接口从那里扩展过来的,当然我使用的是JPA。
JPararePository接口扩展自CrudRepository。findone(id)
方法在CrudRepository
中定义。
我的问题是:
getOne(id)
方法失败?getOne(id)
方法?我正在处理其他存储库,所有存储库都使用getOne(id)
方法,并且所有存储库都运行良好,只是当我使用propagation.requires_new时它失败了。
根据getOne API:
返回对具有给定标识符的实体的引用。
符合findOne API:
按实体的ID检索实体。
什么时候应该使用findone(id)
方法?
建议使用什么方法?
t findOne(ID ID)
(旧API中的名称)/可选的
(新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);
现在,您将在CrudRepository
中找到的单个findone()
方法是在QueryByExampleExecutor
接口中定义为:
<S extends T> Optional<S> findOne(Example<S> example);
该方法最终由SimpleJparePository
实现,它是CrudRepository
接口的默认实现。
此方法是按示例搜索的查询,您不希望将其作为替换。
实际上,具有相同行为的方法在新API中仍然存在,但方法名称已更改。
在CrudRepository
接口中将其从FindOne()
重命名为FindById()
:
Optional<T> findById(ID id);
现在它返回一个optional
。这对于防止NullPointerException
并不是很坏。
因此,现在实际的选择是在optional
和tgetOne(ID ID)
之间进行。
1)可选
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));
}
public <T> T find(Class<T> entityClass, Object primaryKey,
Map<String, Object> properties);
其javadoc状态为:
使用指定属性按主键查找
因此,检索加载的实体似乎是意料之中的事。
返回对具有给定标识符的实体的引用。
实际上,参考术语实际上是board,而JPA API没有指定任何getOne()
方法。
所以要理解Spring包装器做什么,最好的方法就是研究实现:
@Override
public T getOne(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
return em.getReference(getDomainClass(), id);
}
这里的em.getReference()
是一个EntityManager
方法,声明为:
public <T> T getReference(Class<T> entityClass,
Object primaryKey);
幸运的是,EntityManager
javadoc更好地定义了它的意图(重点是我的):
获取一个实例,该实例的状态可能被懒洋洋地提取。如果数据库中不存在请求的实例,则在首次访问实例状态时引发EntityNotFoundException。(允许持久性提供程序运行库在调用getReference时抛出EntityNotFoundException。)应用程序不应期望实例状态在分离时可用,除非它在实体管理器打开时被应用程序访问。
因此,调用getOne()
可能返回一个懒散提取的实体。
这里,懒散提取不是指实体的关系,而是指实体本身。
这意味着如果我们调用getOne()
并关闭持久性上下文,该实体可能从未加载,因此结果确实不可预测。
例如,如果序列化代理对象,您可能将null
引用作为序列化结果,或者如果在代理对象上调用方法,则引发LazyInitializationException
之类的异常。
因此,在这种情况下,当该实体不存在时,可能从未执行EntityNotFoundException
的抛出,该抛出是使用GetOne()
处理数据库中不存在的实例的主要原因。
在任何情况下,为了确保它的加载,您必须在会话打开时操作实体。您可以通过调用实体上的任何方法来实现。
或更好的替代方法,使用findbyid(ID ID)
代替。
最后,给Spring-Data-JPA开发人员两个问题:
>
为什么不为getOne()
提供更清晰的文档?实体懒加载真的不是一个细节。
为什么需要引入getOne()
来包装EM.getReference()
?
为什么不简单地坚持包装的方法:getReference()
?这个EM方法非常特别,而getOne()
传达了一个如此简单的处理。
问题内容: 我读到它是延迟加载的,并立即获取整个实体。我检查调试日志,我甚至能监视我的SQL服务器上看到的语句被执行的东西,我发现,无论和生成和执行相同的查询。但是,当我使用时,这些值最初为null(当然,除了id)。 因此,谁能告诉我,如果两种方法都在数据库上执行相同的查询,那为什么我要在另一种方法上使用呢?我基本上是在寻找一种获取实体而不获取其所有子代/属性的方法。 编辑1: 实体代码 道代码
问题内容: 我在项目中使用Spring 5。直到今天,仍然有可用的方法。 但是下载最新的快照后,它突然消失了!有没有参考说明该方法现在不可用? 我的依赖项列表: 更新: 似乎此方法已被替换为 问题答案: 请参阅与该提交关联的DATACMNS-944,它具有以下重命名
我在我的项目中使用Spring 5。直到今天,还有可用的方法。 但下载最新快照后,它突然消失了!有没有参考现在没有的方法? 我的依赖项列表: 更新: 似乎此方法已被
我不知道PATCH和PUT方法之间的确切区别。有人能告诉我什么时候我们应该用适当的例子来使用这个方法吗?
我正在努力做到没有方法,以便更好地把握概念。 我真的很接近。我的数学课好像没课了。有什么我不明白的? 输出:
问题内容: 我在一个教程中找到了这个示例。 当我运行此程序时,我的hs.size()值为2 ..,并且equals方法仅被调用一次。有人解释了我何时在HashSet中调用equal()方法 当上述程序中的equal方法调用时 问题答案: 您的代码将调用的唯一一次。另一次调用方法将是对的类。 此答案说明何时由和何时调用方法。摘录: HashSet利用哈希码来加快处理速度。假定彼此相等的两个对象将具有