1。核心概念
Spring Data存储库抽象中的中心接口是Repository
。它需要管理域类以及域类的ID类型作为类型参数。该接口主要作为标记接口来捕获要使用的类型,并帮助您发现扩展该接口的接口。该CrudRepository
规定对于正在管理的实体类复杂的CRUD功能。
通常情况下,你的资料库接口扩展Repository
,CrudRepository
或PagingAndSortingRepository
。
Repository:不公开,
CrudRepository:如果您想公开该域类型的CRUD方法,请扩展CrudRepository
而不是Repository
。也就是在CrudRepository都是通用的方法,其它也可以使用。
PagingAndSortingRepository:分页查询,可以集成Repository也可以继承CrudRepository,如果继承Repository说明是私有的,如果继承CrudRepository说明是通用的。
CrudRepository
接口
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> { //保存给定的实体。 <S extends T> S save(S entity); //返回由给定ID标识的实体。 Optional<T> findById(ID primaryKey); //返回所有实体。 Iterable<T> findAll(); //返回实体的数量。 long count(); //删除给定的实体。 void delete(T entity); //指示是否存在具有给定ID的实体。 boolean existsById(ID primaryKey); //下面还有很多方法,比如派生查询数量,派生删除等。。。 // … more functionality omitted. }
除此之外CrudRepository
,还有一个PagingAndSortingRepository
抽象增加了其他方法来简化对实体的分页访问:
PagingAndSortingRepository
接口集成上面通用接口CrudRepository实现分页查询
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> { Iterable<T> findAll(Sort sort); Page<T> findAll(Pageable pageable); }
要访问User
页面大小为20 的第二页,您可以执行以下操作:
PagingAndSortingRepository<User, Long> repository = // … get access to a bean Page<User> users = repository.findAll(new PageRequest(1, 20));
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);
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);
除了查询方法外,count和delete查询的查询派生都是可用的。以下列表显示派生计数查询的接口定义:
interface UserRepository extends CrudRepository<User, Long> { long countByLastname(String lastname); }
以下列表显示派生删除查询的接口定义:
interface UserRepository extends CrudRepository<User, Long> { long deleteByLastname(String lastname); List<User> removeByLastname(String lastname); }
4.2。查询方法
标准CRUD功能存储库通常会在底层数据存储上进行查询。使用Spring Data,声明这些查询变成了一个四步过程:
声明一个扩展Repository或其子接口的接口,如果您想公开该域类型的CRUD方法,请扩展
CrudRepository
而不是Repository
。并将其键入它应该处理的域类和ID类型,在接口上声明查询方法,如以下示例所示:-
interface PersonRepository extends Repository<Person, Long> { List<Person> findByLastname(String lastname); }
-
设置Spring以使用JavaConfig或XML配置为这些接口创建代理实例。
-
要使用Java配置,请创建类似于以下的类:
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @EnableJpaRepositories class Config {}
-
中间存储库接口用注释@NoRepositoryBean。确保将该注释添加到Spring Data不应在运行时为其创建实例的所有存储库接口。
@NonNullApi
:在包级别上用于声明参数和返回值的默认行为是不接受或生成null
值。@NonNull
:用于参数或返回值,不能是null
(参数不需要,返回值@NonNullApi
适用)。@Nullable
:用于可以是的参数或返回值null
。
关键词 | 样品 | JPQL片段 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
常用注解
@MappedSuperclass:
@NoRepositoryBean:选择性的暴露库
@NamedQuery注解,大致意思就是让我们在Repository接口中定义的findByName方法不使用默认的查询实现,取而代之的是使用这条自定义的查询语句去查询,如果这里没有标注的话,会使用默认实现的。
总结性思考:
1.EntityManager
我们都知道,在使用持久化工具的时候,一般都有一个对象来操作数据库,在原生的Hibernate中叫做Session,在JPA中叫做EntityManager,在MyBatis中叫做SqlSession,通过这个对象来操作数据库。
我们一般按照三层结构来看的话,Service层做业务逻辑处理,Dao层和数据库打交道,在Dao中,就存在着上面的对象。那么ORM框架本身提供的功能有什么呢?答案是基本的CRUD,使用Spring-data-jpa进行开发的过程中,常用的功能,我们几乎不需要写一条sql语句。
2.Entity其实就是实体管理器,注意每一个实体类必须要有主见id,需要加入@Id的注解,否则就会报错说实体类没有定义。
3.dao
数据库访问对象,在jpa当中,有一个词语叫Repository,这里我们一般就用Repository结尾来表示这个dao,比如UserDao,这里我们使用UserRepository,同理,在mybatis中我们一般也不叫dao,mybatis由于使用xml映射文件,我们一般使用mapper结尾,比如我们也不叫UserDao,而叫UserMapper。
首先base-package属性,代表你的Repository接口的位置,repository-impl-postfix属性代表接口的实现类的后缀结尾字符,比如我们的UserRepository,那么他的实现类就叫做UserRepositoryImpl,和我们平时的使用习惯完全一致。
比如:我们的UserRepository和UserRepositoryImpl这两个类就像下面这样来写。
public interface UserRepository extends JpaRepository<User, Integer>{} public class UserRepositoryImpl {}
那么这里为什么要这么做呢?原因是:spring-data-jpa提供基础的CRUD工作,同时也提供业务逻辑的功能,所以我们的Repository接口要做两项工作,继承spring-data-jpa提供的基础CRUD功能的接口,比如JpaRepository接口,同时自己还需要在UserRepository这个接口中定义自己的方法,那么导致的结局就是UserRepository这个接口中有很多的方法,那么如果我们的UserRepositoryImpl实现了UserRepository接口,导致的后果就是我们势必需要重写里面的所有方法,这是Java语法的规定,如此一来,悲剧就产生了,UserRepositoryImpl里面我们有很多的@Override方法,这显然是不行的,结论就是,这里我们不用去写implements部分。原因是:这个过程中cglib发挥了杰出的作用,在spring-data-jpa内部,有一个类,叫做
public class SimpleJpaRepository<T, ID extends Serializable> implements JpaRepository<T, ID>, JpaSpecificationExecutor<T>
我们可以看到这个类是实现了JpaRepository接口的,事实上如果我们按照上面的配置,在同一个包下面有UserRepository,但是没有UserRepositoryImpl这个类的话,在运行时期UserRepository这个接口的实现就是上面的SimpleJpaRepository这个接口。而如果有UserRepositoryImpl这个文件的话,那么UserRepository的实现类就是UserRepositoryImpl,而UserRepositoryImpl这个类又是SimpleJpaRepository的子类,如此一来就很好的解决了上面的这个不用写implements的问题。我们通过阅读这个类的源代码可以发现,里面包装了entityManager,底层的调用关系还是entityManager在进行CRUD。
4.相应的Repository的说明
JpaRepository实现了PagingAndSortingRepository接口,PagingAndSortingRepository接口实现了CrudRepository接口,
CrudRepository接口实现了Repository接口;
Repository接口是一个标识接口,里面是空的;
Repository接口
这个接口是最基础的接口,只是一个标志性的接口,没有定义任何的方法,那这个接口有什么用了?既然Spring data JPA提供了这个接口,自然是有它的用处,例如,我们有一部分方法是不想对外提供的,比如我们只想提供增加和修改方法,不提供删除方法,那么前面的几个接口都是做不到的,这个时候,我们就可以继承这个接口,然后将CrudRepository接口里面相应的方法拷贝到Repository接口就可以了。
总结:上述五个接口,开发者到底该如何选择?其实依据很简单,根据具体的业务需求,选择其中之一。因为各个接口之间并不存在功能强弱的问题。
CrudRepository接口定义了增删改查方法;
- @NoRepositoryBean
- public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
- <S extends T> S save(S entity);//保存
- <S extends T> Iterable<S> save(Iterable<S> entities);//批量保存
- T findOne(ID id);//根据id查询一个对象
- boolean exists(ID id);//判断对象是否存在
- Iterable<T> findAll();//查询所有的对象
- Iterable<T> findAll(Iterable<ID> ids);//根据id列表查询所有的对象
- long count();//计算对象的总个数
- void delete(ID id);//根据id删除
- void delete(T entity);//删除对象
- void delete(Iterable<? extends T> entities);//批量删除
- void deleteAll();//删除所有
- }
PagingAndSortingRepository接口用于分页和排序;
- @NoRepositoryBean
- public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
- Iterable<T> findAll(Sort sort);// 不带分页的排序
- Page<T> findAll(Pageable pageable);// 带分页的排序
- }
JpaRepository接口继承了以上所有接口,所以拥有它们声明的所有方法;
- public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
- List<T> findAll();//查询所有对象,不排序
- List<T> findAll(Sort sort);//查询所有对象,并排序
- <S extends T> List<S> save(Iterable<S> entities);//批量保存
- void flush();//强制缓存与数据库同步
- T saveAndFlush(T entity);//保存并强制同步
- void deleteInBatch(Iterable<T> entities);//批量删除
- void deleteAllInBatch();//删除所有
- }
JpaSpecificationExecutor接口,不属于Repository体系,实现一组JPA Criteria查询相关的方法。
- public interface JpaSpecificationExecutor<T> {
Optional<T> findOne(@Nullable Specification<T> var1);
List<T> findAll(@Nullable Specification<T> var1);
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
List<T> findAll(@Nullable Specification<T> var1, Sort var2);
long count(@Nullable Specification<T> var1);
}
该接口提供了对JPA Criteria(标准)查询的支持。Spring data JPA不会自动扫描识别,所以会报找不到对应的Bean,我们只需要继承任意一个继承了Repository的子接口或直接继承Repository接口,Spring data JPA就会自动扫描识别,进行统一的管理。
编写接口如下:
- public interface SpecificationExecutorRepository extends CrudRepository<User, Integer>, JpaSpecificationExecutor<User> {
- }
Service 类:
- @Service
- public class SpecificationExecutorRepositoryManager {
- @Autowired
- private SpecificationExecutorRepository dao;
- /**
- * 描述:根据name来查询用户
- */
- public User findUserByName(final String name){
- return dao.findOne(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
- Predicate predicate = cb.equal(root.get("name"), name);
- return predicate;
- }
- });
- }
- /**
- * 描述:根据name和email来查询用户
- */
- public User findUserByNameAndEmail(final String name, final String email){
- return dao.findOne(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- List<Predicate> list = new ArrayList<Predicate>();
- Predicate predicate1 = cb.equal(root.get("name"), name);
- Predicate predicate2 = cb.equal(root.get("email"), email);
- list.add(predicate1);
- list.add(predicate2);
- // 注意此处的处理
- Predicate[] p = new Predicate[list.size()];
- return cb.and(list.toArray(p));
- }
- });
- }
- /**
- * 描述:组合查询
- */
- public User findUserByUser(final User userVo){
- return dao.findOne(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- Predicate predicate = cb.equal(root.get("name"), userVo.getName());
- cb.and(predicate, cb.equal(root.get("email"), userVo.getEmail()));
- cb.and(predicate, cb.equal(root.get("password"), userVo.getPassword()));
- return predicate;
- }
- });
- }
- /**
- * 描述:范围查询in方法,例如查询用户id在[2,10]中的用户
- */
- public List<User> findUserByIds(final List<Integer> ids){
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- return root.in(ids);
- }
- });
- }
- /**
- * 描述:范围查询gt方法,例如查询用户id大于9的所有用户
- */
- public List<User> findUserByGtId(final int id){
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- return cb.gt(root.get("id").as(Integer.class), id);
- }
- });
- }
- /**
- * 描述:范围查询lt方法,例如查询用户id小于10的用户
- */
- public List<User> findUserByLtId(final int id){
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- return cb.lt(root.get("id").as(Integer.class), id);
- }
- });
- }
- /**
- * 描述:范围查询between方法,例如查询id在3和10之间的用户
- */
- public List<User> findUserBetweenId(final int start, final int end){
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- return cb.between(root.get("id").as(Integer.class), start, end);
- }
- });
- }
- /**
- * 描述:排序和分页操作
- */
- public Page<User> findUserAndOrder(final int id){
- Sort sort = new Sort(Direction.DESC, "id");
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- return cb.gt(root.get("id").as(Integer.class), id);
- }
- }, new PageRequest(0, 5, sort));
- }
- /**
- * 描述:只有排序操作
- */
- public List<User> findUserAndOrderSecondMethod(final int id){
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- cb.gt(root.get("id").as(Integer.class), id);
- query.orderBy(cb.desc(root.get("id").as(Integer.class)));
- return query.getRestriction();
- }
- });
- }
- }