Spring Boot JPA@CreatedDate @LastModifiedDate не заполняется при сохранении объекта
Я пишу приложение с Spring Boot + JPA, используя базу данных Postgres. У меня есть пользовательский объект, и я пытаюсь получить временную метку, когда запись пользователя сохраняется и / или изменяется. Это не работает. Сохраняемая запись имеет значение "null" как для createDate, так и lastModifiedDate.
Вот весь соответствующий код в Kotlin:
@Entity
@Table(name = "app_user")
@EntityListeners(AuditingEntityListener::class)
data class User(
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
val id: UUID? = null,
@NotNull
@Column(unique = true)
val email: String,
val name: String,
private val password: String,
@CreatedDate
@Column(name = "created_date", nullable = false, updatable = false)
var createdDate: LocalDateTime? = null,
@LastModifiedDate
@Column(name = "last_modified_date", nullable = false)
var lastModifiedDate: LocalDateTime? = null
)
Мои SQL-запросы для добавления полей даты выглядят так:
ALTER TABLE app_user
ADD COLUMN last_modified_date TIMESTAMP,
ADD COLUMN created_date TIMESTAMP;
У меня также есть класс конфигурации
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
class JpaAuditingConfiguration {
@Bean
fun auditorProvider(): AuditorAware<String> {
return AuditorAware { Optional.of(SecurityContextHolder.getContext().authentication.name) }
}
}
И мой тестовый класс:
@Autowired
private lateinit var userRepository: UserRepository
val user = User(
email = "email",
name = "name",
password = "password"
)
@Test
fun `it sets the timestamps when a user is created`() {
userRepository.save(user)
user.let {
assertThat(it.createdDate).isNotNull()
assertThat(it.lastModifiedDate).isNotNull()
}
}
ОБНОВИТЬ:
Кажется, проблема возникает только в тестах. Кажется, это нормально, когда я выполняю один и тот же код для создания пользователя в среде разработки, а не в тестах.
Нужна ли мне дополнительная настройка в тестах?
ОБНОВЛЕНИЕ 2
Я также попытался использовать менеджер сущностей следующим образом:
@Autowired
lateinit var entityManager: TestEntityManager
Затем в тесте делаем:
entityManager.persist(user)
entityManager.flush()
А также
entityManager.persistAndFlush(user)
Все еще не заполняет временные метки.
6 ответов
Методы AuditingEntityListener вызываются в @PrePersist
а также @PreUpdate
фаза.
Это означает, что они вызываются непосредственно перед выполнением операторов вставки или обновления SQL.
Подробнее о событиях JPA в документе Hibernate: https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html
Модульные тесты
При использовании в тестах необходимо включить аудит также на тесте
@DataJpaTest
@RunWith(SpringRunner.class)
@EnableJpaAuditing
public class EntityListenerTest {
У меня была аналогичная проблема (Spring Boot 2.2.1 и JUnit5) с null
ценности. Добавление@EnableJpaAuditing
на тестовый класс не вышло.
Мой пример (не копировал все):
@Getter(AccessLevel.PROTECTED)
@Setter(AccessLevel.PROTECTED)
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class Auditable<U> {
@CreatedBy
@Column(name = "created_by", nullable = false)
private U createdBy;
@CreatedDate
@Column(name = "created", nullable = false)
private OffsetDateTime created;
}
и соответствующий класс:
@Getter
@Setter
@Entity
@Table(name="survey_records")
public class SurveyRecord extends Auditable<String> implements Serializable {
У меня была следующая конфигурация для аудита:
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorProvider", dateTimeProviderRef = "auditingDateTimeProvider")
public class JpaAuditingConfiguration {
@Bean(name = "auditingDateTimeProvider")
public DateTimeProvider dateTimeProvider() {
return () -> Optional.of(OffsetDateTime.now());
}
@Bean
public AuditorAware<String> auditorProvider() {
Получить @CreatedBy
а также @CreatedDate
работает только внутри теста @Import
был нужен
@DataJpaTest
@Import(JpaAuditingConfiguration.class)
class SurveyRecordRepositoryTest {
Использованные ссылки:
У меня была аналогичная проблема, но причина в том, что у меня @Transactional
аннотации к тестовому классу, после ее удаления все работает должным образом.
Мое предпочтительное решение - это набор из нескольких аннотаций для проверки @CreatedBy
или @CreatedDate
:
@RunWith(SpringRunner.class)
@DataJpaTest
@Import(JPAConfig.class)
@WithMockUser(username = "testuser")
public class MYRepositoryTest {
...
}
Мои сущности выглядят так:
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class AbstractEntity {
@CreatedBy
private String createdBy;
@CreatedDate
private Date createdOn;
@LastModifiedDate
private Date updatedOn;
@LastModifiedBy
private String updatedBy;
}
Моя конфигурация JPA:
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class JPAConfig {
@Bean
public AuditorAware<String> auditorAware() {
return () -> Optional.of(((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
}
}
Теперь ваши поля аудита также создаются во время теста JPA:
assertThat(result.getCreatedBy()).isNotNull().isEqualTo("testuser");
assertThat(result.getCreatedOn()).isNotNull();
assertThat(result.getUpdatedBy()).isNotNull().isEqualTo("testuser");
assertThat(result.getUpdatedOn()).isNotNull();
Столкнулся с той же проблемой, но другие решения не помогли. (Я использую Kotlin) Затем я понял, что объявил поле с @LastModifiedBy как val, а не var, то есть только для чтения. Он не преминул обновить его, просто молча ничего не делал. Когда я изменил объявление поля на var, оно начало работать нормально.
вы должны использовать
@Column(name = "created_date", nullable = false, updatable = false)
private LocalDate created = LocalDate.now() ;