此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Data Relational 3.5.2! |
查询方法
本节提供了有关 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 将偏移量和排序参数传递给数据库。 |
3 | 返回一个Slice<Person> .选择LIMIT+1 行,以确定是否有更多数据要使用。ResultSetExtractor 不支持自定义。 |
4 | 运行分页查询返回Page<Person> .仅选择给定页面边界内的数据,并可能选择计数查询来确定总计数。ResultSetExtractor 不支持自定义。 |
5 | 为给定条件查找单个实体。
它完成为IncorrectResultSizeDataAccessException 在非唯一结果上。 |
6 | 与 <3> 相比,即使查询产生更多结果文档,也始终发出第一个实体。 |
7 | 这findByLastname 方法显示对所有具有给定lastname . |
8 | 这streamByLastname 方法返回一个Stream ,这使得值在从数据库返回后立即成为可能。 |
9 | 您可以使用 Spring 表达式语言来动态解析参数。 在示例中,Spring Security 用于解析当前用户的用户名。 |
下表显示了查询方法支持的关键字:
关键词 | 样本 | 逻辑结果 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
查询派生仅限于可用于WHERE 子句而不使用连接。 |
查询查找策略
JDBC 模块支持手动将查询定义为 String@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 或字段访问设置的属性列是可选的。
不会设置结果中没有匹配列的属性。
该查询用于填充聚合根、嵌入实体和一对一关系,包括作为 SQL 数组类型存储和加载的基元类型数组。
为图、列表、集合和实体数组生成单独的查询。
属性一对一关系必须具有以关系名称加 为前缀的名称前缀的名称。
例如,如果_
User
从上面的例子中,有一个address
与该物业city
该列city
必须贴上标签address_city
.
请注意,基于字符串的查询不支持分页,也不接受Sort ,PageRequest 和Limit 作为查询参数,对于这些查询,需要重写查询。
如果要应用限制,请使用 SQL 表达此意图,并自行将相应的参数绑定到查询中。 |
查询可能包含 SpEL 表达式。 有两种变体的评估方式不同。
在第一个变体中,SpEL 表达式前缀为:
并像 bind 变量一样使用。
这样的 SpEL 表达式将被 bind 变量替换,并且该变量将绑定到 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
添加。
当表名本身是动态的时,这种用法最有用,因为它们也使用 SpEL 表达式。
Spring 完全支持 Java 8 的参数名称发现,基于-parameters 编译器标志。
通过在构建中使用此标志作为调试信息的替代方法,可以省略@Param 命名参数的注释。 |
Spring Data JDBC 仅支持命名参数。 |
命名查询
如果如上一节所述,在注释中没有给出查询,Spring Data JDBC 将尝试找到命名查询。
有两种方法可以确定查询的名称。
默认是采用查询的域类,即存储库的聚合根,采用其简单名称并附加方法的名称,以.
. 或者,@Query
注释有一个name
属性,可用于指定要查找的查询的名称。
属性文件中应提供命名查询META-INF/jdbc-named-queries.properties
在类路径上。
可以通过将值设置为@EnableJdbcRepositories.namedQueriesLocation
.
命名查询的处理方式与注释提供的查询相同。
流式处理结果
当您将Stream指定为查询方法的返回类型时,Spring Data JDBC会在元素可用时立即返回元素。 在处理大量数据时,这适用于减少延迟和内存需求。
流包含与数据库的打开连接。
为避免内存泄漏,最终需要通过关闭流来关闭该连接。
推荐的方法是try-with-resource clause
.
这也意味着,一旦与数据库的连接关闭,流就无法获取更多元素,并且可能会抛出异常。
习惯RowMapper
或ResultSetExtractor
这@Query
注释允许您指定自定义RowMapper
或ResultSetExtractor
使用。
属性rowMapperClass
和resultSetExtractorClass
允许您指定要使用的类,这些类将使用默认构造函数进行实例化。
或者,您可以将rowMapperClassRef
或resultSetExtractorClassRef
添加到 Spring 应用程序上下文中的 bean 名称。
如果要使用某个RowMapper
不仅适用于单个方法,而且适用于具有返回特定类型的自定义查询的所有方法
您可以注册一个RowMapperMap
bean 并注册一个RowMapper
每个方法返回类型。
以下示例演示如何注册DefaultQueryMappingConfiguration
:
@Bean
QueryMappingConfiguration rowMappers() {
return new DefaultQueryMappingConfiguration()
.register(Person.class, new PersonRowMapper())
.register(Address.class, new AddressRowMapper());
}
在确定哪个RowMapper
要用于方法,请根据方法的返回类型执行以下步骤:
-
如果类型是简单类型,则没有
RowMapper
被使用。相反,查询应返回具有单个列的单行,并将对返回类型的转换应用于该值。
-
中的实体类
QueryMappingConfiguration
被迭代,直到找到一个是相关返回类型的超类或接口。 这RowMapper
使用该类的注册。迭代按注册顺序进行,因此请确保在特定类型之后注册更通用的类型。
如果适用,包装器类型,例如集合或Optional
被解包。因此,返回类型Optional<Person>
使用Person
在前面的进程中键入。
使用自定义RowMapper 通过QueryMappingConfiguration ,@Query(rowMapperClass=…) 或自定义ResultSetExtractor 禁用实体回调和生命周期事件,因为结果映射可以根据需要发出自己的事件/回调。 |