【问题标题】:Spring data JPA save vs JPA managed objectsSpring数据JPA保存与JPA托管对象
【发布时间】:2021-03-13 09:46:59
【问题描述】:

我想知道 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 管理的实体与常规 EntityManager 操作的实体有些不同。但是某处是否有一些明确的文档? spring-data-jpa-reference.pdf 文件没有多大帮助。

【问题讨论】:

  • 您是否在@Transactional 方法中执行您的服务代码?
  • 是的,当然。
  • 打开 SQL spring.jpa.hibnerate.show-sql=true 作为开始了解发生了什么。但除此之外,谁拥有 Message-Label 关系,你为什么要脸红?显示您的标签实体代码。
  • 附带说明,如果您想了解存储库方法的工作原理,请查看SimpleJpaRepository
  • @Lesiak 所以,这就是问题所在。用 PostConstruct 注释的方法不能是事务性的。

标签: jpa spring-data-jpa entitymanager


【解决方案1】:

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

有问题的代码实际上如下:

 @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() {        
  ...
 }

反而解决了问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-28
    • 2016-02-11
    • 1970-01-01
    • 1970-01-01
    • 2017-05-26
    • 1970-01-01
    • 2021-05-31
    • 2015-05-10
    相关资源
    最近更新 更多