保存对象时未填充 Spring Boot JPA@CreatedDate @LastModifiedDate

Posted

技术标签:

【中文标题】保存对象时未填充 Spring Boot JPA@CreatedDate @LastModifiedDate【英文标题】:Spring Boot JPA@CreatedDate @LastModifiedDate not being populated when saving the object 【发布时间】:2019-01-13 12:53:36 【问题描述】:

我正在使用 Postgres 数据库使用 Spring Boot + JPA 编写应用程序。我有一个用户实体,我试图在保存和/或修改用户记录时获取时间戳。这是行不通的。正在保存的记录的 createdDate 和 lastModifiedDate 均为“null”。

以下是 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)

仍然没有填充时间戳。

【问题讨论】:

我已经编辑了我的问题。我无法自动填充日期字段。基本上我的测试失败了。我最终得到一个空记录而不是时间戳 【参考方案1】:

我有类似的问题,但我的情况的原因是我在测试类上有@Transactional 注释,删除它后,一切都按预期工作。

【讨论】:

【参考方案2】:

我有类似的问题(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 

参考文献:

https://springbootdev.com/2018/03/13/spring-data-jpa-auditing-with-createdby-createddate-lastmodifiedby-and-lastmodifieddate/

https://github.com/spring-projects/spring-boot/issues/10743#issuecomment-416777423

【讨论】:

【参考方案3】:

我首选的解决方案是一组多个注释,以确保@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();

【讨论】:

【参考方案4】:

@PrePersist@PreUpdate 阶段调用 AuditingEntityListener 方法。

这意味着它们在插入或更新 SQL 语句执行之前被调用。

在 Hibernate 文档中阅读有关 JPA 事件的更多信息: https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#events-jpa-callbacks

单元测试

在测试中使用时,您还必须在测试中启用审核

@DataJpaTest
@RunWith(SpringRunner.class)
@EnableJpaAuditing
public class EntityListenerTest 

【讨论】:

感谢您的回答。我写了一个更新。我认为注入和使用存储库应该可以像在测试环境之外一样存储用户模型。我还尝试使用 TestEntityManager 实例来执行此操作。它包含您在此处描述的方法。 我编辑了我的问题。您必须在测试中明确启用审核 如果您使用像 @CreatedBy 这样的审计字段,您还需要启用一些配置来配置您的 @EnableJpaAuditing 东西,比如 AuditorAware。在这种情况下,只需在测试类中额外导入@Import(YourJPAConfiguration.class)。也许还添加@WithMockUser(username = "testuser")。另见***.com/questions/43705720/…(来自悉尼用户的回答)

以上是关于保存对象时未填充 Spring Boot JPA@CreatedDate @LastModifiedDate的主要内容,如果未能解决你的问题,请参考以下文章

多对多 Spring Boot JPA 未填充多对多表

MYSQL Workbench 未填充 Spring Boot JPA 实体的表数据

是否可以在 Spring Boot 中运行两个使用 spring.jpa.generate-ddl 填充的嵌入式数据库?

Spring Boot Data JPA @CreatedBy 和 @UpdatedBy 未填充使用 OIDC 进行身份验证

使用 Spring Boot 和 Spring Data JPA 的 Hibernate 拦截器或侦听器

Spring-Boot,无法使用 spring-data JPA 在 MySql 中保存 unicode 字符串