1. 程式人生 > >springboot學習筆記5(JPA 實現分頁、排序、返回map集合)

springboot學習筆記5(JPA 實現分頁、排序、返回map集合)

   前言

    在相當長的一段時間裡,實現應用程式的資料訪問層非常麻煩。必須編寫太多的模板程式碼來執行簡單的查詢,以及執行分頁。Spring data  JPA旨在通過減少實際需要的工作量來顯著改進資料訪問層的實現。作為開發人員,您可以編寫儲存庫介面,包括自定義查詢程式方法。spring data JPA核心介面是Repository,其它所有介面都在此類的基礎上進行了擴充套件。 下面是JPA關係圖解圖:


 熟悉JPA的幾個API

     CurdRepository提供了增刪改產方法,PagingAndSortingRepositroy增加了分頁查詢方法,JpaRepositroy又增加了例項查詢方法。實際應用中可以有選擇的繼承上面介面即可。上面說過,jpa提供了封裝查詢資料庫模板方法。下面就來一一介紹JPA給我們提供的模板方法:

CurdRepository 

 介面,提供增刪改查基本的功能:

@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);
	
	boolean exists(ID id);
	
	Iterable<T> findAll();

	Iterable<T> findAll(Iterable<ID> ids);
	
	long count();
	
	void delete(ID id);

	void delete(T entity);

	void delete(Iterable<? extends T> entities);

	void deleteAll();
}

PagingAndSortingRepositroy

  介面 ,分頁查詢,和排序:

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
       //分類查詢
	Iterable<T> findAll(Sort sort);
       //分頁查詢
	Page<T> findAll(Pageable pageable);
}

JpaRepositroy

      介面,提供例項查詢方法

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

	
	<S extends T> S saveAndFlush(S entity);

	void deleteInBatch(Iterable<T> entities);
	
	void deleteAllInBatch();

	T getOne(ID id);
         
      //例項查詢
	@Override
	<S extends T> List<S> findAll(Example<S> example);

      //例項查詢,分類排序
	@Override
	<S extends T> List<S> findAll(Example<S> example, Sort sort);
}

SimpleRepository

     實現類,增加了事務註解,進行事務管理,這就不介紹了,講到自定義封裝基類的時候會用到,一般都不會繼承使用這個類.

使用 

       自定義一個PersonRepository介面繼承上述任何一個介面都可以。不需要在介面上加@Repository註解,也不需要寫實現類,即可在service,controller層注入使用,是不是很簡潔。對於一般的修改儲存操作,只要直接呼叫JPA介面提供的方法就可以用了,這裡就不在介紹了。

entity層 

 Person實體類:

@Entity
public class Person {
 
  private Integer id;  
 
  private String name;
  
  private String password;

  .....省略get set方法

}

持久層

PersonRepository 介面

     下面介紹了JPA查詢方法,條件查詢,list集合查詢,分頁查詢,排序查詢,sql語句查詢,指定欄位查詢,例項查詢,基本上包含了實際開發中所需要的查詢方法。

//省略了註釋。
public interface PersonRepository extends JpaRepository<Person, Integer> {
	
       //自定義查詢,AND為jpa 關鍵字,相當於(where x.lastname = ?1 and x.firstname = ?2)
        jpa關鍵字應該都要熟悉
        Person findByNameAndPassword(String name ,String password);

        //指定欄位查詢,只返回name
        PersonName   findById(Integer id); 
        
         //排序查詢,返回list集合
        List<Person> findByNameAndPassword(String name ,String password,Sort sort);
         
         //分頁查詢 查詢計算元素總個數總頁數,資料多的情況下,代價是昂貴的
        Page<Person> findByNameAndPassword(String name ,String password,Pageable pageable);
         //分頁查詢,返回的是一個片段,它只知道下一片段或者上一片段是否可用。
        Slice<Person> findByNameAndPassword(String name,Pageable pageable); 

         //sql查詢。?後面數字,對應方法中引數位置。使用原生sql語句
        @Query("select p from person as p where p.name = ?1 and p.password = ?2 ",nativeQuery = true) 
        Person myfind(String name ,String password);
        
        //例項查詢
        Person findByExample(Example example)
}

 說明: spring data JPA 提供兩種查詢方式,一種就是直接從方法名中派生中查詢(需要遵循它的規範,見下文 JPA 查詢語句關鍵字)上面的例子就是這種方式;另一種就是手動定義查詢了(使用JPA 核心類 EntityManager,下文自定義查詢會介紹)。

Controller 層

@RestController public class TestController  {
	
