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語句對資料庫操作,對於其他情況,大家可以自己嘗試。