Spring Boot JPA @UpdateTimestamp не работает с Postgresql

#postgresql #spring-boot #jpa

#postgresql #spring-загрузка #jpa

Вопрос:

Я столкнулся со странной проблемой, дата последнего изменения не обновляется автоматически. Я использую 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, например:

  usersRepository.save(user); 
  

Комментарии:

1. И как вы обновляете эти записи.

2. использование методов jpa, таких как usersRepository.save(user);

3. Одну вещь вы пробовали без специфики гибернации dynamicUpdate ? Просто интересно. Также вы не обновляете что-то с помощью запроса где-нибудь (поскольку это обходит слушателей).

4. @M.Deinum Я попытался обновить запись напрямую с помощью CLI (psql), даже с последней даты, которая не изменилась.

5. Конечно, это не изменится, если вы напрямую запустите SQL. Это особенность Spring Data JPA, поэтому, если вы обойдете это, это ничего не даст.

Ответ №1:

Можете ли вы попробовать использовать аннотацию prePersist amp; preUpdate вместо CreationTimestamp amp; UpdateTimestampas, чтобы иметь больше контроля над объектом и применить ниже-

 @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. Под капотом это то, что в конечном итоге будет использоваться, хотя и через прослушиватель.

Ответ №2:

Вы случайно вызываете метод сохранения в транзакции?

Временные метки заполняются при записи в БД, поэтому вы не увидите их значения до тех пор, пока транзакция не будет зафиксирована.

Когда я столкнулся с этой проблемой, у меня был код, который выглядел следующим образом:

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

Есть два решения:

  1. Если даты требуются другому объекту в транзакции БД, вы можете использовать saveAndFlush вместо save . Это приводит к принудительной записи в БД, возвращая обратно созданные и измененные даты.
  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);
}
  

Обратите внимание, что для работы аннотации транзакции вызов не должен быть самозапускающимся (т. е. должен вызываться в другом классе), и метод должен быть общедоступным.