对于最新的稳定版本,请使用 Spring Data Relational 3.4.0! |
查询方法
本节提供了有关 Spring Data JDBC 的实现和使用的一些具体信息。
您通常在存储库上触发的大多数数据访问操作都会导致对数据库运行查询。 定义此类查询就是在存储库接口上声明方法,如下例所示:
interface PersonRepository extends PagingAndSortingRepository<Person, String> {
List<Person> findByFirstname(String firstname); (1)
List<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable); (2)
Slice<Person> findByLastname(String lastname, Pageable pageable); (3)
Page<Person> findByLastname(String lastname, Pageable pageable); (4)
Person findByFirstnameAndLastname(String firstname, String lastname); (5)
Person findFirstByLastname(String lastname); (6)
@Query("SELECT * FROM person WHERE lastname = :lastname")
List<Person> findByLastname(String lastname); (7)
@Query("SELECT * FROM person WHERE lastname = :lastname")
Stream<Person> streamByLastname(String lastname); (8)
@Query("SELECT * FROM person WHERE username = :#{ principal?.username }")
Person findActiveUser(); (9)
}
1 | 该方法显示具有给定 .
查询是通过解析可与 和 连接的约束的方法名称来派生的。
因此,方法名称会导致查询表达式 。firstname And Or SELECT … FROM person WHERE firstname = :firstname |
2 | 用于将 offset 和 sorting 参数传递到数据库。Pageable |
3 | 返回一个 .选择行以确定是否有更多数据可供使用。 不支持自定义。Slice<Person> LIMIT+1 ResultSetExtractor |
4 | 运行分页查询,返回 .仅选择给定页面边界内的数据,并可能选择计数查询来确定总计数。 不支持自定义。Page<Person> ResultSetExtractor |
5 | 为给定条件查找单个实体。
它以非唯一结果结束。IncorrectResultSizeDataAccessException |
6 | 与 <3> 相比,即使查询生成更多结果文档,也始终会发出第一个实体。 |
7 | 该方法显示具有给定 .findByLastname lastname |
8 | 该方法返回 a ,这将使值从数据库返回后立即成为可能。streamByLastname Stream |
9 | 您可以使用 Spring 表达式语言动态解析参数。 在该示例中,使用 Spring Security 解析当前用户的用户名。 |
下表显示了查询方法支持的关键字:
关键词 | 样本 | 逻辑结果 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
查询派生仅限于可在子句中使用而无需使用联接的属性。WHERE |
查询查找策略
JDBC 模块支持手动将查询定义为 Comments 中的 String 或属性文件中的命名查询。@Query
从方法名称派生查询目前仅限于简单属性,即直接存在于聚合根中的属性。 此外,此方法仅支持 select 查询。
用@Query
以下示例演示如何使用 声明查询方法:@Query
interface UserRepository extends CrudRepository<User, Long> {
@Query("select firstName, lastName from User u where u.emailAddress = :email")
User findByEmailAddress(@Param("email") String email);
}
为了将查询结果转换为实体,默认情况下与 Spring Data JDBC 自身生成的查询使用相同的结果。
您提供的查询必须与预期的格式匹配。
必须提供实体的构造函数中使用的所有属性的列。
通过 setter、wither 或 field 访问设置的属性列是可选的。
不会设置结果中没有匹配列的属性。
该查询用于填充聚合根、嵌入实体和一对一关系,包括作为 SQL 数组类型存储和加载的基元类型数组。
为 map、list、sets 和 arrays 的实体生成单独的查询。RowMapper
RowMapper
属性一对一关系必须具有以关系名称加 为前缀的 name 。
例如,如果上面示例中的 the 具有 with 属性,则该列必须标记为 ._
User
address
city
city
address_city
请注意,基于字符串的查询不支持分页,也不接受 、 、 ,并且作为查询参数,对于这些查询,需要重写查询。
如果要应用限制,请使用 SQL 表达此意图,并自行将相应的参数绑定到查询。Sort PageRequest Limit |
查询可能包含允许绑定变量的 SPEL 表达式。 这样的 SPEL 表达式将被绑定变量替换,并且该变量将绑定到 SPEL 表达式的结果。
@Query("SELECT * FROM person WHERE id = :#{#person.id}")
Person findWithSpEL(PersonRef person);
这可用于访问参数的成员,如上面的示例所示。
对于更多涉及的用例,可以在应用程序上下文中提供 an,这反过来又可以使任何对象在 SPEL 中可用。EvaluationContextExtension
Spring 完全支持基于 compiler 标志的 Java 8 参数名称发现。
通过在构建中使用此标志作为调试信息的替代方法,您可以省略命名参数的注释。-parameters @Param |
Spring Data JDBC 仅支持命名参数。 |
命名查询
如果 Comments 中没有给出查询,如上一节所述,Spring Data JDBC 将尝试查找命名查询。
有两种方法可以确定查询的名称。
默认情况下,采用查询的 domain 类,即存储库的聚合根,采用其简单名称,并附加由分隔的方法名称。
或者,注释具有一个属性,该属性可用于指定要查找的查询的名称。.
@Query
name
命名查询应在 Classpath 上的属性文件中提供。META-INF/jdbc-named-queries.properties
可以通过将值设置为 来更改该文件的位置。@EnableJdbcRepositories.namedQueriesLocation
命名查询的处理方式与 annotation 提供的查询相同。
流式处理结果
当您指定 Stream 作为查询方法的返回类型时, Spring Data JDBC 会在元素可用后立即返回这些元素。 在处理大量数据时,这适用于减少延迟和内存需求。
流包含与数据库的开放连接。
为避免内存泄漏,最终需要通过关闭流来关闭该连接。
推荐的方法是 .
这也意味着,一旦关闭了与数据库的连接,流就无法获取更多元素,并可能引发异常。try-with-resource clause
自定义或RowMapper
ResultSetExtractor
该注释允许您指定 custom 或 to use。
属性 和 允许您指定要使用的类,这些类将使用默认构造函数进行实例化。
或者,您可以从 Spring 应用程序上下文中将 or 设置为 bean 名称。@Query
RowMapper
ResultSetExtractor
rowMapperClass
resultSetExtractorClass
rowMapperClassRef
resultSetExtractorClassRef
如果你不仅想对单个方法使用 certain,而且对自定义查询返回特定类型的所有方法使用 certain,
您可以注册一个 Bean 并注册一个 Per Method 返回类型。
以下示例显示如何注册 :RowMapper
RowMapperMap
RowMapper
DefaultQueryMappingConfiguration
@Bean
QueryMappingConfiguration rowMappers() {
return new DefaultQueryMappingConfiguration()
.register(Person.class, new PersonRowMapper())
.register(Address.class, new AddressRowMapper());
}
在确定要用于方法的哪个方法时,根据方法的返回类型,将遵循以下步骤:RowMapper
-
如果类型是简单类型,则使用 no。
RowMapper
相反,查询应返回具有单个列的单个行,并且对该值应用到返回类型的转换。
-
迭代 中的实体类,直到找到一个是相关返回类型的超类或接口。 使用为该类注册的 。
QueryMappingConfiguration
RowMapper
迭代按照注册的顺序进行,因此请确保在特定类型之后注册更通用的类型。
如果适用,将解包包装类型(如 collections 或 )。
因此,return type of 使用前面进程中的类型。Optional
Optional<Person>
Person
使用自定义 through 、 或 custom 会禁用实体回调和生命周期事件,因为结果映射可以根据需要发出自己的事件/回调。RowMapper QueryMappingConfiguration @Query(rowMapperClass=…) ResultSetExtractor |
修改查询
您可以使用 on query 方法将查询标记为修改查询,如下例所示:@Modifying
@Modifying
@Query("UPDATE DUMMYENTITY SET name = :name WHERE id = :id")
boolean updateName(@Param("id") Long id, @Param("name") String name);
您可以指定以下返回类型:
-
void
-
int
(更新的记录计数) -
boolean
(记录是否已更新)
修改查询直接针对数据库执行。 不会调用任何事件或回调。 因此,如果带有审核注释的字段未在带注释的查询中更新,则不会更新这些字段。