浅谈Springboot之整合JPA

最近学习了模仿天猫商城项目,对springboot项目中的JPA做了一些了解,做一些必要的笔记,一来是对自己学习的知识的巩固,二来对有同样问题的人有参考作用



一 概述

1 概念

   JPA是一种规范,而hibernate是实现这种规范的底层实现,spring data 对持久化接口JPA再抽象一层,针对持久层业务再进一步简化。这样开发者就连持久层的业务逻辑也不用写了,只要按照spring data的命名规范,写好接口继承即可。

2 使用场景

根据我自己的开发经验(其实也谈不上经验,本人小菜一匹 )

UserDao extends JpaRepository<User,Integer>
需求难易程度 选用方式 描述 举例
简单 JPA自带的方法 能够解决大部分的需求 (CURD) userDao.save(user)
复杂 自定义 @Query() 主要用来解决复杂查询 @Query(nativeQuery = true,value = “select r.name from user_roles urs inner join role r on urs.uid = ?1 and urs.rid = r.id;”)
特殊需求 实现接口Specification 一些非常复杂或自定义的查询 多条件动态查询(下文有代码)

二 主要接口

1 Repository

根接口,其他接口继承该接口。源码如下:

/**
 * Central repository marker interface. Captures the domain type to manage as well as the domain type's id type. General
 * purpose is to hold type information as well as being able to discover interfaces that extend this one during
 * classpath scanning for easy Spring bean creation.
 * <p>
 * Domain repositories extending this interface can selectively expose CRUD methods by simply declaring methods of the
 * same signature as those declared in {@link CrudRepository}.
 * 
 * @see CrudRepository
 * @param <T> the domain type the repository manages
 * @param <ID> the type of the id of the entity the repository manages
 * @author Oliver Gierke
 */
public interface Repository<T, ID extends Serializable> {

}

   基础的 Repository 提供了最基本的数据访问功能,其几个子接口则扩展了一些功能。

2 CrudRepository

   基本的增删改查接口,提供了最基本的对实体类的添删改查操作,其源码如下:

/** Interface for generic CRUD operations on a repository for a specific type.*/
@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> 
		extends Repository<T, ID> {

/* 保存单个实体:实体存在时进行update操作;实体不存在时进行insert操作 */
	<S extends T> S save(S entity);

	/* 保存实体集合*/
	<S extends T> Iterable<S> save(Iterable<S> entities);

	/* 根据id查找实体.*/
	T findOne(ID id);

	/** 根据id判断实体是否存在*/
	boolean exists(ID id);

	/* 查询所有实体,不用或慎用!  */
	Iterable<T> findAll();

	/* 根据id集合查找实体集合*/
	Iterable<T> findAll(Iterable<ID> ids);

	/* 查询实体数量*/
	long count();

	/** 根据id删除实体 */
	void delete(ID id);

	/** 删除给定的实体*/
	void delete(T entity);

	/** 删除给定的实体集合*/
	void delete(Iterable<? extends T> entities);
	
	/* 删除所有实体,不用或慎用! */
	void deleteAll();
}

继承 Repository,实现了一组 CRUD 相关的方法 。

3 PagingAndSortingRepository

增加了分页和排序操作,源码示例如下:

/**
 * Extension of {@link CrudRepository} to provide additional methods to retrieve entities using the pagination and
 * sorting abstraction.
 */
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {

	/* 根据Sort返回所有实体排序过后的集合*/
	Iterable<T> findAll(Sort sort);

	/* 根据Pageable返回当前页的数据信息*/
	//通常会使用PageRequest
	Page<T> findAll(Pageable pageable);
}

继承 CrudRepository,实现了一组分页排序相关的方法,不过没法实现带查询条件的分页 。

4 JpaRepository

添加了批量操作,并从写了了父接口一些方法的返回类型,源码示例如下:

@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable>
		extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

	/*
	 *查询所有实体
	 */
	List<T> findAll();

	/*
	 * 根据sort查找所有实体---返回的实体集合是排序过的
	 */
	List<T> findAll(Sort sort);

	/*
	 *根据id集合查找对应的实体集合
	 */
	List<T> findAll(Iterable<ID> ids);

	/*
	 * 保存实体集合
	 */
	<S extends T> List<S> save(Iterable<S> entities);

	/**
	 * 刷新缓存,与数据库同步
	 */
	void flush();

	/* 保存实体并立即刷新缓存,即强制执行持久化*/
	<S extends T> S saveAndFlush(S entity);

	/**
	 * 删除一个实体集合
	 */
	void deleteInBatch(Iterable<T> entities);

	/**
	 * 删除所有的实体,禁用或慎用!
	 */
	void deleteAllInBatch();

	/*根据id获取一个实体
	 */
	T getOne(ID id);

	/* (non-Javadoc)
	 * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)
	 */
	@Override
	<S extends T> List<S> findAll(Example<S> example);

	/* (non-Javadoc)
	 * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
	 */
	@Override
	<S extends T> List<S> findAll(Example<S> example, Sort sort);

}

