"Could not prepare statement"一对多

"Could not prepare statement" OneToMany

这是我想要实现的目标:

Write a web service that gives the details of a repository: id, name, user name and list of commits.

需要以JSON格式返回,例如:

{
  "repository_id": "1",
  "repository_name": "My repo",
  "owner": "Noah",
  "commits": [
    "First commit",
    "Second commit",
    "Third commit"
  ]
}

您会发现数据库结构如下:

这是我的 CrudRepository 我正在尝试构建的查询:

public interface RepositoriesDB extends CrudRepository<Repository, String> {
    @Query(value = "SELECT r.repositoryId, r.repositoryName, r.owner.userName, r.commits FROM Repository r WHERE r.repositoryId = :repoId")
    List<Object[]> getRepo(@Param("repoId") long repoId);
}

我的 User class :

@Entity
@NoArgsConstructor
@Data
public class User {
    @NotNull
    @Id
    private String userLogin;

    @NotBlank
    @NotNull
    private String userName;

    @OneToMany(mappedBy = "owner", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JsonIgnore
    private List<Repository> repositories;
}

我的 Repository class :

@Entity
@NoArgsConstructor
@Data
public class Repository {
    @NotNull
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long repositoryId;

    @NotNull
    @NotBlank
    @Size(min = 3, max = 25)
    private String repositoryName;

    @OneToMany(mappedBy = "repository", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JsonIgnore
    private List<Commit> commits;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "owner_login", nullable = false)
    private User owner;
}

我的 Commit class :

@Entity
@NoArgsConstructor
@Data
public class Commit {
    @NotNull
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long commitId;

    @NotBlank
    @NotNull
    private LocalDateTime date = LocalDateTime.now();

    @NotNull
    @NotBlank
    @Size(min = 1, max = 255)
    private String message;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "repository_id", nullable = false)
    private Repository repository;
}

最后,这里是堆栈跟踪:

There was an unexpected error (type=Internal Server Error, status=500).
could not prepare statement; SQL [select repository0_.repository_id as col_0_0_, repository0_.repository_name as col_1_0_, user1_.user_name as col_2_0_, . as col_3_0_, commits2_.commit_id as commit_i1_0_, commits2_.date as date2_0_, commits2_.message as message3_0_, commits2_.repository_id as reposito4_0_ from repository repository0_ cross join user user1_ inner join commit commits2_ on repository0_.repository_id=commits2_.repository_id where repository0_.owner_login=user1_.user_login and repository0_.repository_id=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [select repository0_.repository_id as col_0_0_, repository0_.repository_name as col_1_0_, user1_.user_name as col_2_0_, . as col_3_0_, commits2_.commit_id as commit_i1_0_, commits2_.date as date2_0_, commits2_.message as message3_0_, commits2_.repository_id as reposito4_0_ from repository repository0_ cross join user user1_ inner join commit commits2_ on repository0_.repository_id=commits2_.repository_id where repository0_.owner_login=user1_.user_login and repository0_.repository_id=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:259)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)
[...]
Caused by: org.hibernate.exception.SQLGrammarException: could not prepare statement
    at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:63)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
[...]
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "SELECT REPOSITORY0_.REPOSITORY_ID AS COL_0_0_, REPOSITORY0_.REPOSITORY_NAME AS COL_1_0_, USER1_.USER_NAME AS COL_2_0_, .[*] AS COL_3_0_, COMMITS2_.COMMIT_ID AS COMMIT_I1_0_, COMMITS2_.DATE AS DATE2_0_, COMMITS2_.MESSAGE AS MESSAGE3_0_, COMMITS2_.REPOSITORY_ID AS REPOSITO4_0_ FROM REPOSITORY REPOSITORY0_ CROSS JOIN USER USER1_ INNER JOIN COMMIT COMMITS2_ ON REPOSITORY0_.REPOSITORY_ID=COMMITS2_.REPOSITORY_ID WHERE REPOSITORY0_.OWNER_LOGIN=USER1_.USER_LOGIN AND REPOSITORY0_.REPOSITORY_ID=?"; expected "*, NOT, EXISTS, INTERSECTS, UNIQUE"; SQL statement:
select repository0_.repository_id as col_0_0_, repository0_.repository_name as col_1_0_, user1_.user_name as col_2_0_, . as col_3_0_, commits2_.commit_id as commit_i1_0_, commits2_.date as date2_0_, commits2_.message as message3_0_, commits2_.repository_id as reposito4_0_ from repository repository0_ cross join user user1_ inner join commit commits2_ on repository0_.repository_id=commits2_.repository_id where repository0_.owner_login=user1_.user_login and repository0_.repository_id=? [42001-200]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:453)
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:429)
    at org.h2.message.DbException.getSyntaxError(DbException.java:243)
    at org.h2.command.Parser.getSyntaxError(Parser.java:1053)
[...]

我认为问题出在我正在尝试 select 一个 OneToMany 关系,因为当 selecting 所有者时我没有这个问题,因为我有一个 @JsonIgnore 注释和无限循环...
除了,在这种情况下,我需要 select 交易列表 :(

在此先感谢您的帮助!

您的 sql 查询中的错误是 r.commits 部分。它是一个值列表,sql 列仅接受 single (or scalar) 值类型,例如 numbervarchar

由于 RepositoryCommit 实体之间的关系是 One-To-Many 关联,因此 r.commits 是一个值列表,因此 Hibernate 无法准备 sql声明。

您可以从 sql 查询中删除 r.commits 部分,它将起作用。

如果您想获取存储库的提交列表,您可以为此实现一个特定的方法。 像

public interface CommitRepository extends JpaRepository<Commit, Long> {
      List<Commit> findAllByRepository(Repository repository);
}

您可以将要获取所有关联提交的 repository 对象传递给此方法。

此外,由于 Repository.repositoryIdLong 数据类型,因此您必须将 RepositoriesDB 定义更改为

public interface RepositoriesDB extends JpaRepository<Repository, Long>