【问题标题】:Spring Boot JPA@CreatedDate @LastModifiedDate not being populated when saving the object保存对象时未填充 Spring Boot JPA@CreatedDate @LastModifiedDate
【发布时间】: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)

仍然没有填充时间戳。

【问题讨论】:

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

标签: spring spring-boot kotlin spring-data-jpa


【解决方案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 {
    

    参考文献:

    【讨论】:

      【解决方案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")。另见stackoverflow.com/questions/43705720/…(来自悉尼用户的回答)
        猜你喜欢
        • 2017-12-14
        • 1970-01-01
        • 2021-01-28
        • 2019-02-26
        • 2019-06-16
        • 2019-06-16
        • 2021-08-18
        • 2019-10-09
        • 2023-01-12
        相关资源
        最近更新 更多