继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法 。

5 JpaSpecificationExecutor

用来做动态查询,可以实现带查询条件的分页,源码示例如下:

/*Interface to allow execution of {@link Specification}s based on the JPA criteria API.
*/
public interface JpaSpecificationExecutor<T> {

	/*根据查询条件返回一个实体*/
	T findOne(Specification<T> spec);

	/* 根据查询条件返回多个实体.*/
	List<T> findAll(Specification<T> spec);

	/*根据查询条件和分页参数,返回当前页的实体信息.*/
	Page<T> findAll(Specification<T> spec, Pageable pageable);

	/*根据查询条件和排序规则,返回一个排序好的实体集合. */
	List<T> findAll(Specification<T> spec, Sort sort);

	/**
	 *根据查询条件统计实体的数量 */
	long count(Specification<T> spec);
}

不属于Repository体系,实现一组 JPA Criteria 查询相关的方法 。

6 Specification

Specification封装 JPA Criteria 查询条件。通常使用匿名内部类的方式来创建该接口的对象。

Spring Data JPA提供的一个查询规范,要做复杂的查询,只需围绕这个规范来设置查询条件即可。

源码示例如下:

public interface Specification<T> {

	/**
	 * Creates a WHERE clause for a query of the referenced entity in form of a {@link Predicate} for the given
	 * {@link Root} and {@link CriteriaQuery}.
	 * 
	 * @param root
	 * @param query
	 * @return a {@link Predicate}, may be {@literal null}.
	 */
	Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}

三 应用举例

1 简单JPA自带方法
  • dao层
    public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
        List<User> findAllByName(String userName,Pageable pageable);
    }
    
  • service层
    //添加用户
    public void add(User user){
        userDao.save(user);
    }
    //根据用户名进行分页查找
    Public List<User> findAllByUserName(String userName,Pageable pageable){
        Return userDao. findAllByName(username,pageable);
    }
    

   仅仅在UserDao接口中定义抽象方法 List findAllByName(String userName,Pageable pageable); JPA会根据我们自定义的方法名进行解析和拆分,得知该方法的功能。JPA对自定义方法名的解析规则如下:

Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age ⇐ ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection age) … where x.age not in ?1
TRUE findByActiveTrue() … where x.active = true
FALSE findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)
2 复杂自定义@Query
  • dao层
    public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
    
    List<User> findAllByName(String userName,Pageable pageable);
    
        @Query(nativeQuery = true,value = "select r.name from user_roles urs inner join role r on urs.uid = ?1 and urs.rid = r.id;")
        List<String> getRolesByUser(Integer uid);
    
    }
    
  • service层
    //根据userId查询用户对应的角色
    public List<String> getRolesByUser(Integer uid){
        return userDao.getRolesByUser(uid);
    }
    
3 特殊需求实现接口Specification
  • dao层
    public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
    
    List<User> findAllByName(String userName,Pageable pageable);
    
        @Query(nativeQuery = true,value = "select r.name from user_roles urs inner join role r on urs.uid = ?1 and urs.rid = r.id;")
        List<String> getRolesByUser(Integer uid);
        //多条件动态查询
    	List<User> findAll(Specification mySpec);
    
    }
    
  • service层
       /*
    动态查询的条件:
        条件1:性别为 男;
        条件2:年龄在25-35之间;
        条件3:吴国人;
     */
    public List<User> findByDynamicCases() {
    
        return userDao.findAll( new Specification() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Predicate predicate1,predicate2,predicate3;
    
                Path sex = root.get("sex");
                Path age = root.get("age");
                Path address = root.get("address");
    
                predicate1 = cb.like(sex,"男");
                predicate2 = cb.between(age,25,35);
                predicate3 = cb.equal(address,"吴国");
    
                query.where(predicate1,predicate2,predicate3);
                return null;
            }
        });
    }
    

匿名内部类有几个对象需要解释下:

  • Specification:规则、标准。该对象主要是告诉JPA查询的过滤规则是什么。
  • Predicate:谓语、断言。该对象主要是定义具体的判断条件。如predicate1 = cb.like(sex,“男”);即判断条件为性别为男性。
  • Root: Root root就是定义引用root指向Student的包装对象。Path sex = root.get(“sex”);即通过root来获取Student的具体属性。
  • CriteriaQuery:查询条件的组装。query.where(predicate1,predicate2,predicate3);表示按条件predicate1 and predicate2 and predicate3进行组合条件查询。
  • CriteriaBuilder:用来构建CritiaQuery的构建器对象;如:predicate2 = cb.between(age,25,35);表示判断条件为Student.age between 25 and 35;

四 总结

   如有错误恳请指正,如有侵权请联系我删除
   参考文章: Spring Data JPA入门简解与XML配置实现
                   Springboot整合JPA以及动态条件查询的实现

猜你喜欢

转载自blog.csdn.net/qq_39007083/article/details/105278749
今日推荐