hibernatejpaentitymanagerhibernate-entitymanager

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


For a years in a project i used from hibernate 4.3.11.Final and spring 4.3.1.RELEASE to maniuplating and persisting objects. Newly i migrated to hibernate 5.2.10.Final and encountered a problem that describe it. I have two domain class as you can see below:

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

and these are hibernate mapping files:

<?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>

In the PostService class exists two methods that one of theme persists Post entity and it's PostComment's and another save the Post entity and logs count of it's PostCommment's.

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

Both of PostRepository and PostCommentRepository are extended from GenericRepository that you can see it below:

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

Everything is working fine when i call save method and then call saveAndGetDetailsCount it updates the entity and then executes select... query to load Post entity and then logs 3 and post comments.

But after i migrated to hibernate 5.2.10.Final and applied these changes:

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

and in then GenericRepository i used EntityManager to get Session class as you can see below:

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

Now, the problem is that, when i run previous method calls, in the saveAndGetDetailsCount, hibernate does not execute select... query and logs 0 as comments count.

I wondered why this happened.

UPDATE

Maybe you ask why i didn't used from EntityManager insteadof Session, i have some limitation to use EntityManager.

UPDATE

When i call the saveAndGetDetailsCount, the entity parameter contains only id and title and in the next lines as you can see i want to load comments field and get it's size.


Solution

  • Your problem is not in migration to hibernate 5 .I think your problem is in transaction.When persist or merge call in same transaction, loadById dose not execute until method return .You can flush after save to merge working correctly and your object load .

    change your code to this

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