    @Resource
    private PersonRepository personRepository;
        @GetMapping(value = "/test")
	  public String test(Integer personId){
        
       Person  p = personRepository.findByNameAndPassword("張三","123");
  	
      //排序
      List<Person> p1 = personRepository.findByNameAndPassword("張三","123",
			new Sort(Sort.Direction.ASC,"id"));
      //分頁
      Page<Person> p2 = personRepository.findByNameAndPassword("張三",
				"123", new PageRequest(1,10,new Sort("id")));
      
       Person tempPerson=new Person();
       tempPerson.setId(personId));
       //通過例項查詢 
       Person p3= personRepository.findByExample(Example.of(tempPerson))     
        
		return    p+"+"+ p1 "+"+p2.getTotalElements()+"+"+p3;
	}

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 findByFirstname,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<Age> ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection<Age> 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)

 自定義查詢(返回map集合)

       上面的例子都是一些簡單的查詢,但實際開發中,我們可能會需要用到複雜的sq、連線查詢或者需要查詢結果只包含我們需要的欄位。如果實體之間的關聯關係都配置得當的話,JPA提供的方法也可以滿足我們的需求。當然我們也可以自定義一個繼承JPA的模板類,然後封裝查詢的方法:

BaseRepository

建立一個BaseRepository ,定義兩個sq語句查詢:

/**
 * repository 基類,封裝自定義查詢方法
 *
 * @author 
 * @date 2017/5/12 8:32
 * @Package com.base
 * @Version v1.0
 */
@NoRepositoryBean //該註解表示 spring 容器不會建立該物件
public interface BaseRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>,JpaRepository<T,ID> {
       
    /**
     * sql查詢
     *
     * @param sql
     * @param args
     * @return
     */
    List<Map> findAllByParams(String sql, Object... args);

  /**
     * sql分頁查詢
     *
     * @param sql
     * @param args
     * @return
     */
    Page<Map> findPageByParams(String sql, Pageable pageable, Object... args);


}

BaseRepositoryImpl

BaseRepositoryImpl 類,實現BaseRepository 介面:

/**
 * @author 
 * @date 2017/5/12 8:32
 * @Package com.base.impl
 * @Version v1.0
 */
public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> {
    
    //實體管理類,對持久化實體做增刪改查,自動義sq操作模板所需要的核心類
    public final EntityManager entityManager;

    public BaseRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Map> findAllByParms(String sql, Object... args) {
        //獲取session
        Session session = (Session) entityManager.getDelegate();
        org.hibernate.Query q = session.createSQLQuery(sql);
         //查詢結果轉map        
        q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
        int i = 0;
        for (Object arg : args
                ) {
            q.setParameter(i++, arg);
        }
        return q.list();
    }

     @Override
     @Transactional(rollbackFor = Exception.class)
    public Page<Map> findPageByParams(String sql, Pageable pageable, Object... args) {
        Session session = (Session) entityManager.getDelegate();
        org.hibernate.Query q = session.createSQLQuery(sql);
        q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
        int i = 0;
        for (Object arg : args
                ) {
            q.setParameter(i++, arg);
        }

        List<Map> totalCount = q.list();
        q.setFirstResult(pageable.getPageSize() * (pageable.getPageNumber() - 1));
        q.setMaxResults(pageable.getPageSize());
        List<Map> pageCount = q.list();
        Page<Map> pages = new PageImpl<>(pageCount, pageable, totalCount.size());
        return pages;
    }

}

PersonRepositroy

建立一個PersonRepositroy 繼承BaseRepository,就可以直接使用定義的查詢方法了:

/**
 * @author 
 * @date 2017/5/12 10:07
 * @Package com.repository
 * @Version v1.0
 */
public interface PersonRepository extends BaseRepository<Person, Integer> {

}

TestController 

@RestController public class TestController  {
	
    @Resource
    private PersonRepository personRepository;

   @GetMapping(value = "/test")
   public String test( ){
        //訂單表與使用者表關聯,通過使用者ID查詢該使用者的所有訂單,只獲取訂單編號和訂單詳情。
       String sql="select o.no,o.detail from person as p inner join order as o on o.personid=p.id and p.id= ? "
      Integer userId=11;
      Page<Map>  orderLists = personRepository.findPageByParams(sql,new PageRequest(1,10),userId);
  
		return   orderLists;
	}
}
最後,在專案啟動類上加一個註解,告訴springboot,我要使用JPA,請到repository包下去掃描我建立的repository 類,我的repository基類是BaseRepositoryImpl:
@EnableJpaRepositories(basePackages = {"com.repository"}, repositoryBaseClass = BaseRepositoryImpl.class)
public class MybootApplication {
 
public static void main(String[] args) {
		SpringApplication.run(MybootApplication.class, args);
	}

}

這樣啟動專案,就可以進行對資料庫訪問了。上面的例子主要是解決如何使用sq語句對資料庫操作,對於其他情況,大家可以自己嘗試。