将@Service层中的@Transactional方法结果传递给Controller层(Spring Boot)
将@Service层中的@Transactional方法结果传递给Controller层(Spring Boot)
我正在尝试从Service中延迟获取ManyToMany关系(Courses-Students)并将结果传递给Controller。当我在Service中时,由于@Transactional注释,不会抛出LazyInitializationException。然而,当我在Controller中时,由于Session已关闭,会抛出LazyInitializationException(在获取Course.students时)。我如何解决这个问题,而不急切地获取集合?
以下是我的代码:
Course Model
@Entity @Getter @Setter public class Course { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private String name; @ManyToMany @JoinTable(name = "COURSES_STUDENTS", joinColumns = {@JoinColumn(name = "COURSE_ID")}, inverseJoinColumns = {@JoinColumn(name = "STUDENT_ID")}) private Setstudents; public Course() { this.students = new HashSet<>(); } }
Student Model
@Entity @Getter @Setter public class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private String name; @ManyToMany(mappedBy = "students") private Setcourses; public Student() { this.courses = new HashSet<>(); } }
Course Repository
@Repository public interface CourseRepository extends JpaRepository{ }
Course Service
@Service public class CourseService { private final CourseRepository courseRepository; @Autowired public CourseService(CourseRepository courseRepository) { this.courseRepository = courseRepository; } @Transactional public ResponseEntity> findAll() { return this.courseRepository.findAll().isEmpty() ? ResponseEntity.noContent().build() : ResponseEntity.ok(this.courseRepository.findAll()); } }
Course Controller
@Controller @RequestMapping("/") public class CourseController { private final CourseService courseService; @Autowired public CourseController(CourseService courseService) { this.courseService = courseService; } @GetMapping public ResponseEntity> index() { return this.courseService.findAll(); } }
application.properties
spring.datasource.url=jdbc:h2:~/database;AUTO_SERVER=TRUE spring.datasource.username=sa spring.datasource.password= spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect spring.jpa.hibernate.ddl-auto=create-drop spring.h2.console.enabled=true spring.h2.console.path=/h2 spring.jpa.open-in-view=false spring.mvc.hiddenmethod.filter.enabled=true logging.level.org.springframework.web=DEBUG spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true
谢谢提前的帮助。
问题:在Spring Boot中,如何将@Service层的@Transactional方法的结果传递到@Controller层?
原因:通常情况下,使用@Transactional注解的方法会在事务中执行,这意味着数据库会话将保持打开状态,直到方法完成。然而,当涉及到懒加载(lazy loading)的集合属性时,数据库会话必须保持打开状态,直到集合属性被访问。这可能导致性能问题,因为数据库连接会被长时间占用。
解决方法:有两种解决方法可以避免这个问题:
1. 避免使用spring.jpa.open-in-view=true属性。这个属性会在每个请求结束后保持数据库会话打开状态,以便在视图渲染时延迟加载集合属性。然而,这种方法会带来性能问题,应尽量避免使用。
2. 在DAO层使用jpql查询来获取需要的懒加载集合属性,以便在Controller层使用时已经可用。通过在数据库/DAO层中使用join fetch来获取懒加载集合属性的数据。
总之,不要使用@Transactional来保持数据库会话打开以获取懒加载集合属性。而是在数据库/DAO层中使用join fetch来获取每个端点所需的数据。
代码示例:
@Repository public class MyEntityDAO { @PersistenceContext private EntityManager entityManager; public ListgetEntitiesWithLazyCollection() { String jpql = "SELECT e FROM MyEntity e JOIN FETCH e.lazyCollection"; TypedQuery query = entityManager.createQuery(jpql, MyEntity.class); return query.getResultList(); } }
参考链接:How to fetch FetchType.LAZY associations with JPA and Hibernate in a Spring Controller