Невозможно найти соответствующую ошибку конструктора для объекта вложенного списка в kotlin и JpaRepository - PullRequest
0 голосов
/ 31 марта 2020

Я сталкиваюсь со следующей ошибкой, когда 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

Я использую плагин spring-boot-starter-data-jpa и org.jetbrains.kotlin.plugin.jpa в моем Kotlin проект. У меня есть репозиторий, определенный следующим образом:

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

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

Обратите внимание, что тип, используемый JpaRepository (DbUser), отличается от того, который был перенастроен методом findUserByUsername (User), а также на ошибку выше класса User правильно найдено JPA ( ... class [com.example.dto.User] ... ), но роль - нет. Он ожидает DbRole в целевом DTO, что не так.

DbUser - аннотированный класс @Entity и ссылается на другой аннотированный класс @Entity, называемый DbRole. Оба определены ниже:

@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 во вложенный список DTO Role?

Ответы [ 2 ]

1 голос
/ 31 марта 2020

Проблема в том, что запрос к базе данных может возвращать только простой результат. Постоянный провайдер может преобразовать его в сущности со списками вложенных сущностей. Что касается 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

0 голосов
/ 31 марта 2020

Почему ваш JdbcUserRepository возвращает User объект для findUserByUsername, а не DbUser? Решение должно быть очевидным:

override fun findUserByUsername(username: String): DbUser?

А потом вы делаете сопоставление вручную.

Вы должны думать, что люди знают, почему они ввели несколько представлений одного и того же объекта вместо использования одного представления. как можно дольше ...

...