首页 > 解决方案 > 无法为 kotlin 和 JpaRepository 中的嵌套列表对象找到适当的构造函数错误

问题描述

当 JPA 尝试将查询的结果映射到结果存储库方法 DTO 时,我遇到以下错误:

org.hibernate.hql.internal.ast.QuerySyntaxException: 
Unable to locate appropriate constructor on class [com.example.dto.User]. Expected arguments are: java.lang.String, java.lang.String, com.example.repository.DbRole

我在我的 Kotlin 项目中使用spring-boot-starter-data-jpaorg.jetbrains.kotlin.plugin.jpa插件。我有一个这样定义的存储库:

@Repository
internal interface JdbcUserRepository : UserRepository, JpaRepository<DbUser, String> {

    override fun findUserByUsername(username: String): User?
}

请注意,JpaRepository (DbUser) 使用的类型与 findUserByUsername 方法 (User) 重新调整的类型不同,并且在上面的错误中,JPA 正确找到了 User 类 ( ...class [com.example.dto.User] ... ) 但角色没有。它期望目标 DTO 中有一个 DbRole,这是怎么回事。

DbUser 是一个@Entity 注释类,它引用另一个名为DbRole 的@Entity 注释类。两者定义如下:

@Entity
@Table(name = "user")
internal data class DbUser(
    @Id @Column val username: String,
    @Column val password: String,
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
        name = "user_role",
        joinColumns = [JoinColumn(name = "username", referencedColumnName = "username")],
        inverseJoinColumns = [JoinColumn(name = "role_id", referencedColumnName = "id")]
    ) val roles: List<DbRole>
)

@Entity
@Table(name = "role")
internal data class DbRole(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long,
    @Column val roleName: String,
    @Column val description: String
)

以下是 JPA 必须将结果映射到的类:

data class User(
    val username: String,
    val password: String,
    val roles: List<Role>
)

data class Role(val roleName: String, val description: String)

有谁知道如何解决这个问题,JPA 正确地找到 DbRole 的嵌套实体列表并将其映射到 Role 的嵌套 DTO 列表?

标签: hibernatejpakotlinspring-data-jpa

解决方案


问题是数据库查询只能返回纯结果。持久性提供程序可以将其转换为具有嵌套实体列表的实体。至于dto你必须自己解决问题。

因此,您可以使用User带有构造函数的 dto 获得简单的结果,如下所示

public User(String username, String password, String roleName, String roleDescription) {
     this.username = username;
     this.password = password;
     roles = new ArrayList<>();
     roles.add(new Role(roleName, roleDescription));  
}

然后你需要这样的存储库方法

@Query("select new com.example.dto.User(u.username, u.password, r.roleName, r.description) from DbUser u join u.roles r where u.username=:username")
List<User> findUserByUsername(@Param("username") String username);

在服务层处理结果

public Optional<User> findUserByUsername(username) {
    List<User> users = findUserByUsername(username);

    if(users.isEmpty()) {
        return Optional.empty();
    }

    User user = users.get(0);
    if(users.size() > 1) {
         users.subList(1, users.size()).forEach(u -> user.getRoles().addAll(u.getRoles()));
    }

    return Optional.of(user);
}

您可以使用相同的方法Kotlin


推荐阅读