我有以下简单的应用程序
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 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();
}
}
答案 0 :(得分: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);
答案 1 :(得分:2)
如果您需要使用CrudRepository
提供的给定方法,请使用JpaRepository.deleteInBatch()
。这解决了这个问题。
答案 2 :(得分:0)
在进行UserRoleUser
调用时未删除子对象(userRoleUserRepository.delete(userRoleUser)
)的原因是,每个UserRoleUser
指向一个Users
,而该Set<UserRoleUser> userRoleUser
则持有一个@OneToMany参考Set<UserRoleUser>
。
如this StackOverflow answer中所述,您的JPA实现(例如Hibernate)有效执行的操作是:
Set<UserRoleUser>
中的任何更改解决方案将首先从userRoleUserRepository.delete(userRoleUser)
中删除子元素,然后继续进行userRepository.save(user)
或userRoleUserRepository.deleteById(userRoleUser.getId())
为了避免这种复杂性,提供了两个答案:
userRoleUserRepository.deleteByIdIn(userRoleUserList.stream().map(UserRoleUser::getId).collect(Collectors.toList()))
来按ID删除元素:在这种情况下,在删除之前不检查实体结构(并因此检查父对象)。在delete的模拟情况下,必须使用所有更复杂的东西,例如deleteInBatch(userRoleUserList)
repo.deleteAll()
方法。如this article和this StackOverflow answer中所述,deleteInBatch方法尝试一次删除所有记录,在记录数量太大的情况下可能会产生StackOverflow错误。由于deleteInBatch
每次都会删除一条记录,因此将这种风险降到最低(除非调用本身在@Transactional方法内)根据this StackOverflow answer,在重复使用userRoleUserRepository.delete(userRoleUser)
时应格外小心:
最后,据我所知,仅通过调用Route::delete('/whatever/{child}', [ChildController::class, 'deleteChild'])
->name('preliminary-children.deleteChild');
而不先更新父对象是不可能做到的。关于此问题的任何更新(无论是通过批注,配置还是通过任何其他方式允许这种行为)都将是对答案的欢迎补充。