查询方法
本节提供了有关 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 | 用Pageable 将 offset 和 sorting 参数传递给数据库。 |
3 | 返回一个Slice<Person> .选择LIMIT+1 rows 来确定是否有更多数据可供使用。ResultSetExtractor 不支持自定义。 |
4 | 运行分页查询,返回Page<Person> .仅选择给定页面边界内的数据,并可能选择计数查询来确定总计数。ResultSetExtractor 不支持自定义。 |
5 | 为给定条件查找单个实体。
它以IncorrectResultSizeDataAccessException 在非唯一结果上。 |
6 | 与 <3> 相比,即使查询生成更多结果文档,也始终会发出第一个实体。 |
7 | 这findByLastname method 显示具有给定lastname . |
8 | 这streamByLastname method 返回一个Stream ,这使得值在从数据库返回后立即成为可能。 |
9 | 您可以使用 Spring 表达式语言动态解析参数。 在该示例中,使用 Spring Security 解析当前用户的用户名。 |
下表显示了查询方法支持的关键字:
关键词 | 样本 | 逻辑结果 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
查询派生仅限于可在WHERE 子句。 |
用@Query
以下示例演示如何使用@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);
}
要将查询结果转换为相同的实体RowMapper
默认用于 Spring Data JDBC 自身生成的查询。
您提供的查询必须与RowMapper
预计。
必须提供实体的构造函数中使用的所有属性的列。
通过 setter、wither 或 field 访问设置的属性列是可选的。
不会设置结果中没有匹配列的属性。
该查询用于填充聚合根、嵌入实体和一对一关系,包括作为 SQL 数组类型存储和加载的基元类型数组。
为 map、list、sets 和 arrays 的实体生成单独的查询。
属性一对一关系必须具有以关系名称加 为前缀的 name 。
例如,如果_
User
在上面的示例中,有一个address
与属性city
该列city
必须贴标签address_city
.
请注意,基于字符串的查询不支持分页,也不接受Sort ,PageRequest 和Limit 作为查询参数,对于这些查询,需要重写查询。
如果要应用限制,请使用 SQL 表达此意图,并自行将相应的参数绑定到查询。 |
查询可能包含 SPEL 表达式。 有两种变体的评估方式不同。
在第一个变体中,SPEL 表达式的前缀为:
并像 BIND 变量一样使用。
这样的 SPEL 表达式将被绑定变量替换,并且该变量将绑定到 SPEL 表达式的结果。
@Query("SELECT * FROM person WHERE id = :#{#person.id}")
Person findWithSpEL(PersonRef person);
这可用于访问参数的成员,如上面的示例所示。
有关更多涉及的使用案例,请参阅EvaluationContextExtension
可以在应用程序上下文中可用,而应用程序上下文又可以使任何对象可用于 SPEL。
另一个变体可以在查询中的任何位置使用,并且评估查询的结果将替换查询字符串中的表达式。
@Query("SELECT * FROM #{tableName} WHERE id = :id")
Person findWithSpEL(PersonRef person);
它在第一次执行之前评估一次,并使用StandardEvaluationContext
替换为两个变量tableName
和qualifiedTableName
添加。
当 table 名本身是动态的时,这种用法最有用,因为它们也使用 SPEL 表达式。
Spring 完全支持 Java 8 的参数名称发现,基于-parameters compiler 标志。
通过在构建中使用此标志作为调试信息的替代方法,您可以省略@Param 注解。 |
Spring Data JDBC 仅支持命名参数。 |
命名查询
如果 Comments 中没有给出查询,如上一节所述,Spring Data JDBC 将尝试查找命名查询。
有两种方法可以确定查询的名称。
默认情况下,采用查询的 domain 类,即存储库的聚合根,采用其简单名称,并附加由分隔.
.
或者,@Query
annotation 具有name
属性,该属性可用于指定要查找的查询的名称。
命名查询应在属性文件中提供META-INF/jdbc-named-queries.properties
在 Classpath 上。
可以通过将值设置为@EnableJdbcRepositories.namedQueriesLocation
.
命名查询的处理方式与 annotation 提供的查询相同。
流式处理结果
当您指定 Stream 作为查询方法的返回类型时, Spring Data JDBC 会在元素可用后立即返回这些元素。 在处理大量数据时,这适用于减少延迟和内存需求。
流包含与数据库的开放连接。
为避免内存泄漏,最终需要通过关闭流来关闭该连接。
推荐的方法是try-with-resource clause
.
这也意味着,一旦关闭了与数据库的连接,流就无法获取更多元素,并可能引发异常。
习惯RowMapper
或ResultSetExtractor
这@Query
annotation 允许您指定自定义RowMapper
或ResultSetExtractor
使用。
属性rowMapperClass
和resultSetExtractorClass
允许您指定要使用的类,这些类将使用默认构造函数进行实例化。
或者,您可以将rowMapperClassRef
或resultSetExtractorClassRef
添加到 Spring 应用程序上下文中的 bean 名称。
如果您想使用某个RowMapper
不仅适用于单个方法,还适用于自定义查询返回特定类型的所有方法,
您可以注册一个RowMapperMap
bean 并注册一个RowMapper
per 方法返回类型。
以下示例演示如何注册DefaultQueryMappingConfiguration
:
@Bean
QueryMappingConfiguration rowMappers() {
return new DefaultQueryMappingConfiguration()
.register(Person.class, new PersonRowMapper())
.register(Address.class, new AddressRowMapper());
}
确定哪个RowMapper
要使用 for 方法,请根据方法的返回类型执行以下步骤:
-
如果类型是简单类型,则没有
RowMapper
被使用。相反,查询应返回具有单个列的单个行,并且对该值应用到返回类型的转换。
-
的
QueryMappingConfiguration
进行迭代,直到找到一个是相关返回类型的超类或接口。 这RowMapper
registered for that class 的 。迭代按照注册的顺序进行,因此请确保在特定类型之后注册更通用的类型。
如果适用,包装器类型(如 collections 或Optional
解包。
因此,返回类型Optional<Person>
使用Person
type 在前面的进程中。
使用自定义RowMapper 通过QueryMappingConfiguration ,@Query(rowMapperClass=…) 或自定义ResultSetExtractor 禁用 Entity Callbacks 和 Lifecycle Events,因为结果映射可以根据需要发出自己的事件/回调。 |