为什么在Spring Data JPA Repository上的save()之后使用返回的实例?

aka*_*lou 55 java spring jpa spring-data spring-data-jpa

这是代码:

@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {}
Run Code Online (Sandbox Code Playgroud)

来自Spring Data JPA项目的JpaRepository.

这是测试代码:

public class JpaAccountRepositoryTest extends JpaRepositoryTest {
    @Inject
    private AccountRepository accountRepository;

    @Inject
    private Account account;

    @Test
    @Transactional
    public void createAccount() {
        Account returnedAccount = accountRepository.save(account);

        System.out.printf("account ID is %d and for returned account ID is %d\n", account.getId(), returnedAccount.getId());
    }
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

account ID is 0 and for returned account ID is 1
Run Code Online (Sandbox Code Playgroud)

这是来自CrudReporsitory.save()javadoc:

保存给定的实体.使用返回的实例进行进一步操作,因为save操作可能已完全更改了实体实例.

以下是Spring Data JPA中SimpleJpaRepository的实际代码:

 @Transactional
    public T save(T entity) { 
            if (entityInformation.isNew(entity)) {
                    em.persist(entity);
                    return entity;
            } else {
                    return em.merge(entity);
            }
    }
Run Code Online (Sandbox Code Playgroud)

所以,问题是为什么我们需要使用返回的实例而不是原始实例?(是的,我们必须这样做,否则我们继续使用分离的实例,但为什么)

原始的EntityManager.persist()方法返回void,因此我们的实例附加到持久化上下文.通过帐户保存到存储库时是否会发生一些代理魔术?它是Spring Data JPA项目的架构限制吗?

Oli*_*ohm 60

接口的save(…)方法CrudRepository应该抽象简单地存储一个实体,无论它处于什么状态.因此它不得暴露实际的商店特定实现,即使(如在JPA中)情况,商店区分要存储的新实体和现有的更新.这就是为什么该方法实际上save(…)不被称为create(…)update(…).我们从该方法返回一个结果,实际上允许商店实现返回一个完全不同的实例,因为JPA merge(…)在调用时可能会这样做.

因此,一般来说,更多的API决定对于实际实现是宽松的(允许的,容忍的),从而像我们一样实现JPA的方法.传递的实体没有额外的代理消息传递.

  • 有关返回对象状态的一些文档在哪里?即,有时它返回更新的字段(时间戳),有时字段不会从DB更新. (4认同)
  • 乐观锁定和 @Version 列也存在类似问题。它在数据库中更新,但在返回的实体中未更新。JPA 肯定应该知道它已更新...... (2认同)

JB *_*zet 15

你错过了第二部分:如果实体不是新的,merge则被调用.merge将其参数的状态复制到具有相同ID的附加实体,并返回附加的实体.如果实体不是新实体,并且您不使用返回的实体,则您将对分离的实体进行修改.