- 此篇博文主要是结合官方文档以及对JPA的一些使用进行记录。
- 对于相关介绍,可参考相应博客。
- 后续会对相关实现机制和核心思想进行深入探讨。
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-jpaartifactId>
dependency>
The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.
建立抽象的 Repository或Dao 从而减少持久层相关代码的编写(模板化的代码,重复的代码)
空接口、标记接口:没有包含方法声明的接口
public interface Repository<T, ID extends Serializable> {
}
/**
* 继承 Repository 接口使得 Spring 容器能对自定义的 PersonRepository 进行管理
*/
public interface PersonRepository extends Repository<Person, Long> {
//定义数据访问操作的方法
}
/**
* 除了继承 Repository 接口以外,还可以通过注解实现 Spring 容器对自定义 repository 进行管理
*/
@RepositoryDefinition(domainClass = Person.class,idClass = Long.class)
public interface PersonRepository {
//定义数据访问操作的方法
}
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
// Saves the given entity.
S save(S entity);
// Returns the entity identified by the given id.
Optional findById(ID primaryKey);
// Returns all entities.
Iterable findAll();
// Returns the number of entities
long count();
// Deletes the given entity.
void delete(T entity);
// Indicates whether an entity with the given id exists.
boolean existsById(ID primaryKey);
// … more functionality omitted.
}
public interface PagingAndSortingRepository<T, ID extends Serializable>
extends CrudRepository<T, ID> {
Iterable findAll(Sort sort);
Page findAll(Pageable pageable);
}
**************************************interface MyBaseRepository****************************
// Make sure you add that annotation to all repository interfaces
// that Spring Data should not create instances for at runtime.
@NoRepositoryBean
interface MyBaseRepository extends Repository {
// Java 8 Optinal refering http://www.importnew.com/6675.html
// 也可以返回其他包装类型
Optional findById(ID id);
S save(S entity);
}
**************************************package-info.java**********************************
@NonNullApi
package tech.shunzi.repository;
import org.springframework.lang.NonNullApi;
**************************************interface UserRepository****************************
package tech.shunzi.repository;
import org.springframework.lang.Nullable;
public interface UserRepository extends MyBaseRepository<User, Long> {
// test success
@Nullable
User findByEmailAddress(@Nullable EmailAddress emailAddress);
}
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
interface Configuration { }
// step 1:Declare an interface extending Repository or one of its subinterfaces and type it to the domain class and ID type that it will handle.
interface PersonRepository extends Repository {
// step 2: Declare query methods on the interface.
List findByLastname(String lastname);
}
step 3:Set up Spring to create proxy instances for those interfaces.
use xml
<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>
use java annotation to config
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
class Config {}
class SomeClient {
private final PersonRepository repository;
SomeClient(PersonRepository repository) {
this.repository = repository;
}
void doSomething() {
List persons = repository.findByLastname("Matthews");
}
}
CREATE
:根据查询方法名称构建,从方法名称中移除一组已知的前缀并解析该方法的其余部分。find…By, read…By, query…By, count…By, get…By, And and Or.
USE_DECLARED_QUERY
:试图找到一个声明的查询(注释或其他方式声明),如果未找到会抛出一个异常。CREATE_IF_NOT_FOUND
:default.它首先查找已声明的查询,并且如果未找到已声明的查询,则会创建一个基于自定义方法名称的查询。这是默认的查找策略,因此如果不明确配置任何内容,将会使用它。它允许通过方法名称快速查询定义,还可以根据需要引入已声明的查询来自定义这些查询。AddressZipCode
,没有该属性,匹配失败,从右往左,匹配Code
和AddressZip
,匹配失败,继续匹配Address
和ZipCode
,匹配成功。对应的则为Address.ZipCode
。List<Person> findByAddressZipCode(ZipCode zipCode);
Person
有addressZip
属性,则会因为addressZip
没有code
属性匹配失败,此时需要使用下划线来消除歧义。–不推荐使用List<Person> findByAddress_ZipCode(ZipCode zipCode);
// A Page knows about the total number of elements and pages available.
Page findByLastname(String lastname, Pageable pageable);
// A Slice only knows about whether there’s a next Slice available
// Slice is suitable for large result set.
Slice findByLastname(String lastname, Pageable pageable);
// Can use sort parameter to sort data.
List findByLastname(String lastname, Sort sort);
// List can avoid extra query for page instance,such as total amount.
List findByLastname(String lastname, Pageable pageable);
To find out how many pages you get for a query entirely you have to trigger an additional count query. By default this query will be derived from the query you actually trigger.
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page queryFirst10ByLastname(String lastname, Pageable pageable);
Slice findTop3ByLastname(String lastname, Pageable pageable);
List findFirst10ByLastname(String lastname, Sort sort);
List findTop10ByLastname(String lastname, Pageable pageable);
@Query("select u from User u")
Stream findAllByCustomQueryAndStream();
Stream readAllByFirstnameNotNull();
@Query("select u from User u")
Stream streamAllPaged(Pageable pageable);
try (Stream stream = repository.findAllByCustomQueryAndStream()) {
stream.forEach(…);
}
// java.util.concurrent.Future
@Async
Future findByFirstname(String firstname);
// Java 8 java.util.concurrent.CompletableFuture
@Async
CompletableFuture findOneByFirstname(String firstname);
// org.springframework.util.concurrent.ListenableFuture
@Async
ListenableFuture findOneByLastname(String lastname);
<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">
<context:exclude-filter type="regex" expression=".*SomeRepository" />
repositories>
beans:beans>
@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {
@Bean
EntityManagerFactory entityManagerFactory() {
// …
}
}
RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);
interface CustomizedUserRepository {
void someCustomMethod(User user);
}
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
public void someCustomMethod(User user) {
// Your custom implementation
}
// you can add some beans according to your demand
@Autowired
private JdbcTemplate jdbcTemplate;
}
// Doing so combines the CRUD and custom functionality
class UserRepository extends CrudRepository, CustomizedUserRepository {
// Declare query methods here
}
注意: 当
CustomRepo
中包含和BaseRepo
(JPA自带Repo)相同的方法时,譬如如下例子的save
,CustomRepo
有更高的优先级,所以可以实现 自定义Repo
对原生的Repo
的相关方法的重写。与此同时,可以结合 泛型 使得自定义Repo
被更多的RepoImpl
类继承实现。
interface CustomizedSave<T> {
S save(S entity);
}
class CustomizedSaveImpl<T> implements CustomizedSave<T> {
public S save(S entity) {
// Your custom implementation
}
}
interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> {
}
interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> {
}
Repo
的相关配置,如果使用 namespace
相关配置,会自动检测扫描指定包下 指定格式的自定义 Repo
,默认后缀为 Impl
,可以通过修改相关配置进行定制。<repositories base-package="com.acme.repository" />
<repositories base-package="com.acme.repository" repository-impl-postfix="FooBar" />
Repo
接口有多个实现,且位于不同的包下时, Spring 将基于 beanName
对 RepoImpl
进行扫描.接口未指定对应的 beanName
时,默认为 接口名 + Impl,指定了Component
时,则对应扫描 Component
对应的 Repo
. 注意:也可以在配置中自定义实例化相关 Bean
package com.acme.impl.one;
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
// Your custom implementation
}
package com.acme.impl.two;
@Component("specialCustomImpl")
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
// Your custom implementation
}
interface UserRepository implements CustomizedUserRepository {
// default matches CustomizedUserRepositoryImpl
// if @Component("specialCustomImpl"),matches CustomizedUserRepositoryImpl
}
结合文档官方文档 Version 2.0.4.RELEASE,
2018-02-19