简体   繁体   English

Hibernate 的延迟初始化异常

[英]lazyinitializationexception with Hibernate

I use lazy initialization of entities from the database in the project.我在项目中使用数据库中的实体的延迟初始化。 As a JPA implementation, I use Hibernate.作为 JPA 实现,我使用 Hibernate。 But when I need to get a collection of entities on a custom JSP, I get a lazyinitializationexception, because Hibernate session is already closed.但是当我需要在自定义 JSP 上获取实体集合时,我得到了一个延迟初始化异常,因为 Hibernate session 已经关闭。 Please tell me whether it is possible to prevent this exception without resorting to using a special request, as described in https://thorben-janssen.com/hibernate-tips-initialize-lazy-relationships-within-query/ .请告诉我是否可以在不使用特殊请求的情况下阻止此异常,如https://thorben-janssen.com/hibernate-tips-initialize-lazy-relationships-within-query/中所述。 Here is some of my code:这是我的一些代码:

@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);
    }
}

I solved my question as follows (using this article herehttps://thorben-janssen.com/hibernate-tip-entitygraph-multiple- ):我解决了我的问题如下(使用这篇文章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;
}

There are a few problems with entity graphs.实体图存在一些问题。 IMO the most pressing issue is, that you use the same java model that exposes all state although not everything is available. IMO 最紧迫的问题是,您使用相同的 java model 公开所有 state,尽管并非所有内容都可用。 You always have to keep track about where an object came from which is a pain as well.您始终必须跟踪 object 的来源,这也是一种痛苦。

I can recommend you take a look atBlaze-Persistence Entity-Views which is a more appropriate solution: https://blazebit.com/blog/2016/getting-started-with-blaze-persistence-entity-views.html我可以建议您查看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