我正在尝试对 mysql 5.7 数据库使用 spring-data-jpa 来使用正则表达式查找实体。我对 jpaRepository 方法的 native 查询产生错误。
我正在用 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);
...
}
但我不断收到以下错误:不支持的关键字正则表达式 (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 类(class):
@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”列。在licensekinships上找到‘ParentID’,这是license与license之间多对一关系的关系表。
我的本机查询是否需要考虑 DBLicense 上的所有其他关系注释?这是有问题的,因为有很多(内置的 LicenseRepository findById
方法执行不少于 59 个查询!)。
最佳答案
如果您在实体上使用 hibernate/javax.persistence 关系注释(即 @OneToOne、@OneToMany、@ManyToOne、@ManyToMany),并且您尝试使用 native 查询,那么您可能会遇到与在问题帖子中提出。
如果您有一个复杂的架构,其中一个实体与另一个实体共享关系,而另一个实体又具有进一步的关系,并且您尝试从 native 查询返回其中一个实体,则尤其会发生这种情况。要修复此错误,您需要在 spring-data-jpa 的 native 查询中提供足够的信息,以解析实体中存在的关系。
例如,考虑以下类对象:
@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:
- 查找并打开您的 application.properties(或 application.yaml)文件。通常位于“src/main/resources”中。
- 添加以下行(如果不存在):
spring.jpa.show-sql=true
,然后保存。 - 为您的 native 查询返回的实体创建一个存储库。例如,如果您的 native 查询返回 EntityA,那么您的存储库可能如下所示:
@Repository
public interface MyRepository extends JpaRepository<EntityA, Long> {}
- 调用存储库的 findById 方法(从 Controller 或测试)并检查控制台输出。许多查询将记录到控制台。您的 native 查询需要提供与第一个查询相同的列并实现相同的联接。
关于mysql - 如何使用 spring-data-jpa 对具有持久关系的实体执行 native 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57100428/