簡體   English   中英

@Transactional 在 Spring 使用 CrudRepository 啟動時不起作用

[英]@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()方法到studentRepositorystudentService的每個調用都是單獨的會話/事務。

這幾乎就像你這樣做了:

...
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()方法中保留StudentSection對象的實例化和初始化,但代碼的 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM