简体   繁体   中英

Pass @Transactional method result from Service to Controller Layer Spring Boot

I'm trying to lazily fetch a ManyToMany relationship (Courses - Students) from the Service and pass the result to the Controller. While i'm in the Service, no LazyInitializationException is thrown, thanks to the @Transactional annotation. However, while i'm in the Controller the LazyInitializationException is thrown (while getting Course.students ), because the Session was closed. How can i resolve this issue, without eagerly fetch the Collection?

That's my code:

Couse 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<Student> 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<Course> courses;

    public Student() {
        this.courses = new HashSet<>();
    }
}

Course Repository

@Repository
public interface CourseRepository extends JpaRepository<Course, Long> {
}

Course Service

@Service
public class CourseService {

    private final CourseRepository courseRepository;

    @Autowired
    public CourseService(CourseRepository courseRepository) {
        this.courseRepository = courseRepository;
    }

    @Transactional
    public ResponseEntity<List<Course>> 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<List<Course>> 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

Thanks in advance.

So there are 2 approaches:

  1. What is this spring.jpa.open-in-view=true property in Spring Boot?

This is bad for performance and must be avoided at all costs.

  1. use jpql queries to join fetch lazy collections needed in DAO layer so they are available in the controller when you need them to be.

All in all, do not use transactional to keep the db session open to fetch lazy collections. Just join fetch lazy collections in db / dao layer to have the data needed for each endpoint available.

If you want have a look here for how to use join fetch How to fetch FetchType.LAZY associations with JPA and Hibernate in a Spring Controller

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM