Spring JPA 无法在 Kotlin 数据类中使用自定义 setter 映射字段

标签 spring spring-data-jpa kotlin

我有一个带有自定义 setter 的 Kotlin 数据类。 Spring JPA 框架似乎无法使用自定义 setter 映射属性。如果我删除自定义 getter/setter 并将属性重命名为 login 而不是 _login,一切似乎都工作正常。如何使用自定义 setter 在 Kotlin 数据类中创建属性,以便在 JPA 框架中识别它?

用户.kt

@Entity
@Table(name = "jhi_user")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
data class User (

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
    @SequenceGenerator(name = "sequenceGenerator")
    var id: Long? = null,

    @NotNull
    @Pattern(regexp = Constants.LOGIN_REGEX)
    @Size(min = 1, max = 50)
    @Column(name = "login", length = 50, unique = true, nullable = false)
    var _login: String? = null,

    @JsonIgnore
    @NotNull
    @Size(min = 60, max = 60)
    @Column(name = "password_hash",length = 60)
    var password: String? = null,

    ...

    @JsonIgnore
    @ManyToMany
    @JoinTable(
    name = "jhi_user_authority",
    joinColumns = arrayOf(JoinColumn(name = "user_id", referencedColumnName = "id")),
    inverseJoinColumns = arrayOf(JoinColumn(name = "authority_name", referencedColumnName = "name")))
    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    @BatchSize(size = 20)
    var authorities: MutableSet<Authority>? = null): AbstractAuditingEntity(), Serializable {

    //Lowercase the login before saving it in database
    var login: String?
        get() = _login
        set(value) {
            _login = StringUtils.lowerCase(value, Locale.ENGLISH)
        }
}

我收到的错误:

...
Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute  with the the given name [login] on this ManagedType [com.sample.domain.AbstractAuditingEntity]
    at org.hibernate.metamodel.internal.AbstractManagedType.checkNotNull(AbstractManagedType.java:128)
    at org.hibernate.metamodel.internal.AbstractManagedType.getAttribute(AbstractManagedType.java:113)
    at org.hibernate.metamodel.internal.AbstractManagedType.getAttribute(AbstractManagedType.java:111)
    at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:569)
    at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.getTypedPath(JpaQueryCreator.java:377)
    at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.build(JpaQueryCreator.java:300)
    at org.springframework.data.jpa.repository.query.JpaQueryCreator.toPredicate(JpaQueryCreator.java:205)
    at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:117)
    at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:54)
    at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:111)
    at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:90)
    at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:78)
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.<init>(PartTreeJpaQuery.java:135)
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$CountQueryPreparer.<init>(PartTreeJpaQuery.java:256)
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:72)
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:103)

最佳答案

像这样对构造函数参数使用自定义 setter 有点难看(但不幸的是,这是我知道这样做的唯一方法)。

对于初学者来说,JPA 希望将 _login 和 login 注册为数据库中的单独列,因为它们都不是 @Transient。我相信您的问题出现在这里,因为您已将 _login 属性标记为映射到“login”列,而登录属性没有 @Column 注释,因此它试图映射到已经具有 _login 属性的“login”的默认值映射到它。

因此,我认为您可能想让 _login 成为暂时的并且只保留登录(为了简洁和清晰,我错过了不相关的代码):

...

@Transient
var _login: String? = null,

...

@NotNull
@Pattern(regexp = Constants.LOGIN_REGEX)
@Size(min = 1, max = 50)
@Column(name = "login", length = 50, unique = true, nullable = false)
var login: String?
    get() = _login
    set(value) {
        _login = StringUtils.lowerCase(value, Locale.ENGLISH)
    }

如果这仍然不起作用,那么我真的认为这比尝试在与 JPA 一起使用的构造函数属性上使用自定义 setter 来获得这个已经有点老套的解决方法更麻烦。我建议改为使用 @PrePersist/@PreUpdate 方法在将其保存到数据库之前为您执行小写操作。

关于Spring JPA 无法在 Kotlin 数据类中使用自定义 setter 映射字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45666529/

相关文章:

java - Spring无法 Autowiring 类

java - 捕获测试中的日志

java - 找不到 XML 模式命名空间 http ://www. springframework.org/schema/data/jpa 的 Spring NamespaceHandler

java - Spring:如何从JPA中的两个表中获取数据

kotlin - 我怎么知道房间的@Insert 已完成?

java - 一起使用 Spring JUnit4 和 JMock

spring-boot - 如何使用Spring Data JPA将数据保存到H2数据库中

kotlin - TornadoFX指南:如何实现客户类

android - Kotlin 中的构造函数

Spring @Value 转义冒号(:) in default value