代码如下:
@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {}
JpaRepository from Spring Data JPA project.
下面是测试代码: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 %dn", 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(…)
方法应该抽象地简单地存储一个实体,而不管它处于什么状态。因此,它不能公开实际的特定于存储的实现,即使(如在JPA中)存储区分了要存储的新实体和要更新的现有实体。这就是为什么这个方法实际上被称为save(…)
,而不是create(…)
或update(…)
。我们从该方法返回一个结果,以实际允许存储实现返回一个完全不同的实例,就像JPA在调用merge(…)
时可能做的那样。
所以一般来说,对于实际实现来说,API的决定更宽松(允许的,宽容的),从而像我们一样实现JPA的方法。没有对传递的实体做额外的代理消息处理。
您错过了第二部分:如果实体不是新的,则调用merge
。merge
将其参数的状态复制到具有相同ID的附加实体中,并返回附加实体。如果实体不是新的,并且不使用返回的实体,则需要对分离实体进行修改。