源码
这就是Repostory
的源码,里边没有任何方法。
package org.springframework.data.repository;
import org.springframework.stereotype.Indexed;
@Indexed
public interface Repository<T, ID> {
}
但是只要继承了这个类,就可以实现JPA的一些默认实现方法。
Repostory类层次关系
从层级关系可以看出来分了四大类,下边简单介绍一下。
ReactiveCrudRepository
这个是响应式编程的一个类,主要支持NoSQL
方面的操作。目前主要支持了Cassandra,MongoDB,Redis的实现。
RxJava2CrudRepository
这个是为了支持RxJava2做的标准响应式编程的接口。
CoroutineCrudRepository
这个是为了支持Kotlin语法而增加的
CrudRepository
这个则是我学习的重点
我们再看一下Repository
的类层级关系图:我把类的作用标注了一下。
Repostory接口
这个接口没有默认方法,也就是说,我们增加了什么方法,就可以使用什么方法。
public interface UserRepository extends Repository<UserInfo,Integer> {
List<UserInfo> findByName(String name);
List<UserInfo> findByAgeAndName(Integer age, String name);
}
当我们添加了两个方法如上代码所示,一个根据name找user,一个根据age和name找用户。
这时,之前写的save和findAll方法就报错了,因为这里边没有这两个方法。
UserRepository
实例通过继承Repository
,使Spring容器知道UserRepository
是操作数据库的类,并可以对数据库进行CRUD操作。
CrudRepository接口
我们将每个方法的作用用注解进行标注,以便更直观的查看。
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
//保存
<S extends T> S save(S var1);
//批量保存
<S extends T> Iterable<S> saveAll(Iterable<S> var1);
//根据主键查询
Optional<T> findById(ID var1);
//根据主键查询是否存在
boolean existsById(ID var1);
//查询所有实体的列表
Iterable<T> findAll();
//根据主键的list列表查询所有实体
Iterable<T> findAllById(Iterable<ID> var1);
//查询总数
long count();
//根据主键删除
void deleteById(ID var1);
//根据实体类对象删除
void delete(T var1);
//根据主键的List删除所有
void deleteAll(Iterable<? extends T> var1);
//根据主键删除
void deleteAll();
}
以上这些方法就是CrudRepository
对外暴露的最常见的接口,当我们的UserRepository
继承CrudRepository
之后,我们就可以使用这些方法操作我们的UserInfo
表了。
这里我们特别看一些save和delete方法
<S extends T> S save(S var1)
;
从源码可以看出,会先判断是否是新的数据,若是新的则新增,若是已有的则修改已有数据。
@Transactional
public <S extends T> S save(S entity) {
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
<S extends T> Iterable<S> saveAll(Iterable<S> var1);
这里可以看出,尽管是saveAll()但也是轮询然后一个一个调save方法保存的,并没有性能上的提升。
@Transactional
public <S extends T> List<S> saveAll(Iterable<S> entities) {
Assert.notNull(entities, "Entities must not be null!");
List<S> result = new ArrayList();
Iterator var3 = entities.iterator();
while(var3.hasNext()) {
S entity = var3.next();
result.add(this.save(entity));
}
return result;
}
void deleteById(ID var1);
delete方法则是先判断了传入的实体是否存在,若是不存在还抛出了错误。
@Transactional
public void deleteById(ID id) {
Assert.notNull(id, "The given id must not be null!");
this.delete(this.findById(id).orElseThrow(() -> {
return new EmptyResultDataAccessException(String.format("No %s entity with id %s exists!", this.entityInformation.getJavaType(), id), 1);
}));
}
PagingAndSortingRepository接口
这个接口继承自CrudRepository
,然后添加了主要用于分页查询和排序查询的两个方法。
源码如下:
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
//排序查询
Iterable<T> findAll(Sort var1);
//分页查询
Page<T> findAll(Pageable var1);
}
这个接口只有两个方法,分别当排序和分页的时候使用。
使用的时候,直接让我们的UserRepository
继承PagingAndSortingRepository
即可。
public interface UserRepository extends PagingAndSortingRepository<UserInfo,Integer> {
}
这时查询的时候,就可以使用分页和排序了
分页
这段代码的意思是,取第一页,每页20个数据,根据name排序。
@GetMapping(path = "users")
@ResponseBody
@WebLog(printResult = true)
public Page<UserInfo> getAllUsers(){
return userRepository.findAll(PageRequest.of(1,20, Sort.by(new Sort.Order(Sort.Direction.ASC,"name"))));
}
排序
这则是根据name字段逆序排序
@GetMapping(path = "users")
@ResponseBody
@WebLog(printResult = true)
public Iterable<UserInfo> getAllUsers(){
return userRepository.findAll(Sort.by(new Sort.Order(Sort.Direction.DESC,"name")));
}