gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-01 19:51:11 33 4
gpt4 key购买 nike

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

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

观察@Transactional具有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 方法,因为它是在 JpaRepository 中定义的。接口(interface)和我的存储库接口(interface)从那里扩展,我当然正在使用 JPA。

JpaRepository 接口(interface)扩展自 CrudRepositoryfindOne(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.

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

  • 建议使用什么方法?

  • 最佳答案

    TL;DR

    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接口(interface)。
    此方法是通过示例搜索进行的查询,您不希望将其作为替换。

    事实上,具有相同行为的方法在新 API 中仍然存在,但方法名称已更改。
    它由 findOne() 更名至findById() CrudRepository 接口(interface):

    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.

    事实上,引用术语确实很板,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()然后持久化上下文被关闭,实体可能永远不会被加载,因此结果是不可预测的。
    例如,如果代理对象被序列化,您可能会得到 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/59088009/

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