Spring data簡介:
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、小程序定制開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了新北免費(fèi)建站歡迎大家使用!
Spring Data是一個(gè)用于簡化數(shù)據(jù)庫訪問,并支持云服務(wù)的開源框架。其主要目標(biāo)是使得對(duì)數(shù)據(jù)的訪問變得方便快捷,并支持map-reduce框架和云計(jì)算數(shù)據(jù)服務(wù)。 Spring Data 包含多個(gè)子項(xiàng)目:
Commons - 提供共享的基礎(chǔ)框架,適合各個(gè)子項(xiàng)目使用,支持跨數(shù)據(jù)庫持久化
JPA - 簡化創(chuàng)建 JPA 數(shù)據(jù)訪問層和跨存儲(chǔ)的持久層功能
Hadoop - 基于 Spring 的 Hadoop 作業(yè)配置和一個(gè) POJO 編程模型的 MapReduce 作業(yè)
Key-Value? - 集成了 redis 和 Riak ,提供多個(gè)常用場景下的簡單封裝
Document - 集成文檔數(shù)據(jù)庫:CouchDB 和 MongoDB 并提供基本的配置映射和資料庫支持
Graph - 集成 Neo4j 提供強(qiáng)大的基于 POJO 的編程模型
Graph Roo AddOn - Roo support for Neo4j
JDBC Extensions - 支持 Oracle RAD、高級(jí)隊(duì)列和高級(jí)數(shù)據(jù)類型
Mapping - 基于 Grails 的提供對(duì)象映射框架,支持不同的數(shù)據(jù)庫
Examples - 示例程序、文檔和圖數(shù)據(jù)庫
Guidance - 高級(jí)文檔
一、Spring data JPA簡介
Spring data JPA是Spring在ORM框架,以及JPA規(guī)范的基礎(chǔ)上,封裝的一套JPA應(yīng)用框架,并提供了一整套的數(shù)據(jù)訪問層解決方案。
二、Spring data JPA的功能
Spring data JPA的功能非常的強(qiáng)大,這里我們先跳過環(huán)境搭建這一步,來一睹Spring data JPA的“芳容”。
Spring data JPA提供給用戶使用的,主要有以下幾個(gè)接口:
Repository:僅僅是一個(gè)標(biāo)識(shí),表明任何繼承它的均為倉庫接口類,方便Spring自動(dòng)掃描識(shí)別?
CrudRepository:繼承Repository,實(shí)現(xiàn)了一組CRUD相關(guān)的方法?
PagingAndSortingRepository:繼承CrudRepository,實(shí)現(xiàn)了一組分頁排序相關(guān)的方法?
JpaRepository:繼承PagingAndSortingRepository,實(shí)現(xiàn)一組JPA規(guī)范相關(guān)的方法?
JpaSpecificationExecutor:比較特殊,不屬于Repository體系,實(shí)現(xiàn)一組JPA Criteria查詢相關(guān)的方法。
三、Spring data JPA的接口
1、CrudRepository接口
建立一個(gè)Entity類:
@Entity
@Table(name="USER")
public class User {
@Id
? ? @GeneratedValue
? ? private Integer id;
? ??
? ? //賬號(hào)
? ? private String account;
? ??
? ? //姓名
? ? private String name;
? ??
? ? //密碼
? ? private String password;
? ??
? ? // 郵箱
? ? private String email;
}
編寫接口,并繼承CrudRepository接口:
public interface UserRepository extends CrudRepository<User, Integer> {
}
編寫測(cè)試類(為了更直觀的看到效果,所有測(cè)試類都沒有使用斷言,直接使用的打印語句):
public class UserRepositoryTest {
@Autowired
private UserRepository dao;
@Test//保存
public void testSave(){
User user = new User();
user.setName("chhliu");
user.setAccount("10000");
user.setEmail("chhliu@.com");
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("tanjie@.com");
user.setPassword("123456");
users.add(user);
user = new User();
user.setName("esdong");
user.setAccount("10000");
user.setEmail("esdong@.com");
user.setPassword("123456");
users.add(user);
user = new User();
user.setName("qinhongfei");
user.setAccount("10000");
user.setEmail("qinhongfei@.com");
user.setPassword("123456");
users.add(user);
user = new User();
user.setName("huizhang");
user.setAccount("10000");
user.setEmail("huizhang@.com");
user.setPassword("123456");
users.add(user);
user = new User();
user.setName("caican");
user.setAccount("10000");
user.setEmail("caican@.com");
user.setPassword("123456");
users.add(user);
dao.save(users);
}
@Test//更新
public void testUpdate(){
User user = dao.findOne(1);
user.setPassword("123890");// 要想這樣實(shí)現(xiàn)更新的功能,需要在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對(duì)象是否存在
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));
}
}
大家可以看出,到這里,我就只寫了一個(gè)接口類,并沒有實(shí)現(xiàn)這個(gè)接口類,就可以完成基本的CRUD操作。因?yàn)檫@個(gè)接口會(huì)自動(dòng)為域?qū)ο髣?chuàng)建增刪改查方法,供業(yè)務(wù)層直接使用。
該接口的定義如下,總共提供了11個(gè)方法,基本上可以滿足簡單的CRUD操作以及批量操作:
@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);//根據(jù)id查詢一個(gè)對(duì)象
boolean exists(ID id);//判斷對(duì)象是否存在
Iterable<T> findAll();//查詢所有的對(duì)象
Iterable<T> findAll(Iterable<ID> ids);//根據(jù)id列表查詢所有的對(duì)象
long count();//計(jì)算對(duì)象的總個(gè)數(shù)
void delete(ID id);//根據(jù)id刪除
void delete(T entity);//刪除對(duì)象
void delete(Iterable<? extends T> entities);//批量刪除
void deleteAll();//刪除所有
}
2、PagingAndSortingRepository接口
PagingAndSortingRepository接口繼承了CrudRepository接口。
編寫接口,并繼承PagingAndSortingRepository接口
public interface UserRepositoryWithOrder extends
PagingAndSortingRepository<User, Integer> {
?
}
編寫測(cè)試類:
@RunWith(SpringJUnit4Cla***unner.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());
}
}
只要繼承了這個(gè)接口,Spring data JPA就已經(jīng)為你提供了分頁和排序的功能了。該接口的定義如下,主要提供了兩個(gè)方法,供使用,其中T是要操作的實(shí)體類,ID是實(shí)體類主鍵的類型
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);// 不帶分頁的排序
Page<T> findAll(Pageable pageable);// 帶分頁的排序
}
3、JpaRepository接口
如果業(yè)務(wù)需要即提供CRUD操作,又需要提供分頁以及排序功能,那么就可以直接繼承這個(gè)接口。該接口繼承了PagingAndSortingRepository接口。
接口定義如下:
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
List<T> findAll();//查詢所有對(duì)象,不排序
List<T> findAll(Sort sort);//查詢所有對(duì)象,并排序
<S extends T> List<S> save(Iterable<S> entities);//批量保存
void flush();//強(qiáng)制緩存與數(shù)據(jù)庫同步
T saveAndFlush(T entity);//保存并強(qiáng)制同步
void deleteInBatch(Iterable<T> entities);//批量刪除
void deleteAllInBatch();//刪除所有
}
4、JpaSpecificationExecutor接口
該接口提供了對(duì)JPA Criteria查詢的支持。注意,這個(gè)接口很特殊,不屬于Repository體系,而Spring data JPA不會(huì)自動(dòng)掃描識(shí)別,所以會(huì)報(bào)找不到對(duì)應(yīng)的Bean,我們只需要繼承任意一個(gè)繼承了Repository的子接口或直接繼承Repository接口,Spring data JPA就會(huì)自動(dòng)掃描識(shí)別,進(jìn)行統(tǒng)一的管理。
編寫接口如下:
public interface SpecificationExecutorRepository extends CrudRepository<User, Integer>,
JpaSpecificationExecutor<User> {
?
}
Service類:
@Service
public class SpecificationExecutorRepositoryManager {
@Autowired
private SpecificationExecutorRepository dao;
/**
* 描述:根據(jù)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;
}
});
}
/**
* 描述:根據(jù)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();
}
});
}
}
測(cè)試類:
@RunWith(SpringJUnit4Cla***unner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfiguration(defaultRollback = false)
@Transactional
public class SpecificationExecutorRepositoryManagerTest {
@Autowired
private SpecificationExecutorRepositoryManager manager;
@Test
public void testFindUserByName(){
User user = manager.findUserByName("chhliu");
System.out.println(JSON.toJSONString(user));
}
@Test
public void testFindUserByNameAndEmail(){
User user = manager.findUserByNameAndEmail("chhliu", "chhliu@.com");
System.out.println(JSON.toJSONString(user));
}
@Test
public void testFindUserByUserVo(){
User user = new User();
user.setName("chhliu");
user.setEmail("chhliu@.com");
User u = manager.findUserByUser(user);
System.out.println(JSON.toJSONString(u));
}
@Test
public void testFindUserByIds(){
List<User> users = manager.findUserByIds(new ArrayList<Integer>(Arrays.asList(1,3,5,6)));
System.out.println(JSON.toJSONString(users));
}
@Test
public void testFindUserByGtId(){
List<User> users = manager.findUserByGtId(5);
System.out.println(JSON.toJSONString(users));
}
@Test
public void testFindUserByLtId(){
List<User> users = manager.findUserByLtId(5);
System.out.println(JSON.toJSONString(users));
}
@Test
public void testFindUserBetweenId(){
List<User> users = manager.findUserBetweenId(4, 9);
System.out.println(JSON.toJSONString(users));
}
@Test
public void testFindUserAndOrder(){
Page<User> users = manager.findUserAndOrder(1);
System.out.println(JSON.toJSONString(users));
}
@Test
public void testFindUserAndOrderSecondMethod(){
List<User> users = manager.findUserAndOrderSecondMethod(1);
System.out.println(JSON.toJSONString(users));
}
}
5、Repository接口
這個(gè)接口是最基礎(chǔ)的接口,只是一個(gè)標(biāo)志性的接口,沒有定義任何的方法,那這個(gè)接口有什么用了?既然Spring data JPA提供了這個(gè)接口,自然是有它的用處,例如,我們有一部分方法是不想對(duì)外提供的,比如我們只想提供增加和修改方法,不提供刪除方法,那么前面的幾個(gè)接口都是做不到的,這個(gè)時(shí)候,我們就可以繼承這個(gè)接口,然后將CrudRepository接口里面相應(yīng)的方法拷貝到Repository接口就可以了。
總結(jié):上述五個(gè)接口,開發(fā)者到底該如何選擇?其實(shí)依據(jù)很簡單,根據(jù)具體的業(yè)務(wù)需求,選擇其中之一。因?yàn)楦鱾€(gè)接口之間并不存在功能強(qiáng)弱的問題。
四、Spring data JPA的查詢
1、使用 @Query 創(chuàng)建查詢
@Query 注解的使用非常簡單,只需在聲明的方法上面標(biāo)注該注解,同時(shí)提供一個(gè) JP QL 查詢語句即可。很多開發(fā)者在創(chuàng)建 JP QL 時(shí)喜歡使用命名參數(shù)來代替位置編號(hào),@Query 也對(duì)此提供了支持。JP QL 語句中通過": 變量"的格式來指定參數(shù),同時(shí)在方法的參數(shù)前面使用 @Param 將方法參數(shù)與 JP QL 中的命名參數(shù)對(duì)應(yīng)。此外,開發(fā)者也可以通過使用 @Query 來執(zhí)行一個(gè)更新操作,為此,我們需要在使用 @Query 的同時(shí),用 @Modifying 來將該操作標(biāo)識(shí)為修改查詢,這樣框架最終會(huì)生成一個(gè)更新的操作,而非查詢操作。
編寫接口,如下:
/**
?* 描述:自定義查詢,當(dāng)Spring Data JPA無法提供時(shí),需要自定義接口,此時(shí)可以使用這種方式
?*/
public interface UserDefineBySelf extends JpaRepository<User, Integer> {
/**
* 命名參數(shù)
* 描述:推薦使用這種方法,可以不用管參數(shù)的位置
*/
@Query("select u from User u where u.name = :name")
User findUserByName(@Param("name") String name);
/**
* 索引參數(shù)
* 描述:使用?占位符
*/
@Query("select u from User u where u.email = ?1")// 1表示第一個(gè)參數(shù)
User findUserByEmail(String email);
/**
* 描述:可以通過@Modifying和@Query來實(shí)現(xiàn)更新
* 注意:Modifying queries的返回值只能為void或者是int/Integer
*/
@Modifying
@Query("update User u set u.name = :name where u.id = :id")
int updateUserById(@Param("name") String name, @Param("id") int id);
}
注:@Modifying注解里面有一個(gè)配置clearAutomatically
它說的是可以清除底層持久化上下文,就是entityManager這個(gè)類,我們知道jpa底層實(shí)現(xiàn)會(huì)有二級(jí)緩存,也就是在更新完數(shù)據(jù)庫后,如果后面去用這個(gè)對(duì)象,你再去查這個(gè)對(duì)象,這個(gè)對(duì)象是在一級(jí)緩存,但是并沒有跟數(shù)據(jù)庫同步,這個(gè)時(shí)候用clearAutomatically=true,就會(huì)刷新hibernate的一級(jí)緩存了, 不然你在同一接口中,更新一個(gè)對(duì)象,接著查詢這個(gè)對(duì)象,那么你查出來的這個(gè)對(duì)象還是之前的沒有更新之前的狀態(tài)
測(cè)試類:
@RunWith(SpringJUnit4Cla***unner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfiguration(defaultRollback = false)
@Transactional
public class UserDefineBySelfTest {
@Autowired
private UserDefineBySelf dao;
@Test
public void testFindUserByName(){
User user = dao.findUserByName("chhliu");
Assert.assertEquals("chhliu", user.getName());
System.out.println(user.getName());
}
@Test
public void testFindUserByEmail(){
User user = dao.findUserByEmail("chhliu@.com");
Assert.assertEquals("chhliu", user.getName());
System.out.println(user.getName());
}
@Test
public void testUpdateUserById(){
dao.updateUserById("tanjie", 4);
}
}
從測(cè)試代碼可以看出,我們同樣只定義了接口,沒有任何的實(shí)現(xiàn)類,但是卻實(shí)現(xiàn)了我們所需要的功能。
2、使用@NamedQueries創(chuàng)建查詢
命名查詢是 JPA 提供的一種將查詢語句從方法體中獨(dú)立出來,以供多個(gè)方法共用的功能。Spring Data JPA 對(duì)命名查詢也提供了很好的支持。用戶只需要按照 JPA 規(guī)范在 orm.xml 文件或者在代碼中使用 @NamedQuery(或 @NamedNativeQuery)定義好查詢語句,唯一要做的就是為該語句命名時(shí),需要滿足”DomainClass.methodName()”的 命名規(guī)則。
編寫接口:
public interface FindUserByNamedQueryRepository extends JpaRepository<User, Integer> {
User findUserWithName(@Param("name") String name);
}
編寫類:
@Entity
@NamedQueries(value={
@NamedQuery(name="User.findUserWithName",query="select u from User u where u.name = :name")
})
// 注意:此處如果是多個(gè)方法,那么需要使用@NamedQueries,如果只有一個(gè)方法,則可以使用@NamedQuery,寫法如下:@NamedQuery(name="User.findUserWithName",query="select u from User u where u.name = :name")
public class FindUserByNamedQuery {
/**
* 注意:此處必須要給這個(gè)實(shí)體類定義一個(gè)唯一標(biāo)識(shí),否則會(huì)報(bào)異常
*/
@Id
@GeneratedValue
private Integer id;
}
注意:文中標(biāo)記為紅色的部分,需要一一對(duì)應(yīng),否則不滿足JPA 的規(guī)范。
測(cè)試類:
@RunWith(SpringJUnit4Cla***unner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfiguration(defaultRollback = false)
@Transactional
public class FindUserByNamedQueryRepositoryTest {
@Autowired
private FindUserByNamedQueryRepository dao;
@Test
public void testFindUserByName(){
User user = dao.findUserWithName("caican");
System.out.println(JSON.toJSONString(user));
}
}
3、通過解析方法名創(chuàng)建查詢
顧名思義,就是根據(jù)方法的名字,就能創(chuàng)建查詢,也許初聽起來,感覺很不可思議,等測(cè)試后才發(fā)現(xiàn),原來一切皆有可能。
編寫接口:
public interface SimpleConditionQueryRepository extends JpaRepository<User, Integer> {
/**
* 說明:按照Spring data 定義的規(guī)則,查詢方法以find|read|get開頭
? ? ?* 涉及條件查詢時(shí),條件的屬性用條件關(guān)鍵字連接,要注意的是:條件屬性首字母需大寫
*/
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.name = :name and u.email = :email
* 參數(shù)名大寫,條件名首字母大寫,并且接口名中參數(shù)出現(xiàn)的順序必須和參數(shù)列表中的參數(shù)順序一致
*/
User findByNameAndEmail(String name, String email);
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.name = ?1 or u.password = ?2
*/
List<User> findByNameOrPassword(String name, String password);
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.id between ?1 and ?2
*/
List<User> findByIdBetween(Integer start, Integer end);
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.id < ?1
*/
List<User> findByIdLessThan(Integer end);
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.id > ?1
*/
List<User> findByIdGreaterThan(Integer start);
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.name is null
*/
List<User> findByNameIsNull();
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.name is not null
*/
List<User> findByNameIsNotNull();
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.name like ?1
*/
List<User> findByNameLike(String name);
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.name not like ?1
*/
List<User> findByNameNotLike(String name);
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.password = ?1 order by u.id desc
*/
List<User> findByPasswordOrderByIdDesc(String password);
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.name <> ?1
*/
List<User> findByNameNot(String name);
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.id in ?1
*/
List<User> findByIdIn(List<Integer> ids);
/**
* 注:此處這個(gè)接口相當(dāng)于發(fā)送了一條SQL:select u from User u where u.id not in ?1
*/
List<User> findByIdNotIn(List<Integer> ids);
}
測(cè)試類(注釋部分為實(shí)際發(fā)送的sql語句):
@RunWith(SpringJUnit4Cla***unner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfiguration(defaultRollback = false)
@Transactional
public class SimpleConditionQueryRepositoryTest {
@Autowired
private SimpleConditionQueryRepository dao;
/**
* select
? ? ? ? user0_.id as id0_,
? ? ? ? user0_.account as account0_,
? ? ? ? user0_.email as email0_,
? ? ? ? user0_.name as name0_,
? ? ? ? user0_.password as password0_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.name=??
? ? ? ? and user0_.email=? limit ?
*/
@Test
public void testFindUserByNameAndEmail(){
User user = dao.findByNameAndEmail("chhliu", "chhliu@.com");
System.out.println(JSON.toJSONString(user));
}
/**
* select
? ? ? ? user0_.id as id1_,
? ? ? ? user0_.account as account1_,
? ? ? ? user0_.email as email1_,
? ? ? ? user0_.name as name1_,
? ? ? ? user0_.password as password1_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.name=??
? ? ? ? or user0_.password=?
*/
@Test
public void testFindUserByNameOrPassword(){
List<User> users = dao.findByNameOrPassword("chhliu", "123456");
System.out.println(JSON.toJSONString(users));
}
/**
* select
? ? ? ? user0_.id as id1_,
? ? ? ? user0_.account as account1_,
? ? ? ? user0_.email as email1_,
? ? ? ? user0_.name as name1_,
? ? ? ? user0_.password as password1_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.id between ? and ?
*/
@Test
public void testFindByIdBetween(){
List<User> users = dao.findByIdBetween(5, 8);
System.out.println(JSON.toJSONString(users));
}
/**
* select
? ? ? ? user0_.id as id1_,
? ? ? ? user0_.account as account1_,
? ? ? ? user0_.email as email1_,
? ? ? ? user0_.name as name1_,
? ? ? ? user0_.password as password1_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.id<?
*/
@Test
public void testFindByIdLessThan(){
List<User> users = dao.findByIdLessThan(4);
System.out.println(JSON.toJSONString(users));
}
/**
* select
? ? ? ? user0_.id as id0_,
? ? ? ? user0_.account as account0_,
? ? ? ? user0_.email as email0_,
? ? ? ? user0_.name as name0_,
? ? ? ? user0_.password as password0_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.id>?
*/
@Test
public void testFindByIdGreaterThan(){
List<User> users = dao.findByIdGreaterThan(6);
System.out.println(JSON.toJSONString(users));
}
/**
* select
? ? ? ? user0_.id as id0_,
? ? ? ? user0_.account as account0_,
? ? ? ? user0_.email as email0_,
? ? ? ? user0_.name as name0_,
? ? ? ? user0_.password as password0_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.name is null
*/
@Test
public void testFindByNameIsNull(){
List<User> users = dao.findByNameIsNull();
System.out.println(JSON.toJSONString(users));
}
/**
* select
? ? ? ? user0_.id as id1_,
? ? ? ? user0_.account as account1_,
? ? ? ? user0_.email as email1_,
? ? ? ? user0_.name as name1_,
? ? ? ? user0_.password as password1_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.name is not null
*/
@Test
public void testFindByNameIsNotNull(){
List<User> users = dao.findByNameIsNotNull();
System.out.println(JSON.toJSONString(users));
}
/**
* select
? ? ? ? user0_.id as id1_,
? ? ? ? user0_.account as account1_,
? ? ? ? user0_.email as email1_,
? ? ? ? user0_.name as name1_,
? ? ? ? user0_.password as password1_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.name like ?
*/
@Test
public void testFindByNameLike(){
List<User> users = dao.findByNameLike("chhliu");
System.out.println(JSON.toJSONString(users));
}
/**
* select
? ? ? ? user0_.id as id0_,
? ? ? ? user0_.account as account0_,
? ? ? ? user0_.email as email0_,
? ? ? ? user0_.name as name0_,
? ? ? ? user0_.password as password0_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.name not like ?
*/
@Test
public void testFindByNameNotLike(){
List<User> users = dao.findByNameNotLike("chhliu");
System.out.println(JSON.toJSONString(users));
}
/**
* select
? ? ? ? user0_.id as id0_,
? ? ? ? user0_.account as account0_,
? ? ? ? user0_.email as email0_,
? ? ? ? user0_.name as name0_,
? ? ? ? user0_.password as password0_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.password=??
? ? order by
? ? ? ? user0_.id desc
*/
@Test
public void testFindByPasswordOrderByIdDesc(){
List<User> users = dao.findByPasswordOrderByIdDesc("123456");
System.out.println(JSON.toJSONString(users));
}
/**
* select
? ? ? ? user0_.id as id1_,
? ? ? ? user0_.account as account1_,
? ? ? ? user0_.email as email1_,
? ? ? ? user0_.name as name1_,
? ? ? ? user0_.password as password1_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.name<>?
*/
@Test
public void testFindByNameNot(){
List<User> users = dao.findByNameNot("chhliu");
System.out.println(JSON.toJSONString(users));
}
/**
* select
? ? ? ? user0_.id as id1_,
? ? ? ? user0_.account as account1_,
? ? ? ? user0_.email as email1_,
? ? ? ? user0_.name as name1_,
? ? ? ? user0_.password as password1_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.id in (
? ? ? ? ? ? ? , ? , ? , ?
? ? ? ? )
*/
@Test
public void testFindByIdIn(){
List<User> users = dao.findByIdIn(new ArrayList<Integer>(Arrays.asList(3,4,6,8)));
System.out.println(JSON.toJSONString(users));
}
/**
* select
? ? ? ? user0_.id as id0_,
? ? ? ? user0_.account as account0_,
? ? ? ? user0_.email as email0_,
? ? ? ? user0_.name as name0_,
? ? ? ? user0_.password as password0_?
? ? from
? ? ? ? USER user0_?
? ? where
? ? ? ? user0_.id not in? (
? ? ? ? ? ? ? , ? , ? , ?
? ? ? ? )
*/
@Test
public void testFindByIdNotIn(){
List<User> users = dao.findByIdNotIn(new ArrayList<Integer>(Arrays.asList(3,4,6,8)));
System.out.println(JSON.toJSONString(users));
}
}
這里,我們只定義了一個(gè)接口,接口里面只有方法,但是沒有任何的實(shí)現(xiàn),卻完成了各種操作。
看到這里,估計(jì)很多人都會(huì)問,Spring data JPA是怎么做到的了?原來,框架在進(jìn)行方法名解析時(shí),會(huì)先把方法名多余的前綴截取掉,比如 find、findBy、read、readBy、get、getBy,然后對(duì)剩下部分進(jìn)行解析。并且如果方法的最后一個(gè)參數(shù)是 Sort 或者 Pageable 類型,也會(huì)提取相關(guān)的信息,以便按規(guī)則進(jìn)行排序或者分頁查詢。在創(chuàng)建查詢時(shí),我們通過在方法名中使用屬性名稱來表達(dá),比如 findByIdIn()??蚣茉诮馕鲈摲椒〞r(shí),首先剔除 findBy,然后對(duì)剩下的屬性進(jìn)行解析。
在查詢時(shí),通常需要同時(shí)根據(jù)多個(gè)屬性進(jìn)行查詢,且查詢的條件也格式各樣(大于某個(gè)值、在某個(gè)范圍等等),Spring Data JPA 為此提供了一些表達(dá)條件查詢的關(guān)鍵字,大致如下:
And --- 等價(jià)于 SQL 中的 and 關(guān)鍵字,比如 findByUsernameAndPassword(String user, Striang pwd)
Or --- 等價(jià)于 SQL 中的 or 關(guān)鍵字,比如 findByUsernameOrAddress(String user, String addr)
Between --- 等價(jià)于 SQL 中的 between 關(guān)鍵字,比如 findBySalaryBetween(int max, int min)
LessThan --- 等價(jià)于 SQL 中的 "<",比如 findBySalaryLessThan(int max)
GreaterThan --- 等價(jià)于 SQL 中的">",比如 findBySalaryGreaterThan(int min)
IsNull --- 等價(jià)于 SQL 中的 "is null",比如 findByUsernameIsNull()
IsNotNull --- 等價(jià)于 SQL 中的 "is not null",比如 findByUsernameIsNotNull()
NotNull --- 與 IsNotNull 等價(jià)
Like --- 等價(jià)于 SQL 中的 "like",比如 findByUsernameLike(String user)
NotLike --- 等價(jià)于 SQL 中的 "not like",比如 findByUsernameNotLike(String user)
OrderBy ---等價(jià)于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user)
Not --- 等價(jià)于 SQL 中的 "! =",比如 findByUsernameNot(String user)
In --- 等價(jià)于 SQL 中的 "in",比如 findByUsernameIn(Collection<String> userList) ,方法的參數(shù)可以是 Collection 類型,也可以是數(shù)組或者不定長參數(shù)
NotIn --- 等價(jià)于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection<String> userList) ,方法的參數(shù)可以是 Collection 類型,也可以是數(shù)組或者不定長參數(shù)
五、創(chuàng)建查詢的順序
Spring Data JPA 在為接口創(chuàng)建代理對(duì)象時(shí),如果發(fā)現(xiàn)同時(shí)存在多種上述情況可用,它該優(yōu)先采用哪種策略呢?為此,<jpa:repositories> 提供了 query-lookup-strategy 屬性,用以指定查找的順序。它有如下三個(gè)取值:
create --- 通過解析方法名字來創(chuàng)建查詢。即使有符合的命名查詢,或者方法通過 @Query 指定的查詢語句,都將會(huì)被忽略。
create-if-not-found --- 如果方法通過 @Query 指定了查詢語句,則使用該語句實(shí)現(xiàn)查詢;如果沒有,則查找是否定義了符合條件的命名查詢,如果找到,則使用該命名查詢;如果兩者都沒有找到,則通過解析方 法名字來創(chuàng)建查詢。這是 query-lookup-strategy 屬性的默認(rèn)值。
use-declared-query --- 如果方法通過 @Query 指定了查詢語句,則使用該語句實(shí)現(xiàn)查詢;如果沒有,則查找是否定義了符合條件的命名查詢,如果找到,則使用該命名查詢;如果兩者都沒有找到,則拋出異常。
六、Spring Data JPA 對(duì)事務(wù)的支持
細(xì)心的讀者也許從上面的代碼中看出了一些端倪,我們?cè)谑褂肧pring data JPA的時(shí)候,只是定義了接口,在使用的時(shí)候,直接注入就可以了,并沒有做與事物相關(guān)的任何處理,但實(shí)際上,事物已經(jīng)起到效果了,這又是為什么了?
默認(rèn)情況下,Spring Data JPA 實(shí)現(xiàn)的方法都是使用事務(wù)的。針對(duì)查詢類型的方法,其等價(jià)于 @Transactional(readOnly=true);增刪改類型的方法,等價(jià)于 @Transactional。可以看出,除了將查詢的方法設(shè)為只讀事務(wù)外,其他事務(wù)屬性均采用默認(rèn)值。
如果用戶覺得有必要,可以在接口方法上使用 @Transactional 顯式指定事務(wù)屬性,該值覆蓋 Spring Data JPA 提供的默認(rèn)值。同時(shí),開發(fā)者也可以在業(yè)務(wù)層方法上使用 @Transactional 指定事務(wù)屬性,這主要針對(duì)一個(gè)業(yè)務(wù)層方法多次調(diào)用持久層方法的情況。持久層的事務(wù)會(huì)根據(jù)設(shè)置的事務(wù)傳播行為來決定是掛起業(yè)務(wù)層事務(wù)還是加入業(yè)務(wù)層的事務(wù)。
當(dāng)前文章:springdatajpa使用詳解
路徑分享:http://aaarwkj.com/article14/igdsge.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、品牌網(wǎng)站制作、網(wǎng)站建設(shè)、企業(yè)網(wǎng)站制作、ChatGPT、用戶體驗(yàn)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)