我有一种情况,参与者可以注册 课程。
基本上,我具有以下实体配置(省略了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
异常)。
这里最好的方法是什么?
我在 Spring Boot 1.0.4 (和 spring-boot-starter-data-jpa )中使用了 Java 11 。
编辑:
管理者/存储库或以这种方式定义(与registration
和participant
相同),因此应该是事务性的(我的主要班级没有@EnableTransactionManagement
,但不应是必需的,因为我不使用存储库以外的事务):
@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
异常):< / p>
@Command
public void deleteRegistration(Registration reg) {
reg.getCourse().getRegistrations().remove(reg);
reg.getParticipant.getRegistrations().remove(reg);
registrationMgr.delete(reg);
}
我只是将父母设为null,我不在乎,因为它将被删除...
@Command
public void deleteRegistration(Registration reg) {
reg.setCourse(null);
reg.setParticipant(null);
registrationMgr.delete(reg);
}
因此,现在我也可以删除课程而无需触发ConcurrentModificationException
中的@PreRemove
。
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);
}
无需删除参与者的引用...
答案 0 :(得分:1)
您的存储库设置不正确。您需要Registration
的复合PK,并且需要了解双向映射实际上仅用于查询。此外,Course
和Participate
中的双向映射提出了挑战,因为默认情况下,通过ManyToOne
实体的Registration
关系是FetchType.EAGER
。使用所有的cascade
和fetch
批注,您正在向JPA寻求复杂的组合,似乎您还没有真正解决所有问题。从基础开始,请确保打印您的SQL语句,然后从那里继续进行,如果您想尝试从JPA中获得更多帮助。
@Entity
@Data
public class Course {
@Id
private Integer id;
private String name;
}
@Entity
@Data
public class Participant {
@Id
private Integer id;
private String name;
}
@Entity
@Data
public class Registration {
@EmbeddedId
private RegistrationPK id;
@ManyToOne
@MapsId("participant_id")
private Participant participant;
@ManyToOne
@MapsId("course_id")
private Course course;
}
@Embeddable
@Data
public class RegistrationPK implements Serializable {
private static final long serialVersionUID = 1L;
private Integer course_id;
private Integer participant_id;
}
是您的基本Entities
。 RegistrationRepository
需要一个附加查询。
public interface RegistrationRepository extends JpaRepository<Registration, RegistrationPK> {
Set<Registration> findByCourse(Course c);
}
并在示例中使用所有这些内容:
@Override
public void run(String... args) throws Exception {
create();
Course c = courseRepo.getOne(1);
Set<Registration> rs = read(c);
System.out.println(rs);
deleteCourse(c);
}
private void create() {
Course c1 = new Course();
c1.setId(1);
c1.setName("c1");
courseRepo.save(c1);
Participant p1 = new Participant();
p1.setId(1);
p1.setName("p1");
participantRepo.save(p1);
Registration r1 = new Registration();
r1.setId(new RegistrationPK());
r1.setCourse(c1);
r1.setParticipant(p1);
registrationRepo.save(r1);
}
private Set<Registration> read(Course c) {
return registrationRepo.findByCourse(c);
}
private void deleteCourse(Course c) {
registrationRepo.deleteAll( registrationRepo.findByCourse(c) );
courseRepo.delete(c);
}
答案 1 :(得分:0)
好的解决方案非常简单。
我确实需要从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) {
// 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
可以完成工作,但是那样一来,我现在还可以删除课程而无需触发ConcurrentModificationException
。