【问题标题】:hibernate session.get does not load persistent object when migrate from hibernate 4.3.11 to 5.2.10 and use EntityManager insteadof SessionFactory从休眠 4.3.11 迁移到 5.2.10 并使用 EntityManager 而不是 SessionFactory 时,休眠 session.get 不加载持久对象
【发布时间】:2018-11-01 23:53:57
【问题描述】:

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

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 类中存在两种方法,一种是主题持续存在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();
    }
}

PostRepository 和 PostCommentRepository 都是从 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 并发布时一切正常厘米。

但是在我迁移到休眠 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 类,如下所示:

@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 中,休眠不执行 select... 查询并将 0 记录为 cmets 计数。

我想知道为什么会这样。

更新

也许你问我为什么不使用EntityManager 而不是Session,我使用EntityManager 有一些限制。

更新

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

【问题讨论】:

    标签: hibernate jpa entitymanager hibernate-entitymanager


    【解决方案1】:

    您的问题不在于迁移到休眠 5。我认为您的问题在于事务。当在同一事务中持续或合并调用时,loadById 在方法返回之前不会执行。您可以在保存后刷新以合并正常工作并且您的对象加载。

    把你的代码改成这个

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

    【讨论】:

      【解决方案2】:

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

      另外,你为什么叫合并和更新?您应该使用其中一个。但不是两者兼而有之。

      【讨论】:

      • 打电话给merge 和update 都是我的错。请检查我的第二次更新
      • 如果你合并它,返回的实体是托管的,所以只需遍历子集合,你就会得到它的初始化。
      • 我把它改成了entity = (T)session.merge(entity);,但是还是不行!
      • 合并后你访问关联了吗?
      • 不,它再次记录了 0,这意味着合并后我无法访问关联。
      猜你喜欢
      • 2013-01-13
      • 1970-01-01
      • 1970-01-01
      • 2018-09-23
      • 2018-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多