【发布时间】:2019-04-26 14:24:38
【问题描述】:
我有一个案例,参与者可以注册 课程。
基本上我有以下实体配置(省略了getter和setter以及其他无用的属性):
@Entity
@Table(name = "course")
public class Course {
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "course")
private Set<Registration> registrations;
}
@Entity
@Table(name = "participant")
public class Participant {
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "participant")
private Set<Registration> registrations;
}
@Entity
@Table(name = "registration")
public class Registration {
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "course_id")
private Course course;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "participant_id")
private Participant participant;
@PreRemove
private void removeRegistrationFromHolderEntities() {
course.getRegistrations().remove(this);
participant.getRegistrations().remove(this);
}
}
然后我可以从我的视图模型中删除注册或课程(我还删除了不必要的东西):
@Command
public void deleteRegistration(Registration reg) {
registrationMgr.delete(reg);
}
@Command
public void deleteCourse(Course crs) {
courseMgr.delete(crs);
}
问题:
- 如果我删除一个注册,我需要
@PreRemove函数,这样我才能删除引用。没有这个,删除将被忽略(没有错误,只是被忽略) - 如果我删除一门课程,我必须删除
@PreRemove函数,否则我会得到ConcurrentModificationException(显然...)
我也无法从deleteRegistration 方法(而不是@PreRemove)中删除引用,因为参与者注册是延迟加载的(会引发failed to lazily initialize a collection of role: ..., could not initialize proxy - no Session 异常)。
这里最好的方法是什么?
我使用 Java 11 和 Spring Boot 1.0.4(以及 spring-boot-starter-data-jpa)。
编辑:
经理/存储库或以这种方式定义(registration 和 participant 相同)所以它应该是事务性的(我的主类上没有 @EnableTransactionManagement,但它不应该是必需的,因为我没有' t 使用存储库之外的事务):
@Transactional
@Component("courseMgr")
public class CourseManager {
@Autowired
CourseRepository courseRepository;
public void saveOrUpdate(Course course) {
courseRepository.save(course);
}
public void delete(Course course) {
courseRepository.delete(course);
}
}
public interface CourseRepository extends CrudRepository<Course, Long> {
...
}
EDIT2:
我想我找到了一个非常简单的解决方案:
我已经从实体中删除了 @PreRemove 方法,而不是在 deleteRegistration 方法中删除这样的引用(我尝试过但导致 failed to lazily initialize a collection of role 异常):
@Command
public void deleteRegistration(Registration reg) {
reg.getCourse().getRegistrations().remove(reg);
reg.getParticipant.getRegistrations().remove(reg);
registrationMgr.delete(reg);
}
我只是将父母设置为空,我不在乎,因为它会被删除...
@Command
public void deleteRegistration(Registration reg) {
reg.setCourse(null);
reg.setParticipant(null);
registrationMgr.delete(reg);
}
所以现在我也可以在不触发@PreRemove 中的ConcurrentModificationException 的情况下删除课程。
EDIT3:我的错,上面的解决方案没有删除注册(仍然没有错误,但没有任何反应)。我以这个结束了,它终于奏效了:
@Command
public void deleteRegistration(Registration reg) {
// remove reference from course, else delete does nothing
Course c = getRegistration().getCourse();
c.getRegistrations().remove(getRegistration());
courseMgr.saveOrUpdate(c);
// delete registration from the database
registrationMgr.delete(reg);
}
无需删除参与者的引用...
【问题讨论】:
-
为什么需要
@PreRemove来删除引用?您不应该在事务中管理实体图吗? -
@K.Nicholas 我的经理是事务性的(我编辑了问题以添加定义)。我对 JPA 没有太多经验,但这不是我第一次使用它,而且我在其他项目中从未遇到过此类问题,所以这里应该有一些特别的东西。
-
我的观点/问题是,您似乎只想删除实体的子级。我真的没有仔细阅读所有这些内容,但如果是这样的话,那么我认为
@PreRemove不是为此目的,所以你遇到错误并不让我感到惊讶。如果你想删除一个实体的子实体或关系,你应该从单个 repos 中删除所有这些。 -
谢谢,我同意
@PreRemove可能不用于此目的,但这是我发现删除集合中的引用的唯一方法。在deleteRegistration方法中执行此操作总是会引发failed to lazily initialize a collection of roles异常或什么也不做。我试过在方法上使用@EnableTransactionManagement+@Transactional,在删除之前调用.size(),甚至使用spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true,都没有工作,要么我收到错误,要么什么也没发生。 -
你说当你删除一个注册时,它会被忽略,没有错误,但我不这么认为。我认为它正在被删除,您仍然在注册中看到它并认为它没有被删除。或者它正在从数据库中删除,并且级联正在将其添加回来。不知道,我没查。请参阅下面的我的 cmets。
标签: java spring-boot jpa spring-data-jpa