使用过 JPA 的同学比较了解在 Spring 体系的 web 服务开发中,有个比较方便的 dao 工具 repository,并伴随着 Entity 形成的一系列方法能大大加快开发进程,本系列就以 Spring Data 系列逐步讲解该体系的架构和源码实现。
核心概念
repository
Spring data 的中心接口。它需要 Domain 类以及Domain 类的标识符类型作为类型参数。必须有主键。CrudRepository和ListCrudRepository接口为所管理的实体类提供了复杂的CRUD功能。
public interface CrudRepository<T, ID> 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.
}
PagingAndSortingRepository 和 ListPagingAndSortingRepository 提供分页功能。
public interface PagingAndSortingRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(PageRequest.of(1, 20));
定义Repository接口
定义 Repository 接口需要继承 Repository 并将泛型指定为 Domain 类和该类的主键。需要 crud 的继承 CrudRepository 或者它的派生类。
Repository 微调
接口的微调可以通过继承 CrudRepository ListCrudRepository 或者 @RepositoryDefinition 自己定义。如果想写个基类供继承,可以添加 @NoRepositoryBean 注解:
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends Repository<T, ID> {
Optional<T> findById(ID id);
<S extends T> S save(S entity);
}
interface UserRepository extends MyBaseRepository<User, Long> {
User findByEmailAddress(EmailAddress emailAddress);
}
JPA 配置通过 EnableJpaRepositories 注解开启:
@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {
@Bean
EntityManagerFactory entityManagerFactory() {
// …
}
}
投影
投影是在进行性能优化非常重要的部分,一般的 sql 规范都会讲禁止使用 select * 来查询,而一般印象里 JPA 或者 hibernate相关的dao层技术里都会把select 将所有的字段进行平铺。这里的投影就给了我们选择查询字段的机会。
class Person {
@Id UUID id;
String firstname, lastname;
Address address;
static class Address {
String zipCode, city, street;
}
}
interface PersonRepository extends Repository<Person, UUID> {
Collection<Person> findByLastname(String lastname);
}
投影选择:
interface NamesOnly {
String getFirstname();
String getLastname();
}
interface PersonRepository extends Repository<Person, UUID> {
Collection<NamesOnly> findByLastname(String lastname);
}
动态投影:
interface PersonRepository extends Repository<Person, UUID> {
<T> Collection<T> findByLastname(String lastname, Class<T> type);
}
void someMethod(PersonRepository people) {
Collection<Person> aggregates =
people.findByLastname("Matthews", Person.class);
Collection<NamesOnly> aggregates =
people.findByLastname("Matthews", NamesOnly.class);
}