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>{
}
主要Application
class
@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 Data
和 QueryDsl
.
我也尝试过将删除功能放在 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)有效执行的是:
- 缓存记录了请求的儿童排除
- 然而,缓存不会验证
Set<UserRoleUser>
中的任何更改
- 由于父@OneToMany 字段尚未更新,因此未进行任何更改
一个解决方案是首先从 Set<UserRoleUser>
中删除子元素,然后继续 userRoleUserRepository.delete(userRoleUser)
或 userRepository.save(user)
为了避免这种并发症,提供了两个答案:
- 通过调用
userRoleUserRepository.deleteById(userRoleUser.getId())
按 Id 删除元素:在这种情况下,在删除之前不检查实体结构(以及父结构)。在 deleteAll 的模拟情况下,必须使用更复杂的东西,例如 userRoleUserRepository.deleteByIdIn(userRoleUserList.stream().map(UserRoleUser::getId).collect(Collectors.toList()))
- 将您的 CrudRepository 转换为 JpaRepository 并使用其
deleteInBatch(userRoleUserList)
方法。如 this article and this Whosebug answer 中所述,deleteInBatch 方法尝试一次删除所有记录,如果记录数量太大,可能会产生 Whosebug 错误。由于 repo.deleteAll()
一次删除一条记录,此错误可将此风险降至最低(除非调用本身位于 @Transactional 方法内)
根据 this Whosebug answer,在重复出现 deleteInBatch
时应格外小心,因为它:
- 不级联到其他实体
- 不更新持久化上下文,需要清除它(方法绕过缓存)
最后,据我所知,如果不先更新父对象,只需调用 userRoleUserRepository.delete(userRoleUser)
就无法做到这一点。对此的任何更新(无论是通过注释、配置或任何其他方式允许此类行为)都是对答案的欢迎补充。
我有以下简单的应用程序
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>{
}
主要Application
class
@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 Data
和 QueryDsl
.
我也尝试过将删除功能放在 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)有效执行的是:
- 缓存记录了请求的儿童排除
- 然而,缓存不会验证
Set<UserRoleUser>
中的任何更改
- 由于父@OneToMany 字段尚未更新,因此未进行任何更改
一个解决方案是首先从 Set<UserRoleUser>
中删除子元素,然后继续 userRoleUserRepository.delete(userRoleUser)
或 userRepository.save(user)
为了避免这种并发症,提供了两个答案:
- 通过调用
userRoleUserRepository.deleteById(userRoleUser.getId())
按 Id 删除元素:在这种情况下,在删除之前不检查实体结构(以及父结构)。在 deleteAll 的模拟情况下,必须使用更复杂的东西,例如userRoleUserRepository.deleteByIdIn(userRoleUserList.stream().map(UserRoleUser::getId).collect(Collectors.toList()))
- 将您的 CrudRepository 转换为 JpaRepository 并使用其
deleteInBatch(userRoleUserList)
方法。如 this article and this Whosebug answer 中所述,deleteInBatch 方法尝试一次删除所有记录,如果记录数量太大,可能会产生 Whosebug 错误。由于repo.deleteAll()
一次删除一条记录,此错误可将此风险降至最低(除非调用本身位于 @Transactional 方法内)
根据 this Whosebug answer,在重复出现 deleteInBatch
时应格外小心,因为它:
- 不级联到其他实体
- 不更新持久化上下文,需要清除它(方法绕过缓存)
最后,据我所知,如果不先更新父对象,只需调用 userRoleUserRepository.delete(userRoleUser)
就无法做到这一点。对此的任何更新(无论是通过注释、配置或任何其他方式允许此类行为)都是对答案的欢迎补充。