Spring Boot JPA @UpdateTimestamp 在使用 Postgresql 时无效

3

我遇到了一个奇怪的问题,即最后修改日期没有自动更新。我使用的是Postgresql 12.3版本Springboot 2.2.4.RELEASE。这是我的实体类:

@Entity
@Table(name = "users")
@org.hibernate.annotations.Entity(
        dynamicUpdate = true
)
@Data
public class Users {


    @Id
    @GeneratedValue(generator = "UUID")
    @GenericGenerator(
            name = "UUID",
            strategy = "org.hibernate.id.UUIDGenerator"
    )
    @Column(updatable = false, nullable = false)
        private String userId;

        private String userName;

        private String userEmail;

        private String userPhoneNumber;

        @CreationTimestamp
        @Column(updatable = false)
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+05:30")
        private Timestamp createdOn;

        @UpdateTimestamp
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+05:30")
        private Timestamp lastUpdatedOn;

}

数据库记录:

            createdon        |      lastupdatedon
 2020-08-27 07:43:37.994 | 2020-08-27 07:43:37.994
 2020-08-07 07:49:22.797 | 2020-08-07 07:49:22.797
 2020-08-12 13:38:43.503 | 2020-08-12 13:38:43.503

您可以看到,createdOn和lastUpdatedOn是相同的。即使记录经常更新,最后修改日期也没有更新。

我正在使用jpa repository保存记录 例如:

 usersRepository.save(user); 

你是如何更新这些记录的。 - M. Deinum
使用JPA方法,例如:usersRepository.save(user); - Dilli Babu Kadati
你有没有尝试过不使用Hibernate特定的dynamicUpdate?只是好奇。另外,你是否在某个地方通过查询来更新某些内容(因为这会绕过监听器)? - M. Deinum
@M.Deinum 我尝试直接使用CLI(psql)更新记录,即使从那里也无法更改lastUpdatedOn。 - Dilli Babu Kadati
当然,如果你直接执行SQL语句,它是不会改变的。这是Spring Data JPA的一个特性,所以如果你绕过它,它将不起作用。 - M. Deinum
2个回答

2
你可以尝试使用PrePersist和PreUpdate注释,而不是CreationTimestamp和UpdateTimestamp,以更好地控制实体并应用以下内容-
@Entity
@Table(name = "users")
@org.hibernate.annotations.Entity(
        dynamicUpdate = true
)
@Data
public class Users {


    @Id
    @GeneratedValue(generator = "UUID")
    @GenericGenerator(
            name = "UUID",
            strategy = "org.hibernate.id.UUIDGenerator"
    )
    @Column(updatable = false, nullable = false)
    private String userId;

    private String userName;

    private String userEmail;

    private String userPhoneNumber;

    @Column(updatable = false)
    private Timestamp createdOn;

    @Column
    private Timestamp lastUpdatedOn;

    @PrePersist
    public void onInsert() {
      createdOn = Timestamp.from(ZonedDateTime.now(ZoneId.of("Asia/Kolkata")).toInstant());
      lastUpdatedOn = createdOn;
    }

    @PreUpdate
    public void onUpdate() {
      lastUpdatedOn = Timestamp.from(ZonedDateTime.now(ZoneId.of("Asia/Kolkata")).toInstant());
     }
}

1
在幕后,这最终将被使用,尽管是通过一个监听器。 - M. Deinum

1

你是否偶然在事务内调用了保存方法?

时间戳是在写入数据库时填充的,因此只有在提交事务之后才能看到它们的值。

当我遇到这个问题时,我的代码看起来像这样:

@Transactional
public Response createFooBusinessLogic(Foo foo) {
    var createdEntity = fooRepository.save(foo);
    return someMethodUsingCreatedTime(createdEntity);
}

有两种解决方案:
  1. 如果其他实体在DB事务中需要这些日期,可以使用saveAndFlush而不是save。 这会强制向DB写入,返回创建和修改的日期。
  2. 否则,将save调用重构为另一个方法,并在该方法上注释@Transactional

我选择了选项(2),它看起来像这样:

public Response createFooBusinessLogic(Foo foo) {
    var createdEntity = facade.saveFoo(foo);
    return someMethodUsingCreatedTime(createdEntity);
}

// In Facade.java
@Transactional
public Foo saveFoo(Foo foo) {
    return fooRepository.save(foo);
}

请注意,为了使交易注释起作用,调用不能是自调用的(即必须在另一个类上调用),并且该方法必须是公共的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接