Jpa動態查詢常用方法
使用Spring data JPA開發已經有一段時間了,這期間學習了一些東西,也遇到了一些問題,在這裡和大家分享一下。
前言:
Spring data簡介:
Spring Data是一個用於簡化資料庫訪問,並支援雲服務的開源框架。其主要目標是使得對資料的訪問變得方便快捷,並支援map-reduce框架和雲端計算資料服務。 Spring Data 包含多個子專案:
Commons - 提供共享的基礎框架,適合各個子專案使用,支援跨資料庫持久化
JPA - 簡化建立 JPA 資料訪問層和跨儲存的持久層功能
Hadoop - 基於 Spring 的 Hadoop 作業配置和一個 POJO 程式設計模型的
Key-Value - 集成了 Redis 和 Riak ,提供多個常用場景下的簡單封裝
Document - 整合文件資料庫:CouchDB 和 MongoDB 並提供基本的配置對映和資料庫支援
Graph - 整合 Neo4j 提供強大的基於 POJO 的程式設計模型
Graph Roo AddOn - Roo support for Neo4j
JDBC Extensions - 支援 Oracle RAD、高階佇列和高階資料型別
Mapping - 基於 Grails 的提供物件對映框架,支援不同的資料庫
Examples - 示例程式、文件和圖資料庫
Guidance - 高階文件
一、Spring data JPA簡介
Spring data JPA是Spring在ORM框架,以及JPA規範的基礎上,封裝的一套JPA應用框架,並提供了一整套的資料訪問層解決方案。
二、Spring data JPA的功能
Spring data JPA的功能非常的強大,這裡我們先跳過環境搭建這一步,來一睹Spring data JPA的“芳容”。
Spring data JPA提供給使用者使用的,主要有以下幾個介面:
Repository:僅僅是一個標識,表明任何繼承它的均為倉庫介面類,方便Spring自動掃描識別
CrudRepository:繼承Repository,實現了一組CRUD
PagingAndSortingRepository:繼承CrudRepository,實現了一組分頁排序相關的方法
JpaRepository:繼承PagingAndSortingRepository,實現一組JPA規範相關的方法
JpaSpecificationExecutor:比較特殊,不屬於Repository體系,實現一組JPA Criteria查詢相關的方法。
三、Spring data JPA的介面
1、CrudRepository介面
建立一個Entity類:
[java] view plain copy print?- @Entity
- @Table(name=“USER”)
- publicclass User {
- @Id
- @GeneratedValue
- private Integer id;
- //賬號
- private String account;
- //姓名
- private String name;
- //密碼
- private String password;
- // 郵箱
- private String email;
- }
@Entity
@Table(name="USER")
public class User {
@Id
@GeneratedValue
private Integer id;
//賬號
private String account;
//姓名
private String name;
//密碼
private String password;
// 郵箱
private String email;
}
編寫介面,並繼承CrudRepository介面:[java] view plain copy print?
- publicinterface UserRepository extends CrudRepository<User, Integer> {
- }
public interface UserRepository extends CrudRepository<User, Integer> {
}
編寫測試類(為了更直觀的看到效果,所有測試類都沒有使用斷言,直接使用的列印語句):[java] view plain copy print?
- publicclass UserRepositoryTest {
- @Autowired
- private UserRepository dao;
- @Test//儲存
- publicvoid testSave(){
- User user = new User();
- user.setName(”chhliu”);
- user.setAccount(”10000”);
- user.setEmail(”[email protected]”);
- user.setPassword(”123456”);
- dao.save(user);
- }
- @Test//批量儲存
- publicvoid testSave1(){
- List<User> users = new ArrayList<User>();
- User user = new User();
- user.setName(”tanjie”);
- user.setAccount(”10000”);
- user.setEmail(”[email protected]”);
- user.setPassword(”123456”);
- users.add(user);
- user = new User();
- user.setName(”esdong”);
- user.setAccount(”10000”);
- user.setEmail(”[email protected]”);
- user.setPassword(”123456”);
- users.add(user);
- user = new User();
- user.setName(”qinhongfei”);
- user.setAccount(”10000”);
- user.setEmail(”[email protected]”);
- user.setPassword(”123456”);
- users.add(user);
- user = new User();
- user.setName(”huizhang”);
- user.setAccount(”10000”);
- user.setEmail(”[email protected]”);
- user.setPassword(”123456”);
- users.add(user);
- user = new User();
- user.setName(”caican”);
- user.setAccount(”10000”);
- user.setEmail(”[email protected]”);
- user.setPassword(”123456”);
- users.add(user);
- dao.save(users);
- }
- @Test//更新
- publicvoid testUpdate(){
- User user = dao.findOne(1);
- user.setPassword(”123890”);// 要想這樣實現更新的功能,需要在service層加上@Transaction事物註解
- }
- @Test//刪除
- publicvoid testDelete(){
- dao.delete(2);
- }
- @Test//查詢所有
- publicvoid testFindAll(){
- List<User> users = (List<User>) dao.findAll();
- System.out.println(JSON.toJSONString(users));
- }
- @Test//判斷指定的id物件是否存在
- publicvoid testIsExist(){
- boolean isExist = dao.exists(8);
- System.out.println(isExist);
- }
- @Test//通過id列表來查詢
- publicvoid testFindUserByIds(){
- List<Integer> listIds = new ArrayList<Integer>();
- listIds.add(2);
- listIds.add(4);
- listIds.add(7);
- List<User> users = (List<User>) dao.findAll(listIds);
- System.out.println(JSON.toJSONString(users));
- }
- }
public class UserRepositoryTest {
@Autowired
private UserRepository dao;
@Test//儲存
public void testSave(){
User user = new User();
user.setName("chhliu");
user.setAccount("10000");
user.setEmail("[email protected]");
user.setPassword("123456");
dao.save(user);
}
@Test//批量儲存
public void testSave1(){
List<User> users = new ArrayList<User>();
User user = new User();
user.setName("tanjie");
user.setAccount("10000");
user.setEmail("[email protected]");
user.setPassword("123456");
users.add(user);
user = new User();
user.setName("esdong");
user.setAccount("10000");
user.setEmail("[email protected]");
user.setPassword("123456");
users.add(user);
user = new User();
user.setName("qinhongfei");
user.setAccount("10000");
user.setEmail("[email protected]");
user.setPassword("123456");
users.add(user);
user = new User();
user.setName("huizhang");
user.setAccount("10000");
user.setEmail("[email protected]");
user.setPassword("123456");
users.add(user);
user = new User();
user.setName("caican");
user.setAccount("10000");
user.setEmail("[email protected]");
user.setPassword("123456");
users.add(user);
dao.save(users);
}
@Test//更新
public void testUpdate(){
User user = dao.findOne(1);
user.setPassword("123890");// 要想這樣實現更新的功能,需要在service層加上@Transaction事物註解
}
@Test//刪除
public void testDelete(){
dao.delete(2);
}
@Test//查詢所有
public void testFindAll(){
List<User> users = (List<User>) dao.findAll();
System.out.println(JSON.toJSONString(users));
}
@Test//判斷指定的id物件是否存在
public void testIsExist(){
boolean isExist = dao.exists(8);
System.out.println(isExist);
}
@Test//通過id列表來查詢
public void testFindUserByIds(){
List<Integer> listIds = new ArrayList<Integer>();
listIds.add(2);
listIds.add(4);
listIds.add(7);
List<User> users = (List<User>) dao.findAll(listIds);
System.out.println(JSON.toJSONString(users));
}
}
大家可以看出,到這裡,我就只寫了一個介面類,並沒有實現這個介面類,就可以完成基本的CRUD操作。因為這個介面會自動為域物件建立增刪改查方法,供業務層直接使用。
該介面的定義如下,總共提供了11個方法,基本上可以滿足簡單的CRUD操作以及批量操作:
[java] view plain copy print?- @NoRepositoryBean
- publicinterface 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();//刪除所有
- }
@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();//刪除所有
}
2、PagingAndSortingRepository介面
PagingAndSortingRepository介面繼承了CrudRepository介面。
編寫介面,並繼承PagingAndSortingRepository介面
[java] view plain copy print?- publicinterface UserRepositoryWithOrder extends
- PagingAndSortingRepository<User, Integer> {
- }
public interface UserRepositoryWithOrder extends
PagingAndSortingRepository<User, Integer> {
}
編寫測試類:[java] view plain copy print?
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = { “classpath:applicationContext-config.xml” })
- @TransactionConfiguration(defaultRollback = false)
- @Transactional
- publicclass UserRepositoryWithOrderTest {
- @Autowired
- private UserRepositoryWithOrder dao;
- @Test
- publicvoid testOrder(){
- Sort sort = new Sort(Direction.DESC, “id”);
- Pageable pageable = new PageRequest(0, 5, sort);
- Page<User> page = dao.findAll(pageable);
- System.out.println(JSON.toJSONString(page));
- System.out.println(page.getSize());
- }
- }
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfiguration(defaultRollback = false)
@Transactional
public class UserRepositoryWithOrderTest {
@Autowired
private UserRepositoryWithOrder dao;
@Test
public void testOrder(){
Sort sort = new Sort(Direction.DESC, "id");
Pageable pageable = new PageRequest(0, 5, sort);
Page<User> page = dao.findAll(pageable);
System.out.println(JSON.toJSONString(page));
System.out.println(page.getSize());
}
}
只要繼承了這個介面,Spring data JPA就已經為你提供了分頁和排序的功能了。該介面的定義如下,主要提供了兩個方法,供使用,其中T是要操作的實體類,ID是實體類主鍵的型別[java] view plain copy print?
- @NoRepositoryBean
- publicinterface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
- Iterable<T> findAll(Sort sort);// 不帶分頁的排序
- Page<T> findAll(Pageable pageable);// 帶分頁的排序
- }
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);// 不帶分頁的排序
Page<T> findAll(Pageable pageable);// 帶分頁的排序
}
3、JpaRepository介面
如果業務需要即提供CRUD操作,又需要提供分頁以及排序功能,那麼就可以直接繼承這個介面。該介面繼承了PagingAndSortingRepository介面。
介面定義如下:
[java] view plain copy print?- publicinterface 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();//刪除所有
- }
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();//刪除所有
}
4、JpaSpecificationExecutor介面
該介面提供了對JPA Criteria查詢的支援。注意,這個介面很特殊,不屬於Repository體系,而Spring data JPA不會自動掃描識別,所以會報找不到對應的Bean,我們只需要繼承任意一個繼承了Repository的子介面或直接繼承Repository介面,Spring data JPA就會自動掃描識別,進行統一的管理。
編寫介面如下:
[java] view plain copy print?- publicinterface SpecificationExecutorRepository extends CrudRepository<User, Integer>,
- JpaSpecificationExecutor<User> {
- }
public interface SpecificationExecutorRepository extends CrudRepository<User, Integer>,
JpaSpecificationExecutor<User> {
}
Service類:[java] view plain copy print?
- @Service
- publicclass 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(finalint 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(finalint 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(finalint start, finalint 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(finalint 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(finalint 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();
- }
- });
- }
- }
@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();
}
});
}
}
測試類:[java] view plain copy print?
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = { “classpath:applicationContext-config.xml” })
- @TransactionConfiguration(defaultRollback = false)
- @Transactional
- publicclass SpecificationExecutorRepositoryManagerTest {
- @Autowired
- private SpecificationExecutorRepositoryManager manager;
- @Test
- publicvoid testFindUserByName(){
- User user = manager.findUserByName(”chhliu”);
- System.out.println(JSON.toJSONString(user));
- }
- @Test
- publicvoid testFindUserByNameAndEmail(){
- User user = manager.findUserByNameAndEmail(”chhliu”, “[email protected]”);
- System.out.println(JSON.toJSONString(user));
- }
- @Test
- publicvoid testFindUserByUserVo(){
- User user = new User();
- user.setName(”chhliu”);
- user.setEmail(”[email protected]”);
- User u = manager.findUserByUser(user);
- System.out.println(JSON.toJSONString(u));
- }
- @Test
- publicvoid testFindUserByIds(){
- List<User> users = manager.findUserByIds(new ArrayList<Integer>(Arrays.asList(1,3,5,6)));
- System.out.println(JSON.toJSONString(users));
- }
- @Test
- publicvoid testFindUserByGtId(){
- List<User> users = manager.findUserByGtId(5);
- System.out.println(JSON.toJSONString(users));
- }
- @Test
- publicvoid testFindUserByLtId(){
- List<User> users = manager.findUserByLtId(5);
- System.out.println(JSON.toJSONString(users));
- }
- @Test
- publicvoid testFindUserBetweenId(){
- List<User> users = manager.findUserBetweenId(4, 9);
- System.out.println(JSON.toJSONString(users));
- }
- @Test
- publicvoid testFindUserAndOrder(){
- Page<User> users = manager.findUserAndOrder(1);
- System.out.println(JSON.toJSONString(users));
- }
- @Test
- publicvoid testFindUserAndOrderSecondMethod(){
- List<User> users = manager.findUserAndOrderSecondMethod(1);
- System.out.println(JSON.toJSONString(users));
- }
- }
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfigu