将@Service层中的@Transactional方法结果传递给Controller层(Spring Boot)

18 浏览
0 Comments

将@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 Set students;
    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 Set courses;
    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

谢谢提前的帮助。

0
0 Comments

问题:在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 List getEntitiesWithLazyCollection() {
            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

0