在@ManyToOne上使用JPA延迟加载

时间:2017-06-13 09:20:23

标签: spring-boot spring-data-jpa lazy-loading

我有一个Spring Boot项目,我无法进行延迟加载工作。我有2个实体:QuestionAnswer。一个问题可以有很多答案。 我想要的是,当我试图得到答案时,只得到答案而没有问题。而且,如果我想要两者,也有这种可能性。

我做了什么,是我在application.yml中添加的:spring.jpa.open-in-view:true。 Answer实体就像:

@Entity
@Table(name = "mst_ans_answer", schema = "lquest_sc")
public class Answer implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "lquest_sc.mst_ans_answer_ans_lqs_id_seq")
    @SequenceGenerator( name = "lquest_sc.mst_ans_answer_ans_lqs_id_seq", sequenceName = "lquest_sc.mst_ans_answer_ans_lqs_id_seq")
    @Column(name = "ans_lqs_id")
    private int id;

    @Column(name = "qst_lqs_id")
    private int questionId;

    @Column(name = "ans_text")
    private String text;


    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "qst_lqs_id", insertable=false, updatable=false)
    @JsonIgnore
    private Question question;

    //getters and setters
}

Question实体是:

@Entity
@Table(name = "mst_qst_question", schema = "lquest_sc")
public class Question implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "lquest_sc.mst_qst_question_qst_lqs_id_seq")
@SequenceGenerator(name = "lquest_sc.mst_qst_question_qst_lqs_id_seq", sequenceName = "lquest_sc.mst_qst_question_qst_lqs_id_seq")
@Column(name = "qst_lqs_id")
private int id;

@Column(name = "qst_title")
private String title;

@OneToMany(fetch = FetchType.LAZY, mappedBy = "question")
@OrderBy("order asc")
private Set<Answer> answers = new HashSet<Answer>();


//getters and setters here

控制器中的呼叫是:

@RequestMapping(value = "/questionId/{id}", method = RequestMethod.GET)
public List<Answer> listAll( @PathVariable("id") int id ){
    List<Answer> answers = answerRepository.findByEnabledAndQuestionIdOrderByOrderAsc(1,id);
    return answers;
}

并且存储库是

public interface AnswerRepository extends JpaRepository<Answer, Long> {
    List<Answer> findByEnabledAndQuestionIdOrderByOrderAsc(int enabled,int questionId);
}

问题是在控制器中,当我尝试评估时 answers.get(0).getQuestion(),我收到了问题的实体,其中的属性填充了null值和错误Method threw 'org.hibernate.LazyInitializationException' exception. Cannot evaluate Question_$$_jvst5b6_1.toString()。我做错了什么?

1 个答案:

答案 0 :(得分:1)

我不知道为什么spring.jpa.open-in-view = true在您的情况下不起作用。当您评估OpenEntityManagerInViewInterceptor时,可能Question没有被触发或者线程已经离开。或者您有一个不支持它的旧版本。

延迟加载仅适用于事务内部。解决方案可能是 -  正如@Pradeep已经给你提示 - 在业务逻辑类中使用@Tranactional

即使您将@Transactional放入存储库中也无效,因为您必须将注释放在您尝试评估answers.get(0).getQuestion()的方法的顶部。

此外,我建议您不要直接从控制器调用存储库,而是使用服务层,在那里放置业务逻辑。

示例实现

这只是一个示例实现,向您展示如何构建应用程序以及重要的关键字。另请注意,您可以使用@Inject@Autowired。实现逻辑后,只需将服务注入控制器并在那里使用即可。

AnswerService.java

public interface AnswerService {
   List<Answer> findByEnabledAndQuestionIdOrderByOrderAsc(int enabled, Long id);
}

AnswerServiceImpl.java

@Service
public class AnswerServiceImpl implements AnswerService {

    private AnswerRepository answerRepository;

    @Inject
    public AnswerServiceImpl(AnswerRepository answerRepository) {
        this.answerRepository = answerRepository;
    }

    @Transactional
    @Override
    public List<Answer> findByEnabledAndQuestionIdOrderByOrderAsc(int enabled, Long id) {
        List<Answer> answerList = findByEnabledAndQuestionIdOrderByOrderAsc(int enabled,int questionId);

        // do your lazy loading here
        // because you are still in the same transactional context

        // return the list
        return answerList;
    }
}