当从 hibernate 4.3.11 迁移到 5.2.10 并使用 EntityManager 而不是 SessionFactory 时,hibernate session.get 不加载持久对象

hibernate session.get does not load persistent object when migrate from hibernate 4.3.11 to 5.2.10 and use EntityManager insteadof SessionFactory

多年来我在一个项目中使用了 hibernate 4.3.11.Final 和 spring 4.3.1.RELEASE 到操纵和持久化对象。最近我迁移到休眠 5.2.10.Final 并遇到描述它的问题。我有两个域 class,如下所示:

public class Post extends BaseEntity<Long> {
    private String title;
    private Set<PostComment> comments = new HashSet<>(0);

    // getters and setters removed for brevity
}

public class PostComment extends BaseEntity<Long> {
    private Post post;
    private String descripton;

    // getters and setters removed for brevity
}

public abstract class BaseEntity<T> implements Serializable {
    private T id;
}

这些是休眠映射文件:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="" table="tbl_post">
        <id name="id" type="long" >
            <column name="ID"  />
            <generator class="sequence" >
                <param name="sequence">RGH.SEQ_POST</param>
            </generator>
        </id>

        <property name="title" column="title" type="string" not-null="true" />

        <set name="comments" inverse="true" cascade="save-update,merge">
            <key>
                <column name="post_id" not-null="true" />
            </key>
            <one-to-many class="com.rgh.PostComment" />
        </set>
    </class>
</hibernate-mapping>

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="" table="tbl_post_comment">
        <id name="id" type="long" >
            <column name="ID"  />
            <generator class="sequence" >
                <param name="sequence">RGH.SEQ_POST_COMMENT</param>
            </generator>
        </id>

        <property name="descripton" column="descripton" type="string" not-null="true" />

        <many-to-one name="post" column="post_id" entity-name="com.rgh.Post" not-null="true" />
    </class>
</hibernate-mapping>

PostService class 中存在两种方法,一种是主题保留 Post 实体,它是 PostComment 的主题,另一种是保存 Post 实体并记录其 PostCommment 的计数。

@Service
public class PostService {
    @Autowired
    private PostRepository repo;

    @Autowired
    private PostCommentRepository commentRepo;

    @Transactional
    public Long save() {
        Post post = new Post();
        post.setTitle("sample post");
        repo.save(post);

        Set<PostComment> postComments = new HashSet<>();
        for (int i = 0 ; i < 3 ; i++) {
            PostComment postComment = new PostComment();
            postComment.setPost(post);
            postComment.setDescription("description " + i);
            commentRepo.save(postComment);
        }

        return post.getId();
    }

    @Transactional
    public int saveAndGetDetailsCount(Post entity) {
        entity.setTitle(entity.getTitle() + " updated!");
        repo.save(entity);

        Post post = repo.loadById(entity.getId());
        System.out.println(post.Comments().size());
        return post.Comments().size();
    }
}

PostRepositoryPostCommentRepository 都是从 GenericRepository 扩展而来的,您可以在下面看到它:

@Repository
public class PostRepository extends GenericRepository<Post, Long> {
    @Override
    protected Class<Post> getDomainClass() {
        return Post.class;
    }
}

@Repository
public class PostCommentRepository extends GenericRepository<PostComment, Long> {
    @Override
    protected Class<PostComment> getDomainClass() {
        return PostComment.class;
    }
}

@Repository
public abstract class GenericRepository<T extends BaseEntity,PK extends Serializable> {

    protected Class<T> domainClass = getDomainClass();

    @Autowired
    private SessionFactory sessionFactory;

    public Session getSession() {
        try {
            return sessionFactory.getCurrentSession();
        } catch (Exception e) {
        }
        return sessionFactory.openSession();
    }

    public PK save(T Entity) {
        Session session = getSession();
        if(entity.getId() == null) {
            session.save(entity);
        }
        else {
            entity = (T)session.merge(entity);
            session.update(entity);
        }
        return entity.getId();
    }

    public T loadByEntityId(PK entityId) {
        Session session = getSession();
        return  (T) session.get(domainClass.getName(), entityId);
    }
}

当我调用 save 方法然后调用 saveAndGetDetailsCount 它更新实体然后执行 select... 查询以加载 Post 实体然后记录时一切正常3 和 post 评论。

但是在我迁移到休眠模式后 5.2.10.Final 并应用了这些更改:

@Entity
@Table(schema = "RGH", name = "TBL_POST")
@SequenceGenerator(name = "sequence_db", sequenceName = "RGH.SEQ_POST", allocationSize = 1)
public class Post extends BaseEntity<Long> {

    @Column
    private String title;

    @OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
    @Cascade(value = { CascadeType.SAVE_UPDATE, CascadeType.MERGE })
    private Set<PostComment> comments = new HashSet<>(0);

    // getters and setters removed for brevity
}

@Entity
@Table(schema = "RGH", name = "TBL_POST_COMMENT")
@SequenceGenerator(name = "sequence_db", sequenceName = "RGH.SEQ_POST_COMMENT", allocationSize = 1)
public class PostComment extends BaseEntity<Long> {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "POST_ID", nullable = false)
    private Post post;

    @Column
    private String descripton;

    // getters and setters removed for brevity
}

public abstract class BaseEntity<T> implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence_db")
    private T id;
}

然后 GenericRepository 我使用 EntityManager 得到 Session class 如下所示:

@Repository
public abstract class GenericRepository<T extends BaseEntity,PK extends Serializable> {

    protected Class<T> domainClass = getDomainClass();

    /* changed */
    @PersistenceContext
    private EntityManager em;

    public Session getSession() {
        /* changed */
        return  em.unwrap(Session.class);
    }

    public PK save(T Entity) {
        Session session = getSession();
        if(entity.getId() == null) {
            session.save(entity);
        }
        else {
            entity = (T)session.merge(entity);
            session.update(entity);
        }
        return entity.getId();
    }

    public T loadByEntityId(PK entityId) {
        Session session = getSession();
        return  (T) session.get(domainClass.getName(), entityId);
    }
}

现在,问题是,当我 运行 以前的方法调用时,在 saveAndGetDetailsCount 中,hibernate 不执行 select... 查询并记录 0 因为评论很重要。

我想知道为什么会这样。

更新

也许你会问为什么我没有使用 EntityManager 而不是 Session,我使用 EntityManager.

有一些限制

更新

当我调用 saveAndGetDetailsCount 时,entity 参数仅包含 idtitle,在接下来的几行中,您可以看到我想加载 comments 字段并获取它的大小。

如果实体已经附加到当前会话,为什么要生成查询?

还有,为什么要调用merge和update呢?您应该使用其中之一。但不是两者。

您的问题不在于迁移到休眠 5。我认为您的问题在于 transaction.When 在同一事务中坚持或合并调用,loadById 直到方法 return 才执行。您可以在之后刷新保存合并工作正常和你的对象加载。

将您的代码更改为此

@Repository
public abstract class GenericRepository<T extends BaseEntity,PK extends Serializable> {

    protected Class<T> domainClass = getDomainClass();

    /* changed */
    @PersistenceContext
    private EntityManager em;

    public Session getSession() {
        /* changed */
        return  em.unwrap(Session.class);
    }

    public PK save(T Entity) {
        Session session = getSession();
        if(entity.getId() == null) {
            em.persist(entity);
        }
        else {
            em.merge(entity);
            em.flush();
        }
        return entity.getId();
    }

    public T loadByEntityId(PK entityId) {
        Session session = getSession();
        return  (T) session.get(domainClass.getName(), entityId);
    }
}