Spring 数据 JPA 保存与 JPA 管理对象

Spring data JPA save vs JPA managed objects

我想知道 Spring Data JPA 的实际行为在哪里记录,因为它恰好是 与实体管理器在常规 JPA 中的预期不同。我有兴趣找到 关于 Spring JPA 中实体生命周期的文档。

例如,假设我们有三个实体,MessageAuthorLabel。一条消息可以有多个标签,但只能有一个作者。

所以消息基本上是:

@Entity
public class Message implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;
    @ManyToMany
    private Set<Label> labels = new HashSet<>();
    @ManyToOne
    private Author author;
    ...
}

链接是单向的。

考虑服务中的以下代码:

  Author a = new Author("auteur A");
  a = authorRepository.save(a);
  Message msg = new Message("un message", "texte du message", a);
  msg = messageRepository.save(msg);            
  for (int i=0; i < 10; i++) {
    Label lab = new Label("label"+i);
    lab = labelRepository.save(lab);
    labelRepository.flush();                
    msg.addLabel(lab);
  }            
  messageRepository.save(msg);

如果我省略最后一行 (messageRepository.save(msg)),则会创建标签,但它们并没有真正添加到邮件中。考虑到底层技术是 JPA,我觉得这出乎意料:

等效代码为 带有实体管理器的标准 JPA 将是:

 EntityManagerFactory emf = Persistence.createEntityManagerFactory("demo1PU");
 EntityManager em = emf.createEntityManager();
 EntityTransaction transaction = em.getTransaction();
 transaction.begin();
 Author a = new Author("auteur A");
 em.persist(a);
 Message msg = new Message("un message", "texte du message", a);
 em.persist(msg);            
 for (int i=0; i < 10; i++) {
   Label lab = new Label("label"+i);
   em.persist(lab);              
   msg.addLabel(lab);
 }            
 transaction.commit();        
 em.close();
 emf.close();

在基于entitymanager的代码中,你不需要保存消息两次:因为你还在同一个 事务,消息是一个 托管实体, 并且在事务处于活动状态时对该对象所做的所有更改也会对数据库条目进行。

显然,Spring 管理的实体与常规 EntityManagers 管理的实体有些不同。但是某处有一些明确的文档吗? spring-data-jpa-reference.pdf 文件没有多大帮助。

好的,只是为了避免人们很难弄清楚问题是什么:

违规代码实际上如下:

 @Transactional
 @PostConstruct
 public void initDatabase() {        
        if (messageRepository.count() == 0) {
            Auteur a = new Auteur("auteur A");
            a = auteurRepository.save(a);
            Message msg = new Message("un message", "texte du message", a);
            msg = messageRepository.save(msg); // (pas nécessaire, vu qu'on le fait à la fin de la méthode...)
            for (int i = 0; i < 10; i++) {
                Label lab = new Label("label" + i);
                lab = labelRepository.save(lab);
                msg.addLabel(lab);
            }          
        }
    }

而且问题是在调用@PostConstruct注解的方法时,已经做了依赖注入,这很好,但是整个应用程序上下文还没有准备好,所以代码没有在事务中执行。

只需使用

 @Transactional
 @EventListener(ContextRefreshedEvent.class)
 public void initDatabase() {        
  ...
 }

反而解决了问题。