为什么要在Spring Data JPA Repository上的save()之后使用返回的实例?
这里是代码:
@Repository public interface AccountRepository extends JpaRepository<Account, Long> {}
来自Spring Data JPA项目的JpaRepository 。
这里是testing代码:
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()); } }
结果如下:
account ID is 0 and for returned account ID is 1
这里是从CrudReporsitory.save()javadoc:
保存给定的实体。 使用返回的实例进行进一步操作,因为保存操作可能已经完全更改了实体实例。
这里是Spring Data JPA的SimpleJpaRepository的实际代码:
@Transactional public T save(T entity) { if (entityInformation.isNew(entity)) { em.persist(entity); return entity; } else { return em.merge(entity); } }
所以,问题是为什么我们需要使用返回的实例而不是原来的实例呢? (是的,我们必须这样做,否则我们继续与独立的实例合作,但为什么)
最初的EntityManager.persist()方法返回void,所以我们的实例被连接到持久化上下文。 在将帐户传递到存储库时是否发生了一些代理魔术? 是Spring Data JPA项目的架构限制吗?
CrudRepository
接口的save(…)
方法应该抽象地简单地存储一个实体,而不CrudRepository
在什么状态。因此,它不能公开实际的存储特定的实现,即使(如在JPA中)商店区分要存储的新实体和现有的要更新的实体。 这就是为什么该方法实际上被称为save(…)
而不是create(…)
或update(…)
。 我们从该方法返回一个结果,实际上允许商店实现返回一个完全不同的实例,就像JPA在调用merge(…)
时潜在的那样。
所以一般来说,API的决定更多的是对实际的实现宽松,从而像我们一样执行JPA的方法。 对通过的实体没有额外的代理按摩。
您错过了第二部分:如果实体不是新的,则调用merge
。 merge
其参数的状态复制到具有相同ID的附加实体中,并返回附加实体。 如果实体不是新的,并且不使用返回的实体,则将对分离的实体进行修改。