如何在具有持久性关系的实体上使用spring-data-jpa执行本机查询

时间:2019-07-18 18:11:29

标签: mysql regex hibernate jpa spring-data-jpa

我正在尝试对mysql 5.7数据库使用spring-data-jpa来使用正则表达式查找实体。我对jpaRepository方法的本机查询产生错误。

我正在用Spring替换用于许可的旧的定制c ++服务器。我无法更改数据库结构或api。 我正在使用spring-boot-starter-data-jpa:2.1.4.RELEASE,用户hibernate-core:5.3.9.Final和spring-data-jpa:2.1.6:RELEASE。 我的api实现了以下端点:licenses/search/{fieldName}:{regex}/{limit}/{offset} 例如:licenses/search/edition.name:"^Edition X$"/1/0

我的DBLicense实体与DBEdition具有@OneToMany关系。

起初,我尝试在LicenseRepository中编写查询方法,如here所述:

@Repository
public interface LicenseRepository extends JpaRepository<DBLicense, Long> {
...
List<DBLicense> findByEditions_NameRegex(String searchStr, Pageable pageRequest);
...
}

但是我一直收到以下错误:unsupported keyword regex (1): [matchesregex, matches, regex]。该文档指出regex might not be supported,并检查我找不到的商店特定文档。 Other answers使我尝试使用@Query注释。

由于JPQL does not support regex,我选择使用本机查询:

@Repository
public interface LicenseRepository extends JpaRepository<DBLicense, Long> {
...
    @Query(value = "select l.* from licenses as l join licenseeditions as le on l.LicenseID=le.LicenseID join editions as e on le.EditionID=e.EditionID where e.Name regexp :searchStr limit :offset, :limit", nativeQuery = true)
    List<DBLicense> findByEditions_NameRegex(@Param("searchStr") String searchStr, @Param("offset") Integer offset, @Param("limit") Integer limit);
...
}

,我收到以下错误消息:

2019-07-18 11:46:50.145  WARN 24524 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 0, SQLState: S0022
2019-07-18 11:46:50.146 ERROR 24524 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : Column 'ParentID' not found.

我的DBLicense类:

@Entity
@Table(name = "licenses")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DBLicense {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "LicenseID")
...
    @ManyToOne
    @JoinTable(name = "licensekinships", joinColumns = @JoinColumn(name = "ChildID", referencedColumnName = "LicenseID"), inverseJoinColumns = @JoinColumn(name = "ParentID", referencedColumnName = "LicenseID"))
    private DBLicense parentLicense;
...
    @OneToMany
    @JoinTable(name = "licenseeditions", joinColumns = @JoinColumn(name = "LicenseID", referencedColumnName = "LicenseID"), inverseJoinColumns = @JoinColumn(name = "EditionID", referencedColumnName = "EditionID"))
    @Setter(AccessLevel.NONE)
    @Builder.Default
    private List<DBEdition> editions = new ArrayList<DBEdition>();
}

查询在mysql中成功执行(我检查了日志),并且在Spring内部返回某个时间后抛出了错误。

请注意,我的@Query中引用的表(即许可证,许可版本,版本)均不包含“ ParentID”列。在许可亲属关系上可以找到“ ParentID”,这是许可与许可之间多对一关系的关系表。

我的本​​机查询是否需要考虑DBLicense上的所有其他关系注释?这是有问题的,因为有很多(内置的LicenseRepository findById方法执行不少于59个查询!)。

1 个答案:

答案 0 :(得分:0)

如果您在实体(即@ OneToOne,@ OneToMany,@ ManyToOne,@ ManyToMany)上使用hibernate / javax.persistence关系注释,并且尝试使用本机查询,则可能遇到与以下问题相同的问题出现在问题帖中。

如果您有一个复杂的架构,其中一个实体与另一个实体共享一个关系,而后者又具有进一步的关系,并且您正尝试从本机查询中返回这些实体之一,则将发生这种情况。要解决此错误,您将需要在本机查询中为spring-data-jpa提供足够的信息,以解决实体中存在的关系。

例如,考虑以下类对象:

@Entity
@Table(name = "entity_a")
public class EntityA {
  @Column
  private int entityA_field
  ...
  @ManyToOne
  private EntityB entityB
}

@Entity
@Table(name = "entity_b")
public class EntityB {
  @Column
  private int entityB_field
  ...
  @ManyToOne
  private EntityC entityC
}

用于EntityA id的JpaRepository内置的findById方法可能执行多个数据库查询。使用sql数据库,例如:

select a.*, b.* from entity_a as a left outer join entity_b as b on a.id = b.id;
select b.*, c.* from entity_b as b left outer join entity_c as c on b.id = c.id;

您将需要模仿第一个查询的联接和列。幸运的是,通过打开日志记录,您可以看到spring-data-jpa生成的伪sql:

  1. 找到并打开您的application.properties(或application.yaml)文件。通常位于“ src / main / resources”中。
  2. 添加以下行(如果不存在):spring.jpa.show-sql=true,然后保存。
  3. 为您的本机查询返回的实体建立一个存储库。例如,如果您的本机查询返回EntityA,则您的存储库可能如下所示:
@Repository
public interface MyRepository extends JpaRepository<EntityA, Long> {}
  1. 调用存储库的findById方法(从控制器或测试中)并检查控制台输出。许多查询将记录到控制台。您的本机查询需要提供与第一个查询相同的列并实现相同的联接。