gpt4 book ai didi

jpa - 当使用 getOne 和 findOne 方法时 Spring Data JPA

转载 作者:塔克拉玛干 更新时间:2023-11-02 20:23:24 24 4
gpt4 key购买 nike

我有一个调用以下内容的用例:

@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl getUserControlById(Integer id){
return this.userControlRepository.getOne(id);
}

观察 @TransactionalPropagation.REQUIRES_NEW 并且存储库使用 getOne。当我运行该应用程序时,我收到以下错误消息:

Exception in thread "main" org.hibernate.LazyInitializationException: 
could not initialize proxy - no Session
...

但是如果我通过 findOne(id) 更改 getOne(id) 一切正常。

顺便说一句,就在用例调用getUserControlById 方法之前,它已经调用了insertUserControl 方法

@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl insertUserControl(UserControl userControl) {
return this.userControlRepository.save(userControl);
}

这两种方法都是Propagation.REQUIRES_NEW,因为我正在做一个简单的审计控制。

我使用 getOne 方法,因为它是在 JpaRepository 中定义的接口(interface)和我的 Repository 接口(interface)从那里扩展,当然我正在使用 JPA。

JpaRepository 接口(interface)扩展自 CrudRepository .findOne(id) 方法在 CrudRepository 中定义。

我的问题是:

  1. 为什么 getOne(id) 方法失败?
  2. 什么时候应该使用 getOne(id) 方法?

我正在使用其他存储库,并且都使用 getOne(id) 方法并且一切正常,只有当我使用 Propagation.REQUIRES_NEW 时才会失败。

根据 getOne API:

Returns a reference to the entity with the given identifier.

根据findOne API:

Retrieves an entity by its id.

  1. 什么时候应该使用 findOne(id) 方法?

  2. 推荐使用什么方法?

最佳答案

长话短说

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() .


API 变更

至少从 2.0版本,Spring-Data-Jpa已修改 findOne() .
以前,它是在 CrudRepository 中定义的界面为:

T findOne(ID primaryKey);

现在,单findOne()您将在 CrudRepository 中找到的方法是QueryByExampleExecutor中定义的哪一个界面为:

<S extends T> Optional<S> findOne(Example<S> example);

最终由 SimpleJpaRepository 实现, CrudRepository 的默认实现界面。
此方法是通过示例搜索进行查询,您不希望将其作为替代方法。

事实上,具有相同行为的方法在新API中仍然存在,只是方法名称已更改。
它从 findOne() 重命名至 findById() CrudRepository 界面:

Optional<T> findById(ID id); 

现在它返回一个 Optional .预防哪个还不错NullPointerException .

所以,现在实际的选择是 Optional<T> findById(ID id)T getOne(ID id) .


两个不同的方法依赖于两个不同的 JPA EntityManager 检索方法

1) Optional<T> findById(ID id) javadoc声明它:

Retrieves an entity by its 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));
}

这里 em.find() 是一个 EntityManager方法声明为:

public <T> T find(Class<T> entityClass, Object primaryKey,
Map<String, Object> properties);

它的 javadoc 声明:

Find by primary key, using the specified properties

因此,检索加载的实体似乎是预期的。

2) 而 T getOne(ID id) javadoc状态(重点是我的):

Returns a reference to the entity with the given identifier.

事实上,reference 术语确实是白板,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 更好地定义了它的意图(重点是我的):

Get an instance, whose state may be lazily fetched. If the requested instance does not exist in the database, the EntityNotFoundException is thrown when the instance state is first accessed. (The persistence provider runtime is permitted to throw the EntityNotFoundException when getReference is called.) The application should not expect that the instance state will be available upon detachment, unless it was accessed by the application while the entity manager was open.

因此,调用 getOne()可能会返回延迟获取的实体。
在这里,惰性抓取指的不是实体之间的关系,而是实体本身。

这意味着如果我们调用 getOne()然后关闭 Persistence 上下文,实体可能永远不会加载,因此结果真的不可预测。
例如,如果代理对象被序列化,您可以获得 null作为序列化结果引用,或者如果在代理对象上调用方法,则会出现异常,例如 LazyInitializationException被抛出。
所以在这种情况下,抛出 EntityNotFoundException这是使用 getOne() 的主要原因处理数据库中不存在的实例,因为当实体不存在时可能永远不会执行错误情况。

在任何情况下,为确保其加载,您必须在 session 打开时操作实体。您可以通过调用实体上的任何方法来实现。
或者更好的替代用途 findById(ID id)而不是。


为什么 API 如此不明确?

最后,给 Spring-Data-JPA 开发人员两个问题:

  • 为什么不为 getOne() 提供更清晰的文档? ?实体懒加载真的不是一个细节。

  • 为什么要介绍getOne()包裹 EM.getReference() ?
    为什么不简单地坚持包装方法:getReference() ?这个EM方法真的很讲究getOne()传达了如此简单的处理。

关于jpa - 当使用 getOne 和 findOne 方法时 Spring Data JPA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49319017/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com