CrudRepository 接口

CrudRepository 为我们提供的方法有:

count(): long 查询总数返回 long 类型;
void delete(T entity) 根据 entity 进行删除;
void deleteAll(Iterable<? extends T> entities) 批量删除;
void deleteAll() 删除所有;
void deleteById(ID id); 根据主键删除,查看源码会发现,其是先查询出来再进行删除;
boolean existsById(ID id) 根据主键判断实体是否存在;
Iterable< T > findAllById(Iterable ids); 根据主键列表查询实体列表;
Iterable< T > findAll(); 查询实体的所有列表;
Optional< T > findById(ID id); 根据主键查询实体,返回 JDK 1.8 的 Optional,这可以避免 null exception;
< S extends T > S save(S entity); 保存实体方法,参数和返回结果可以是实体的子类;
saveAll(Iterable< S > entities) : 批量保存,原理和 save方法相同,我们去看实现的话,就是 for 循环调用上面的 save 方法。

我们需要注意一下 save 和 deleteById 的实现逻辑,分别看看一下这两种方法是怎么实现的:

//新增或者保存

public <S extends T> S save(S entity) {

   if (entityInformation.isNew(entity)) {

      em.persist(entity);

      return entity;

   } else {

      return em.merge(entity);

   }

}

//删除

public void deleteById(ID id) {

   Assert.notNull(id, ID_MUST_NOT_BE_NULL);

   delete(findById(id).orElseThrow(() -> new EmptyResultDataAccessException(

         String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1)));

}

看上面的源码,会通过 findById 先查询一下实体对象的 ID,然后再去对查询出来的实体对象进行保存操作。而如果在 Delete 的时候,查询到的对象不存在,则直接抛异常。
关于 entityInformation.isNew(entity),如果当传递的参数里面没有 ID,则直接 insert;若当传递的参数里面有 ID,则会触发 select 查询。此方法会去看一下数据库里面是否存在此记录,若存在,则 update,否则 insert。

PagingAndSortingRepository 接口

PagingAndSortingRepository 源码发现有两个方法,分别是用于分页和排序的时候使用的,如下所示:

package org.springframework.data.repository;

import org.springframework.data.domain.Page;

import org.springframework.data.domain.Pageable;

import org.springframework.data.domain.Sort;

@NoRepositoryBean

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {

	Iterable<T> findAll(Sort sort); (1)

	Page<T> findAll(Pageable pageable); (2)

}

其中,第一个方法 findAll 参数是 Sort,是根据排序参数,实现不同的排序规则获取所有的对象的集合;第二个方法 findAll 参数是 Pageable,是根据分页和排序进行查询,并用 Page 对返回结果进行封装。而 Pageable 对象包含 Page 和 Sort 对象。
PagingAndSortingRepository 使用案例:

public interface UserJpaRepository extends PagingAndSortingRepository<User,Integer> {
}
@GetMapping(path = "/page")
    @ResponseBody
    public Page<User> getAllUserByPage() {
        return userRepository.findAll(
                PageRequest.of(1, 20, Sort.by(new Sort.Order(Sort.Direction.ASC,"name"))));
    }

    @GetMapping(path = "/sort")
    @ResponseBody
    public Iterable<User> getAllUsersWithSort() {
        return userRepository.findAll(Sort.by(new Sort.Order(Sort.Direction.ASC,"name")));
    }

JpaRepository接口

JpaRepository 是对关系型数据库进行抽象封装,从类图可以看出来它继承 PagingAndSortingRepository 类,也就继承了其所有方法,并且其实现类也是 SimpleJpaRepository。JpaRepository还继承和拥有了 QueryByExampleExecutor 的相关方法。
样例见:Spring Boot集成Spring Data Jpa

Repository 的实现类 SimpleJpaRepository

关系数据库的所有 Repository 接口的实现类就是 SimpleJpaRepository,如果有些业务场景需要进行扩展了,可以继续继承此类。
SimpleJpaRepository 是 Repository 接口、CrudRepository 接口、PagingAndSortingRepository 接口、JpaRepository 接口的实现。其中,SimpleJpaRepository 的部分源码如下:

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
	private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!";
	private final JpaEntityInformation<T, ?> entityInformation;
	private final EntityManager em;
	private final PersistenceProvider provider;
	private @Nullable CrudMethodMetadata metadata;
	......
	@Transactional
	public void deleteAllInBatch() {
		em.createQuery(getDeleteAllQueryString()).executeUpdate();
	}
	......

通过此类的源码,我们可以挺清晰地看出 SimpleJpaRepository 的实现机制,是通过 EntityManger 进行实体的操作,而 JpaEntityInforMation 里面存在实体的相关信息和 Crud 方法的元数据等。UserRepository 的实现类是 Spring 启动的时候,利用 Java 动态代理机制帮我们生成的实现类,而真正的实现类就是 SimpleJpaRepository。