繁体   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