[英]@Transactional not working in Spring Boot with CrudRepository
我試圖在我的實體之間實現雙向關系。
學生
@Table(name = "students")
@Entity
public class Student {
@Id
// @GeneratedValue(strategy = GenerationType.AUTO)
private long album;
@NotNull
private String name;
@NotNull
private String surname;
@OneToMany(mappedBy = "student", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
private List<StudentSection> studentSections;
@Transactional(propagation=Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class)
public void addSection(Section section){
if(this.studentSections == null){
this.studentSections = new ArrayList<>();
}
StudentSection studentSectionToAdd = new StudentSection();
studentSectionToAdd.setStudent(this);
studentSectionToAdd.setSection(section);
this.studentSections.add(studentSectionToAdd); //here
section.addStudentSection(studentSectionToAdd);
}
}
多對多關系中的連接實體
@Table(name = "student_section")
@Entity
public class StudentSection {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Integer grade;
private Date date;
@NotNull
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
@JoinColumn(name = "student_id")
private Student student;
@NotNull
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
@JoinColumn(name = "section_id")
private Section section;
}
和部分
@Table(name = "sections")
@Entity
public class Section {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
private String name;
@NotNull
private Integer sizeOfSection;
@NotNull
private Boolean isActive;
@OneToMany(mappedBy = "section", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
private List<StudentSection> studentSections;
void addStudentSection(StudentSection studentSection){
if(this.studentSections == null){
this.studentSections = new ArrayList<>();
}
this.studentSections.add(studentSection);
}
}
我遇到了 Student.addSection() 方法的問題。 嘗試執行它時,我在this.studentSections.add(studentSectionToAdd);
上遇到錯誤行,說failed to lazily initialize a collection of role: Student.studentSections, could not initialize proxy - no Session
我讀過它並發現解決此問題的最佳方法是將 @Transactional 注釋添加到方法中,但是它沒有改變任何東西,我無法讓它工作。 我還嘗試將 Student.addSection() 方法移動到 StudentServiceImpl
@Service
@Primary
public class StudentServiceImpl implements StudentService {
protected StudentRepository studentRepository;
@Autowired
public StudentServiceImpl(StudentRepository studentRepository) {
this.studentRepository = studentRepository;
}
@Override
@Transactional(propagation = Propagation.REQUIRED, readOnly = true, noRollbackFor = Exception.class)
public void addSection(Student student, Section section) {
if (student.getStudentSections() == null) {
student.setStudentSections(new ArrayList<>());
}
StudentSection studentSectionToAdd = new StudentSection();
studentSectionToAdd.setStudent(student);
studentSectionToAdd.setSection(section);
student.getStudentSections().add(studentSectionToAdd);
//section.addStudentSection(studentSectionToAdd);
}
}
但我仍然得到錯誤。
我還使用 CrudRepository 從數據庫中檢索實體。
@Repository
public interface StudentRepository extends CrudRepository<Student, Long> {
Student findByName(String name);
}
這是我調用該方法的地方
@Component
public class DatabaseLoader implements CommandLineRunner {
private final StudentRepository studentRepository;
private final SectionRepository sectionRepository;
private final StudentSectionRepository studentSectionRepository;
private final StudentService studentService;
@Autowired
public DatabaseLoader(StudentRepository studentRepository, SectionRepository sectionRepository, StudentSectionRepository studentSectionRepository,
StudentService studentService) {
this.studentRepository = studentRepository;
this.sectionRepository = sectionRepository;
this.studentSectionRepository = studentSectionRepository;
this.studentService = studentService;
}
@Override
public void run(String... strings) throws Exception {
//Testing entities
Student student = new Student();
student.setAlbum(1L);
student.setName("student");
student.setSurname("test");
this.studentRepository.save(student);
Section section = new Section();
section.setName("section");
section.setSizeOfSection(10);
section.setIsActive(true);
this.sectionRepository.save(section);
//end
//Adding Student to a Section test
Student student1 = studentRepository.findByName("student");
//student1.setStudentSections(this.studentSectionRepository.findAllByStudent(student1));
Section section1 = sectionRepository.findByName("section");
//section1.setStudentSections(this.studentSectionRepository.findAllByStudent(student1));
studentService.addSection(student1, section1);
this.studentRepository.save(student1);
//end test
}
}
此外,當我從此處的數據庫中檢索 StudentSection 列表並在添加新關系之前將它們設置為兩個對象時,它工作正常,但這並不是我真正想要的解決方案。
問題是從run()
方法到studentRepository
和studentService
的每個調用都是單獨的會話/事務。
這幾乎就像你這樣做了:
...
beginTransaction();
this.studentRepository.save(student);
commit();
...
beginTransaction();
this.sectionRepository.save(section);
commit();
beginTransaction();
Student student1 = studentRepository.findByName("student");
commit();
beginTransaction();
Section section1 = sectionRepository.findByName("section");
commit();
// This does it's own transaction because of @Transactional
studentService.addSection(student1, section1);
beginTransaction();
this.studentRepository.save(student1);
commit();
由於這里的transaction = session,說明student1
是分離的,懶加載的studentSections
集合不能在session之外按需加載,因此代碼失敗。
插入一個新學生和一個新部分並關聯它們實際上應該是一個事務,所以如果后面的步驟失敗,它就會全部回滾。
這基本上意味着您希望整個run()
方法成為一個事務,因此在您的情況下,應該是run()
方法@Transactional
,而不是addSection()
方法。
通常,在3 層方法中,您會將事務邊界置於服務層:
演示層。 這是@Controller
類,或者是簡單命令行程序的run()
方法。
邏輯層。 這是@Service
類。 這是您放置@Transactional
的地方,因此每個服務調用都是一個原子事務,即它要么成功要么失敗,就數據庫更新而言,沒有一半更新。
數據層。 這是@Repository
和@Entity
類。
因此,您應該在run()
方法中保留Student
和Section
對象的實例化和初始化,但代碼的 rest,包括。 save()
,應該移動到@Service
class 中的單個方法。
關於這個@Transactional(propagation=Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class) public void addSection(Section section){
@Transactional
僅適用於 spring 管理的 bean,並且實體不受 spring 管理。
您收到此異常是因為您嘗試在 session 之外加載惰性關系(因為您的實體實際上處於分離狀態)。
重新附加 --> entityManager.merge(student);
但最好的辦法是在查詢時加載關系。 例如使用EntityGraph
-->
@EntityGraph(attributePaths="studentSections") Student findByName(String name);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.