【问题标题】:Spring Data delete function not deleting recordsSpring Data删除功能不删除记录
【发布时间】:2018-07-02 02:23:21
【问题描述】:

我有以下简单的应用程序

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多对多解析器类

@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>{

}

Application

@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 表中的数据库记录不会被删除。可能是什么问题?我正在使用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();
    }
}

【问题讨论】:

  • 如果你的代码正常运行到main方法结束,那么可能是因为你把@Transactional放在了UserRoleUserRepository接口上。看到这个帖子:stackoverflow.com/questions/5551541/…El Guapo的答案。
  • 其实我只是在尝试放@Transactional注解的时候,因为不管有没有它都不会删除记录。
  • 可能是因为为了有效地删除您应该首先从父类中的 Set userRoleUser 中删除不需要的元素,然后继续删除?

标签: java spring spring-boot spring-data-jpa querydsl


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    问题是实体仍处于附加状态,并且在它们分离之前不会被删除。如果您按他们的 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提供的给定方法。但是感谢您的回答,它引发了其他事情。在下面发布答案。
    • 这个答案是正确答案,不是被接受的答案。所以对接受的投了反对票,对这个投了赞成票
    • 按 ID 删除也解决了我的问题。谢谢!
    【解决方案3】:

    子对象 (UserRoleUser) 在userRoleUserRepository.delete(userRoleUser) 调用时没有被删除的原因是每个UserRoleUser 指向一个Users,而Users 又持有一个@OneToMany 引用Set&lt;UserRoleUser&gt; userRoleUser。 如this StackOverflow answer 中所述,您的 JPA 实现(例如 Hibernate)有效地做的是:

    1. 缓存会记录请求的子排除项
    2. 但是缓存不会验证Set&lt;UserRoleUser&gt; 中的任何更改
    3. 由于父 @OneToMany 字段尚未更新,因此未进行任何更改

    一种解决方案是首先从Set&lt;UserRoleUser&gt; 中删除子元素,然后转到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 articlethis StackOverflow answer 中解释的那样,deleteInBatch 方法尝试一次删除所有记录,如果记录数量太大,可能会生成 StackOverflow 错误。由于repo.deleteAll() 一次删除一条记录,此错误将风险降至最低(除非调用本身在@Transactional 方法中)

    根据this StackOverflow answer,在重复到deleteInBatch 时应格外小心:

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

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

    【讨论】:

    • deleteById 在 JpaRepository 的情况下对我不起作用。在详细条目中设置 Cascade.All 会删除主条目以及所有详细条目。
    • 必须选择master,更新master的列表,一旦从列表中删除详细条目,保存master,删除详细对象
    猜你喜欢
    • 1970-01-01
    • 2016-12-07
    • 1970-01-01
    • 2012-12-20
    • 2017-06-20
    • 2012-12-23
    • 2014-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多