簡體   English   中英

Hibernate 的延遲初始化異常

[英]lazyinitializationexception with Hibernate

我在項目中使用數據庫中的實體的延遲初始化。 作為 JPA 實現,我使用 Hibernate。 但是當我需要在自定義 JSP 上獲取實體集合時,我得到了一個延遲初始化異常,因為 Hibernate session 已經關閉。 請告訴我是否可以在不使用特殊請求的情況下阻止此異常,如https://thorben-janssen.com/hibernate-tips-initialize-lazy-relationships-within-query/中所述。 這是我的一些代碼:

@Entity
public class Statistic extends EntityAbstract {

    private static final long serialVersionUID = -6372957177860305322L;
    @Temporal(TemporalType.TIMESTAMP)
    private Date date;
    private boolean correct;

    @ManyToOne(fetch = FetchType.LAZY, cascade = {MERGE, PERSIST, REFRESH, DETACH})
    @JoinColumn(name = "questionId", nullable = false)
    private Question question;

    @ManyToOne(fetch = FetchType.LAZY, cascade = {MERGE, PERSIST, REFRESH, DETACH})
    @JoinColumn(name = "userId", nullable = false)
    private User user;

    public Statistic() {
    }

    public Statistic(Date date, boolean correct, Question question, User user) {
        this.date = date;
        this.correct = correct;
        this.question = question;
        this.user = user;
    }

    getter... setter...
}

@Entity
public class Question extends EntityAbstract {

    private static final long serialVersionUID = -2751224412459986012L;
    private String description;

    @ManyToOne(fetch = FetchType.LAZY, cascade = {MERGE, PERSIST, REFRESH, DETACH})
    @JoinColumn(name = "testId", nullable = false)
    private Test test;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "question")
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Set<Answer> answers;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "question")
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Set<Literature> literature;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "question")
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Set<Statistic> statistics;

    public Question() {
    }

    public Question(String description, Test test) {
        this.description = description;
        this.test = test;
    }

    getter... setter...
}

@Entity
public class Literature extends EntityAbstract {

    private static final long serialVersionUID = 218407080623072886L;
    private String description;

    @ManyToOne(fetch = FetchType.LAZY, cascade = {MERGE, PERSIST, REFRESH, DETACH})
    @JoinColumn(name = "questionId", nullable = false)
    private Question question;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "literature")
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Set<Link> links;

    public Literature() {
    }

    public Literature(String description, Question question) {
        this.description = description;
        this.question = question;
    }

    getter... setter...
}

@Entity
public class Link extends EntityAbstract {

    private static final long serialVersionUID = 6494218299043499655L;
    private String link;

    @ManyToOne(fetch = FetchType.LAZY, cascade = {MERGE, PERSIST, REFRESH, DETACH})
    @JoinColumn(name = "literatureId", nullable = false)
    private Literature literature;

    public Link() {
    }

    public Link(String link, Literature literature) {
        this.link = link;
        this.literature = literature;
    }

    getter... setter...
}

And I need to get a collection of questions from statistics and further along the chain. I use this service:

@Repository
@Transactional
public interface CrudRepository<T extends EntityAbstract> {

    SessionFactory getBeanToBeAutowired();

    // create
    @SuppressWarnings("unchecked")
    default T add(T entity){
        return (T) getBeanToBeAutowired().getCurrentSession().merge(entity);
    }

    // read
    @SuppressWarnings("unchecked")
    default T getById(Class<T> entityClass, long id){
        return (T)getBeanToBeAutowired().getCurrentSession().find(entityClass, id);
    }

    // update
    @SuppressWarnings("unchecked")
    default T update(T entity){
        return (T) getBeanToBeAutowired().getCurrentSession().merge(entity);
    }

    // delete
    default void delete(T entity){
        getBeanToBeAutowired().getCurrentSession().remove(entity);
    }

    @SuppressWarnings("unchecked")
    default List<T> getAll(Class<T> t){
        return (List<T>)getBeanToBeAutowired()
                .getCurrentSession()
                .createQuery("FROM " + t.getSimpleName())
                .list();
    }
}

public interface StatisticRepository extends CrudRepository<Statistic> {
}

public interface StatisticService extends StatisticRepository {

    Statistic getById(long id);
    List<Statistic> getAll();
}

@Service("statisticService")
public class StatisticServiceImpl implements StatisticService {

    private SessionFactory sessionFactory;

    @Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    public SessionFactory getBeanToBeAutowired() {
        return sessionFactory;
    }

    @Override
    public Statistic getById(long id) {
        return getById(Statistic.class, id);
    }

    @Override
    public List<Statistic> getAll() {
        return getAll(Statistic.class);
    }
}

我解決了我的問題如下(使用這篇文章https://thorben-janssen.com/hibernate-tip-entitygraph-multiple- ):

@NamedEntityGraph(
        name = "graph.statistic",
        attributeNodes = @NamedAttributeNode(value = "question", subgraph = "questionGraph"),
        subgraphs = {
                @NamedSubgraph(name = "questionGraph",
                        attributeNodes = @NamedAttributeNode(value = "literature", subgraph = "literatureGraph")),
                @NamedSubgraph(name = "literatureGraph",
                        attributeNodes = @NamedAttributeNode(value = "links"))}
)
@Entity
public class Statistic extends EntityAbstract {

    private static final long serialVersionUID = -6372957177860305322L;
    @Temporal(TemporalType.TIMESTAMP)
    private Date date;
    private boolean correct;

    @ManyToOne(fetch = FetchType.LAZY, cascade = {MERGE, PERSIST, REFRESH, DETACH})
    @JoinColumn(name = "questionId", nullable = false)
    private Question question;

    @ManyToOne(fetch = FetchType.LAZY, cascade = {MERGE, PERSIST, REFRESH, DETACH})
    @JoinColumn(name = "userId", nullable = false)
    private User user;

    public Statistic() {
    }

    public Statistic(Date date, boolean correct, Question question, User user) {
        this.date = date;
        this.correct = correct;
        this.question = question;
        this.user = user;
    }

    getters... and setters..... 
}

@Service("statisticService")
public class StatisticServiceImpl implements StatisticService {

 ...

    @Override
    @Transactional
    public List<Statistic> getUserStatisticByUserIdAndDate(long id, Date startDate, Date endDate) {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        RootGraph<?> graph = session.getEntityGraph("graph.statistic");
        List<Statistic> statistics = session
                .createQuery("FROM Statistic WHERE user.id = :id AND date between :startDate AND :endDate", Statistic.class)
                .setParameter("id", id)
                .setParameter("startDate", startDate)
                .setParameter("endDate", endDate)
                .setHint("javax.persistence.fetchgraph", graph)
                .getResultList();
        session.getTransaction().commit();
        session.close();
        return statistics;
}

實體圖存在一些問題。 IMO 最緊迫的問題是,您使用相同的 java model 公開所有 state,盡管並非所有內容都可用。 您始終必須跟蹤 object 的來源,這也是一種痛苦。

我可以建議您查看Blaze-Persistence Entity-Views ,這是一個更合適的解決方案: https://blazebit.com/blog/2016/getting-started-with-blaze-persistence-entity-views.html

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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