首页 > 解决方案 > 保存实体后,Hibernate 为使用 @Formula 注释的实体属性返回 null

问题描述

我有简单的课程,如下所示:

@Entity
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue
    Long id;

    @Column(name = "title")
    String title;

    @Formula("(select current_date())")
    Date currentDate;

    @OneToMany(mappedBy = "post")
    Set<PostComment> commentList = new HashSet<>();
}

并希望在服务中更新此实体:

@Service
@Transactional
public class PostService {

    private final PostRepository postRepository;

    public PostService(PostRepository postRepository) {
        this.postRepository = postRepository;
    }

    @Transactional
    public Post save(Post entity) {
        Post post = postRepository.saveAndFlush(entity);
        System.out.println("current_date: " + post.getCurrentDate());
        post.getCommentList().forEach(pc -> System.out.println(pc.getReview()));
        return post;
    }
}

当我检查休眠日志时,它首先是实体select的所有字段,但是当我调用时(它用 注释)返回:Postpost.getCurrentDate()@Formulanull

Hibernate: select post0_.id as id1_0_0_, post0_.title as title2_0_0_, (select current_date()) as formula0_0_ from post post0_ where post0_.id=?
Hibernate: update post set title=? where id=?
current_date: null
Hibernate: select commentlis0_.post_id as post_id3_1_0_, commentlis0_.id as id1_1_0_, commentlis0_.id as id1_1_1_, commentlis0_.post_id as post_id3_1_1_, commentlis0_.review as review2_1_1_ from post_comments commentlis0_ where commentlis0_.post_id=?
review

为什么它返回并记录commentList但不返回currentDate?是休眠错误吗?

笔记

我在 github 中推送了完整的示例项目,您可以在此处查看

标签: javahibernatejpatransactionsformula

解决方案


我在我的 high-performance-java-persistence GitHub 存储库上写了一个测试用例,它就像一个魅力。

实体如下所示:

@Entity(name = "Event")
@Table(name = "event")
public static class Event {

    @Id
    private Long id;

    @Formula("(SELECT current_date)")
    @Temporal(TemporalType.TIMESTAMP)
    private Date createdOn;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Date getCreatedOn() {
        return createdOn;
    }

    public void setCreatedOn(Date createdOn) {
        this.createdOn = createdOn;
    }
}

请注意,该Date属性也使用@Temporal注释。

现在为了模拟您的用例,我编写了以下数据访问逻辑:

Event event = new Event();
event.setId(1L);

entityManager.persist(event);
entityManager.flush();

entityManager.refresh(event);

assertNotNull(event.getCreatedOn());

请注意refresh由于实体缓存在 上所需的调用,persist我们想从数据库中重新获取它。

而且,当执行它时,Hibernate 会生成以下语句:

Query:["insert into event (id) values (?)"], Params:[(1)]
Query:["select formulacur0_.id as id1_0_0_, (SELECT current_date) as formula0_0_ from event formulacur0_ where formulacur0_.id=?"], Params:[(1)]

并且测试用例运行得很好。只需在您的用例和我的用例之间进行比较调试,看看为什么您的用例不起作用而我的用例起作用。


推荐阅读