java - Spring Data JPA - 是否可以在将实体保留在存储库中之前使用主体更新实体?

标签 java spring kotlin spring-security spring-data-jpa

我想使用 Spring Security 5 保护我的资源服务器,以便可供许多用户使用。我有一个实体Task其中包含一个字段 userId .

@Entity
@Table(name = "tasks")
data class Task(
    @NotBlank
    val name: String,

    val description: String?,

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "task_id_seq")
    val id: Long = 0,

    @UUID //custom validation annotation
    val userId: String? = null
)

我找到了一种使用 SpEL 在查询中注入(inject) JWT 主体的方法。我已经做的是我已经覆盖 findAll()我的方法interface TaskRepository : JpaRepository<Task, Long>@Query注释:

@Query("select t from Task t where t.userId = :#{principal.claims['sub']}")
//sub is a userId in OpenID Connect 1.0, claim is a Map<String, Object>
override fun findAll(): List<Task>

问题是我想做类似 save() 的事情方法。我发现的唯一一件事就是对已保存的实体进行更新。我创建了一个全新的方法:

@Modifying
@Query("update Task t set t.userId = :#{principal.claims['sub']} where t.id = :id")
fun setUserId(@Param("id") id: Long)

但问题是我需要在我的 TaskService 中调用这个方法,如下所示:

@Transactional
fun createTask(taskDto: CreateTaskDto): Task {
    val task = taskRepository.save(taskDto.toEntity())
    taskRepository.setUserId(task.id)
    return task
}

每次在数据库中保存实体时是否可以自动添加此信息?那么我在 TaskService 中的代码将如下所示:

@Transactional
fun createTask(taskDto: CreateTaskDto): Task {
    return taskRepository.save(taskDto.toEntity())
}

最佳答案

解决方案实际上很简单,但我不知道我在寻找什么。不过,我不知道这是否是一个规范的解决方案。我为此使用 Jpa 审计。首先,我添加了@CreatedBy在插入实体之前我想要更新的字段的注释。实体类我也用@EntityListeners(AuditingEntityListener::class)注释。请注意,需要更改 val关键字为var (从 final 字段到 Java 世界中的非最终字段)。

@Entity
@Table(name = "tasks")
@EntityListeners(AuditingEntityListener::class)
data class Task(
        @NotBlank
        val name: String,

        val description: String?,

        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        @SequenceGenerator(name = "task_id_seq")
        val id: Long = 0,

        @UUID
        @CreatedBy
        var userId: String? = null
    )

下一步是实现interface AuditorAware<T>并通过使用@Component(或在配置类中创建一个bean)使其对Spring可见

@Component
class SpringSecurityAuditorAware : AuditorAware<String> {

    override fun getCurrentAuditor(): Optional<String> {
        return Optional.ofNullable(SecurityContextHolder.getContext()?.authentication)
                .filter { authentication -> authentication.isAuthenticated }
                .map { authentication -> authentication.principal as Jwt }
                .map { jwt -> jwt.claims["sub"] as String? }
    }

}

最后但并非最不重要的一点是在某些配置类上添加 @EnableJpaAuditing:

@Configuration
@EnableJpaAuditing
class ResourceServerContext

关于java - Spring Data JPA - 是否可以在将实体保留在存储库中之前使用主体更新实体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55941695/

相关文章:

java - Spring - Path 的工厂方法

Java setPreferredSize() 失败

java - 使用 JSch SFTP 下载文件时获取 "The file does not exist."

java - 使用 Scala 中的原始类型实现 Java 接口(interface)

spring - 将外部 application.properties 文件添加到 tomcat 中的 dockerized spring boot web 应用程序

java - 创建自定义 RestTemplate 时 Spring-boot 出错

即使在交换属性后,Android 资源编译也会失败

android - Kotlin Coroutines - 返回流的暂停函数永远运行

kotlin - Kotlin 中的 "receiver"是什么?

java - 为什么我的 if else 语句(即?: ) does not work?