Spring数据删除功能不删除记录

Spring Data delete function not deleting records

我有以下简单的应用程序

Users实体

@Entity
public class Users implements Serializable {

   @Id
   @GeneratedValue
   private long id;

   private String name;

   @OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
   private Set<UserRoleUser> userRoleUser;

   // GETTERS AND SETTERS
}

UserRole实体

@Entity
public class UserRole implements Serializable {

   @Id
   @GeneratedValue
   private long id;

   private String roleName;

   @OneToMany(mappedBy = "userrole", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
   private Set<UserRoleUser> userRoleUser;

   // GETTERS AND SETTERS
}

UserRoleUser 多对多解析器 class

@Entity
public class UserRoleUser implements Serializable {

   @Id
   @GeneratedValue
   private long id;

   @ManyToOne
   @JoinColumn(name = "fk_userId")
   private Users user;

   @ManyToOne
   @JoinColumn(name = "fk_userroleId")
   private UserRole userrole;

   // GETTERS AND SETTERS
}

UserRoleUserRepository

@Repository
@Transactional
public interface UserRoleUserRepository extends JpaRepository<UserRoleUser, Long>, QueryDslPredicateExecutor<UserRoleUser>{

}

主要Applicationclass

@SpringBootApplication
@Configuration
public class Application {

   public static void main(String[] args) {
       ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

       UserRoleUserRepository userRoleUserRepository = context.getBean(UserRoleUserRepository.class);

       Iterable<UserRoleUser> findAll = userRoleUserRepository.findAll(QUserRoleUser.userRoleUser.id.gt(0));

       for (UserRoleUser userRoleUser : findAll) {
           userRoleUserRepository.delete(userRoleUser);
       }

   }

}

在 运行 main 应用程序上,UserRoleUser table 中的数据库记录未被删除。可能是什么问题?我正在使用 Spring DataQueryDsl.

我也尝试过将删除功能放在 Controller 上,但仍然不起作用。

@RestController
@RequestMapping("/api")
public class DeleteController {

    @Autowired
    UserRoleUserRepository userRoleUserRepository;

    @GetMapping("/delete")
    public String delete() {
        Iterable<UserRoleUser> findAll = userRoleUserRepository.findAll(QUserRoleUser.userRoleUser.id.gt(0));

        for (UserRoleUser userRoleUser : findAll) {
            userRoleUserRepository.delete(userRoleUser);
        }

        return new Date().toString();
    }
}

问题是实体仍然附加并且在它们分离之前不会被删除。如果您通过他们的 ID 而不是实体本身删除,它将删除他们。

我注意到的一件事是您一次删除一个用户,这可能会导致数据库性能受到影响,因为每次都会重新创建查询。最简单的做法是将所有 id 添加到一个集合中,然后删除该 id 集合。像这样:

Set<Integer> idList = new HashSet<>();
for (UserRoleUser userRoleUser : findAll) {
  idList.add(userRoleUser.getId());
}

if (!idList.isEmpty()) {
  userRoleUserRepository.delete(idList);
}

然后在您的存储库中添加删除方法

@Modifying
@Query("DELETE FROM UserRoleUser uru WHERE uru.id in ?1")
@Transactional
void delete(Set<Integer> id);

如果您需要使用CrudRepository提供的给定方法,请使用JpaRepository.deleteInBatch()。这解决了问题。

子对象 (UserRoleUser) 在 userRoleUserRepository.delete(userRoleUser) 调用时没有被删除的原因是每个 UserRoleUser 指向一个 Users,后者又持有一个@OneToMany 引用 Set<UserRoleUser> userRoleUser。 如 this Whosebug answer 中所述,您的 JPA 实现(例如 Hibernate)有效执行的是:

  1. 缓存记录了请求的儿童排除
  2. 然而,缓存不会验证 Set<UserRoleUser>
  3. 中的任何更改
  4. 由于父@OneToMany 字段尚未更新,因此未进行任何更改

一个解决方案是首先从 Set<UserRoleUser> 中删除子元素,然后继续 userRoleUserRepository.delete(userRoleUser)userRepository.save(user)

为了避免这种并发症,提供了两个答案:

  1. 通过调用 userRoleUserRepository.deleteById(userRoleUser.getId()) 按 Id 删除元素:在这种情况下,在删除之前不检查实体结构(以及父结构)。在 deleteAll 的模拟情况下,必须使用更复杂的东西,例如 userRoleUserRepository.deleteByIdIn(userRoleUserList.stream().map(UserRoleUser::getId).collect(Collectors.toList()))
  2. 将您的 CrudRepository 转换为 JpaRepository 并使用其 deleteInBatch(userRoleUserList) 方法。如 this article and this Whosebug answer 中所述,deleteInBatch 方法尝试一次删除所有记录,如果记录数量太大,可能会产生 Whosebug 错误。由于 repo.deleteAll() 一次删除一条记录,此错误可将此风险降至最低(除非调用本身位于 @Transactional 方法内)

根据 this Whosebug answer,在重复出现 deleteInBatch 时应格外小心,因为它:

  1. 不级联到其他实体
  2. 不更新持久化上下文,需要清除它(方法绕过缓存)

最后,据我所知,如果不先更新父对象,只需调用 userRoleUserRepository.delete(userRoleUser) 就无法做到这一点。对此的任何更新(无论是通过注释、配置或任何其他方式允许此类行为)都是对答案的欢迎补充。