范培忠 2018-04-26
Spring Data JPA - Reference Documentation
Version 2.0.6.RELEASE,2018-04-04
一般步骤
1.定义接口
interface PersonRepository extends Repository<Person, Long> { … }
2.定义方法
interface PersonRepository extends Repository<Person, Long> {
List<Person> findByLastname(String lastname);
}
3.设置代理
JavaConfig或XML。(按:实际在Spring Boot里并不需要。——范培忠)
①JavaConfig
Example 23. Sample annotation based repository configuration
@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {
@Bean
EntityManagerFactory entityManagerFactory() {
// …
}
}
②XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<jpa:repositories base-package="com.acme.repositories"/>
</beans>
4.注入并使用
class SomeClient {
private final PersonRepository repository;
@AutoWired
SomeClient(PersonRepository repository) {
this.repository = repository;
}
void doSomething() {
List<Person> persons = repository.findByLastname("Matthews");
}
}
Defining repository interfaces
定义持久层接口
As a first step you define a domain class-specific repository interface. The interface must extend Repository and be typed to the domain class and an ID type. If you want to expose CRUD methods for that domain type, extend
CrudRepository
instead of
Repository
.
首先需要实现一个指定了域模型的Repository 接口类,该类指定了域与ID类型。如果想扩展CRUD方法,可继承CrudRepository 类代替。
Repository与CrudRepository
一般继承
Repository
接口即可,也可以继承
CrudRepository
接口,它默认定义了一些方法。(其实并不是太好用)
Example 3. CrudRepository interface
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
<S extends T> S save(S entity);
Optional<T> findById(ID primaryKey);
Iterable<T> findAll();
long count();
void delete(T entity);
boolean existsById(ID primaryKey);
// … more functionality omitted.
}
Defining query methods
定义查询方法
The repository proxy has two ways to derive a store-specific query from the method name. It can derive the query from the method name directly, or by using a manually defined query.
repository 代理有两种方式获取针对特定数据库的查询方法。要么直接通过方法名获取,要么手工定义(废话)。
Query lookup strategies
查询策略
The following strategies are available for the repository infrastructure to resolve the query.
获取查方法的的策略如下。
Keyword
|
Sample
|
JPQL snippet
|
And
|
findByLastnameAndFirstname
|
… where x.lastname = ?1 and x.firstname = ?2
|
Or
|
findByLastnameOrFirstname
|
… where x.lastname = ?1 or x.firstname = ?2
|
Is,Equals
|
findByFirstname
,
findByFirstnameIs
,
findByFirstnameEquals
|
… where x.firstname = ?1
|
Between
|
findByStartDateBetween
|
… where x.startDate between ?1 and ?2
|
LessThan
|
findByAgeLessThan
|
… where x.age < ?1
|
LessThanEqual
|
findByAgeLessThanEqual
|
… where x.age <= ?1
|
GreaterThan
|
findByAgeGreaterThan
|
… where x.age > ?1
|
GreaterThanEqual
|
findByAgeGreaterThanEqual
|
… where x.age >= ?1
|
After
|
findByStartDateAfter
|
… where x.startDate > ?1
|
Before
|
findByStartDateBefore
|
… where x.startDate < ?1
|
IsNull
|
findByAgeIsNull
|
… where x.age is null
|
IsNotNull,NotNull
|
findByAge(Is)NotNull
|
… where x.age not null
|
Like
|
findByFirstnameLike
|
… where x.firstname like ?1
|
NotLike
|
findByFirstnameNotLike
|
… where x.firstname not like ?1
|
StartingWith
|
findByFirstnameStartingWith
|
… where x.firstname like ?1
(parameter bound with appended
%
)
|
EndingWith
|
findByFirstnameEndingWith
|
… where x.firstname like ?1
(parameter bound with prepended
%
)
|
Containing
|
findByFirstnameContaining
|
… where x.firstname like ?1
(parameter bound wrapped in
%
)
|
OrderBy
|
findByAgeOrderByLastnameDesc
|
… where x.age = ?1 order by x.lastname desc
|
Not
|
findByLastnameNot
|
… where x.lastname <> ?1
|
In
|
findByAgeIn(Collection<Age> ages)
|
… where x.age in ?1
|
NotIn
|
findByAgeNotIn(Collection<Age> ages)
|
… where x.age not in ?1
|
True
|
findByActiveTrue()
|
… where x.active = true
|
False
|
findByActiveFalse()
|
… where x.active = false
|
IgnoreCase
|
findByFirstnameIgnoreCase
|
… where UPPER(x.firstame) = UPPER(?1)
|
Query creation
The query builder mechanism built into Spring Data repository infrastructure is useful for building constraining queries over entities of the repository. The mechanism strips the prefixes
find…By
,
read…By
,
query…By
,
count…By
, and
get…By
from the method and starts parsing the rest of it. The introducing clause can contain further expressions such as a
Distinct
to set a distinct flag on the query to be created. However, the first
By
acts as delimiter to indicate the start of the actual criteria. At a very basic level you can define conditions on entity properties and concatenate them with
And
and
Or
.
创建一般查询方法的关键字写法。不用看完,直接看红字就OK。
Example 16. Query creation from method names
interface PersonRepository extends Repository<User, Long> {
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enables the distinct flag for the query
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
// Enabling ignoring case for an individual property
List<Person> findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}
Special parameter handling
Example 17. Using
Pageable
, Slice and
Sort
in query methods
也就是分页和排序参数。(Page和Slice的区别就像List和Iterator,所以量大的时候用Slice).
Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Pageable pageable);
The first method allows you to pass an
org.springframework.data.domain.Pageable
instance to the query method to dynamically add paging to your statically defined query. A
Page
knows about the total number of elements and pages available.
第一个方法使用
Pageable
来分页。一个Page对象知道元素和页的总数。
It does so by the infrastructure triggering a count query to calculate the overall number. As this might be expensive depending on the store used,
Slice
can be used as return instead. A
Slice
only knows about whether there’s a next
Slice
available which might be just sufficient when walking through a larger result set.
分页可能会消耗较多资源,Slice类可以取代它。一个Slice对象仅仅知道是否有下一页(故资源消耗较低)。
Sorting options are handled through the
Pageable
instance too. If you only need sorting, simply add an
org.springframework.data.domain.Sort
parameter to your method.
Pageable还可以设定排序参数。如果你只是需要排序,可以简单地使用Sort对象作为你方法的参数(很简单吗?)。
As you also can see, simply returning a
List
is possible as well. In this case the additional metadata required to build the actual
Page
instance will not be created (which in turn means that the additional count query that would have been necessary not being issued) but rather simply restricts the query to look up only the given range of entities.
你还能看到,还可以简单地定义仅返回List对象。此时元数据不会别创建,但结果集仍然是以分页后的数据子集返回。
Limiting query results
限制结果集
The results of query methods can be limited via the keywords
first
or
top
Example 18. Limiting the result size of a query with
Top
and
First
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
Async query results
异步查询
Repository queries can be executed asynchronously using
Spring’s asynchronous method execution capability
. This means the method will return immediately upon invocation and the actual query execution will occur in a task that has been submitted to a Spring TaskExecutor.
通过使用
Spring’s asynchronous method execution capability
可以实现
异步查询。方法被调用后会立即返回,实际的查询会被提交到Spring TaskExecutor执行。
@Async
Future<User> findByFirstname(String firstname); ①
@Async
CompletableFuture<User> findOneByFirstname(String firstname); ②
@Async
ListenableFuture<User> findOneByLastname(String lastname); ③
(按:我只能想到异步调用在大批量插入或更新数据时有实用价值。——范培忠)
Using @Query
Example 57. Advanced like-expressions in
@Query
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.firstname like %?1")
List<User> findByFirstnameEndsWith(String firstname);
}
还可以使用本地查询,即以与数据库产品耦合的方式的SQL语句(所谓本地查询,就是使用原生的sql语句(根据数据库的不同,在sql的语法或结构方面可能有所区别)进行查询数据库的操作)(如果想使用select * 这样的写法,就必须用
nativeQuery
)
Example 59. Declare native count queries for pagination at the query method using @Query
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
nativeQuery = true)
Page<User> findByAndSort(String lastname, Pageable pageable);
}
Using named parameters
使用命名的参数
Example 61. Using named parameters
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
User findByLastnameOrFirstname(@Param("lastname") String lastname,
@Param("firstname") String firstname);
}
@Modifying
Of course you can add custom modifying behaviour by using facilities described in
Custom implementations for Spring Data repositories
.
你可以添加自定义的修改方法,详见链接。@Modifying注解还是要和@Query注解搭配使用。
Example 64. Declaring manipulating queries
@Modifying
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);
Derived delete queries
扩展删除方法
Spring Data JPA also supports derived delete queries that allow you to avoid having to declare the JPQL query explicitly.
Example 65. Using a derived delete query
interface UserRepository extends Repository<User, Long> {
void deleteByRoleId(long roleId);
@Modifying
@Query("delete from User u where user.role.id = ?1")
void deleteInBulkByRoleId(long roleId);
}
Transactionality
自定义事务控制
CRUD methods on repository instances are transactional by default. For reading operations the transaction configuration
readOnly
flag is set to
true
, all others are configured with a plain
@Transactional
so that default transaction configuration applies. For details see JavaDoc of
SimpleJpaRepository
. If you need to tweak transaction configuration for one of the methods declared in a repository simply redeclare the method in your repository interface as follows:
CRUD方法已经默认使用了简单的事务。比如在查询时已经将readOnly设为true。其它会设置为普通的 @Transactional。你如果想扩展事务控制,则进行自定义。
Example 98. Custom transaction configuration for CRUD
@Transactional(readOnly = true)
public interface UserRepository extends CrudRepository<User, Long> {
@Override
@Transactional(timeout = 10)
public List<User> findAll();
@Modifying
@Transactional
@Query("delete from User u where u.active = false")
void deleteInactiveUsers();
}
Another possibility to alter transactional behaviour is using a facade or service implementation that typically covers more than one repository. Its purpose is to define transactional boundaries for non-CRUD operations:
还有种可能就是你希望在facade或者service里去修改事务行为。此时通常你会使用多个或多次持久类。
Example 99. Using a facade to define transactions for multiple repository calls
@Service
class UserManagementImpl implements UserManagement {
//...
@Transactional
public void addRoleToAllUsers(String roleName) {
Role role = roleRepository.findByName(roleName);
for (User user : userRepository.findAll()) {
user.addRole(role);
userRepository.save(user);
}
}
//...
}
Note
that you will have to activate
<tx:annotation-driven />
or use
@EnableTransactionManagement
explicitly to get annotation based configuration at facades working.
注意你得在框架配置里激活事务。
Locking
加锁
To specify the lock mode to be used the
@Lock
annotation can be used on query methods:
Example 101. Defining lock metadata on query methods
interface UserRepository extends Repository<User, Long> {
// Plain query method
@Lock(LockModeType.READ)
List<User> findByLastname(String lastname);
}
Creating repository instances
创建持久层实例
In this section you create instances and bean definitions for the repository interfaces defined. One way to do so is using the Spring namespace that is shipped with each Spring Data module that supports the repository mechanism although we generally recommend to use the Java-Config style configuration.
可以用XML创建,现在推荐使用Java Config来创建。
XML configuration
Example 21. Enabling Spring Data repositories via XML
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<repositories base-package="com.acme.repositories" />
</beans:beans>
JavaConfig
The repository infrastructure can also be triggered using a store-specific
@Enable${store}Repositories
annotation on a JavaConfig class. For an introduction into Java-based configuration of the Spring container, see the r
eference documentation.
[
1
]
A sample configuration to enable Spring Data repositories looks something like this.
Example 23. Sample annotation based repository configuration
@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {
@Bean
EntityManagerFactory entityManagerFactory() {
// …
}
}
附注:
1.Repository→CrudRepository→JpaRepository
继承关系如是。JpaRepository 实现了一系列常用方法,继承它可免常用方法定义。
2.若entity某字段不想被映射,则使用@Transient 注解
范培忠 2018-04-26