首页 > 解决方案 > 当外部事务提交时,Spring Boot 执行与内部(REQUIRED_NEW)事务先前所做的相同的插入

问题描述

@Entity
public class User {

  @Id
  private Long id;

  @OneToOne(mappedBy = "user", cascade = {CascadeType.MERGE,CascadeType.PERSIST}, fetch = FetchType.LAZY)
  private Profile profile;
}

@Entity
public class Profile {

  @Id
  @Column(name = "user_id")
  private Long id;

  @OneToOne
  @MapsId
  @JoinColumn(name = "user_id")
  private User user;
}

public class ProcessUsers {
  @Transactional
  public void processUsers() {
       List<Users> users = repo.getUsers();

       processPart.processPart(users.getPart())
  }
}

public class ProcessPart {

   @Transactional(propagation = Propagation.REQUIRES_NEW)
   public void processPart(List<User> users) { 
      users.forEach(user -> user.setProfile(new Profile(user))
      repo.saveAll(users)
   }  
}

我有从数据库加载用户的外部事务。然后将用户拆分为多个部分,并在新的(Requires_new)内部事务中进行处理。在每个用户的内部事务期间,我创建配置文件实体然后保存这些用户。当内部事务结束时,它会为新的配置文件执行提交和 sql 插入,并为现有用户执行更新。但是当外部事务结束并执行提交时,它会执行相同的插入和更新,并且我得到唯一的主键违规。

日志:

JpaTransactionManager  : Initiating transaction commit
JpaTransactionManager  : Committing JPA transaction on EntityManager [SessionImpl(48420828<open>)]
org.hibernate.SQL         : insert into profile (user_id, another_field) values (?, ?)
org.hibernate.SQL         : update user set updated_field = ? where id=?
JpaTransactionManager     : Closing JPA EntityManager [SessionImpl(48420828<open>)] after transaction


JpaTransactionManager        : Resuming suspended transaction after completion of inner transaction
JpaTransactionManager        : Initiating transaction commit
JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(713565825<open>)]
org.hibernate.SQL            : insert into profile (user_id, another_field) values (?, ?)
SqlExceptionHelper   : SQL Error: 23505, SQLState: 23505
SqlExceptionHelper   : Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.PROFILE(USER_ID) 

我猜这是因为会话不同(SessionImpl(48420828),SessionImpl(713565825))。

如何防止外部事务重复 sql 查询?

标签: javaspringhibernatetransactions

解决方案


如果我没有弄错,因为这是一个双向关系,您还需要将用户设置为Profile

for (User user: users) {
   Profile profile = new Profile()
   profile.setUser(user)
   user.setProfile(profile)
}

推荐阅读