目錄
一、Spring Data JPA的內部原理剖析
1、Spring Data JPA的常用接口分析
在客戶的案例中,我們發現在自定義的CustomerDao
中,並沒有提供任何方法就可以使用其中的很多方法,那麼這些方法究竟是怎麼來的呢?答案很簡單,對於我們自定義的Dao接口,由於繼承了JpaRepository
和JpaSpecificationExecutor
,所以我們可以使用這兩個接口的所有方法。
在使用Spring Data JPA時,一般實現JpaRepository和JpaSpecificationExecutor接口,這樣就可以使用這些接口中定義的方法,但是這些方法都只是一些聲明,沒有具體的實現方式,那麼在 Spring Data JPA中它又是怎麼實現的呢?
2、Spring Data JPA的實現過程
- 通過對客戶案例,以debug斷點調試的方式,通過分析Spring Data JPA的原來來分析程序的執行過程
斷點執行到方法上時,我們可以發現注入的customerDao對象,本質上是通過JdkDynamicAopProxy生成的一個代理對象
- 代理對象中方法調用的分析
當程序執行的時候,會通過JdkDynamicAopProxy的invoke
方法,對customerDao對象生成動態代理對象。根據對Spring Data JPA介紹而知,要想進行findOne查詢方法,最終還是會出現JPA規範的API完成操作,那麼這些底層代碼存在於何處呢?答案很簡單,都隱藏在通過JdkDynamicAopProxy生成的動態代理對象當中,而這個動態代理對象就是SimpleJpaRepository
通過SimpleJpaRepository的源碼分析,定位到了findOne方法,在此方法中,返回em.find()的返回結果,那麼em又是什麼呢?
帶着問題繼續查找em對象,我們發現em就是EntityManager對象
,而他是JPA原生的實現方式,所以我們得到結論Spring Data JPA只是對標準JPA操作進行了進一步封裝,簡化了Dao層代碼的開發;
3、Spring Data JPA完整的調用過程分析
二、Spring Data JPA的查詢方式
1、使用Spring Data JPA中接口定義的方法進行查詢
跳轉到目錄
在繼承JpaRepository
,和JpaRepository接口
後,我們就可以使用接口中定義的方法進行查詢
2、使用JPQL的方式查詢
跳轉到目錄
使用Spring Data JPA提供的查詢方法已經可以解決大部分的應用場景,但是對於某些業務來說,我們還需要靈活的構造查詢條件,這時就可以使用@Query
註解,結合JPQL的語句方式完成查詢;
@Query 註解的使用非常簡單,只需在方法上面標註該註解,同時提供一個JPQL查詢語句即可
public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
/**
* 案例: 根據客戶名稱查詢客戶
* 使用jpql的形式查詢
* jpql: from Customer where custName = ?
*
* 配置jpql語句, 使用@Query註解
*/
@Query("from Customer where custName = ?")
public Customer findJpql(String custName);
/**
* 案例:根據客戶名稱和客戶id查詢客戶
* jpql: from Customer where custName = ? and custId = ?
*
* 對於多個佔位符參數
* 賦值的時候,默認的情況下,佔位符的位置需要和方法參數中的位置保持一致
*
* 可以指定佔位符參數的位置
* ? 索引的方式,指定此佔位的取值來源
*/
// @Query("from Customer where custName = ? and custId = ?")
// public Customer findCustNameAndId(String name, Long id);
@Query("from Customer where custName = ?2 and custId = ?1")
public Customer findCustNameAndId(Long id, String name);
/**
* 使用jpql完成更新操作
* 案例 : 根據id更新,客戶的名稱
* 更新4號客戶的名稱,將名稱改爲“黑馬程序員”
*
* sql :update cst_customer set cust_name = ? where cust_id = ?
* jpql : update Customer set custName = ? where custId = ?
*
* @Query : 代表的是進行查詢
* * 聲明此方法是用來進行更新操作
* @Modifying
* * 當前執行的是一個更新操作
*
*/
@Query("update Customer set custName = ?2 where custId = ?1")
@Modifying
public void updateCustomer(long custId,String custName);
}
測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JpqlTest {
@Autowired
private CustomerDao customerDao;
@Test
public void testFindJPQL() {
Customer customer = customerDao.findJpql("bb");
System.out.println("customer = " + customer);
}
@Test
public void testFindNameAndId() {
// Customer customer = customerDao.findCustNameAndId("gzy1", 2L);
Customer customer = customerDao.findCustNameAndId(2L, "gzy1");
System.out.println("customer = " + customer);
}
/**
* 測試jpql的更新操作
* * springDataJpa中使用jpql完成 更新/刪除操作
* * 需要手動添加事務的支持
* * 默認會執行結束之後,回滾事務
* @Rollback : 設置是否自動回滾
* false | true
*/
@Test
@Transactional //添加事務的支持
@Rollback(value = false)
public void testUpdateCustomer() {
customerDao.updateCustomer(4L,"桂朝陽");
}
}
此外,也可以通過使用 @Query 來執行一個更新操作,爲此,我們需要在使用 @Query 的同時,用 @Modifying
來將該操作標識爲修改查詢,這樣框架最終會生成一個更新的操作,而非查詢
3、使用SQL語句查詢
跳轉到目錄
Spring Data JPA同樣也支持sql語句的查詢,如下:
public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
/**
* 使用sql的形式查詢:
* 查詢全部的客戶
* sql : select * from cst_customer;
* Query : 配置sql查詢
* value : sql語句
* nativeQuery : 查詢方式
* true : sql查詢
* false:jpql查詢
*
*/
@Query(value = "select * from cst_customer", nativeQuery = true)
public List<Object[]> findSql();
/**
* 根據條件來查詢
*/
@Query(value = "select * from cst_customer where cust_name like ?1", nativeQuery = true)
public List<Object[]> findSqlCondition(String likeName);
}
測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JpqlTest {
@Autowired
private CustomerDao customerDao;
//測試sql查詢
@Test
public void testFindSql() {
List<Object[]> list = customerDao.findSql();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
}
// 根據條件來查詢
@Test
public void testFindSqlCondition() {
List<Object[]> objs = customerDao.findSqlCondition("g%");
for (Object[] obj : objs) {
System.out.println(Arrays.toString(obj));
}
}
}
4、使用方法命名規則查詢
跳轉到目錄
顧名思義,方法命名規則查詢就是根據方法的名字
,就能創建查詢。只需要按照Spring Data JPA提供的方法命名規則定義方法的名稱,就可以完成查詢工作。Spring Data JPA在程序執行的時候會根據方法名稱進行解析,並自動生成查詢語句進行查詢
按照Spring Data JPA 定義的規則,查詢方法以findBy開頭,涉及條件查詢時,條件的屬性用條件關鍵字連接
,要注意的是:條件屬性首字母需大寫。框架在進行方法名解析時,會先把方法名多餘的前綴截取掉,然後對剩下部分進行解析。
-
findBy + 屬性名稱 (根據屬性名稱進行完成匹配的查詢=)
-
findBy + 屬性名稱 + “ 查詢方式(Like | isnull)”
-
多條件查詢
findBy + 屬性名 + “查詢方式” + “多條件的連接符(and|or)” + 屬性名 + “查詢方式”
public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
/**
* 方法名的約定:
* findBy : 查詢
* 對象中的屬性名(首字母大寫) : 查詢的條件
* CustName
* * 默認情況 : 使用 等於的方式查詢
* 特殊的查詢方式
*
* findByCustName -- 根據客戶名稱查詢
*
* 再springdataJpa的運行階段
* 會根據方法名稱進行解析 findBy from xxx(實體類)
* 屬性名稱 where custName =
*
*
* 1.findBy + 屬性名稱 (根據屬性名稱進行完成匹配的查詢=)
*
* 2.findBy + 屬性名稱 + “查詢方式(Like | isnull)”
* findByCustNameLike
*
* 3.多條件查詢
* findBy + 屬性名 + “查詢方式” + “多條件的連接符(and|or)” + 屬性名 + “查詢方式”
*/
// 根據名稱查詢
public Customer findByCustName(String custName);
//使用客戶名稱模糊匹配查詢
public List<Customer> findByCustNameLike(String custName);
//使用客戶名稱模糊匹配和客戶所屬行業精準匹配的查詢
public Customer findByCustNameLikeAndCustIndustry(String custName,String custIndustry);
}
測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JpqlTest {
// 測試方法命名規則的查詢
@Test
public void testNaming() {
Customer custName = customerDao.findByCustName("gzy1");
System.out.println("custName = " + custName);
}
@Test
public void testFindByCustNameLike() {
List<Customer> list = customerDao.findByCustNameLike("g%");
for (Customer customer : list) {
System.out.println("customer = " + customer);
}
}
@Test
public void testFindByCustNameLikeAndCustIndustry() {
Customer customer = customerDao.findByCustNameLikeAndCustIndustry("g%", "leaf");
System.out.println("customer = " + customer);
}
}
三、總結
* SpringDataJpa
第一 springDataJpa的概述
第二 springDataJpa的入門操作
案例:客戶的基本CRUD
i.搭建環境
創建工程導入座標
配置spring的配置文件(配置spring Data jpa的整合)
編寫實體類(Customer),使用jpa註解配置映射關係
ii.編寫一個符合springDataJpa的dao層接口
* 只需要編寫dao層接口,不需要編寫dao層接口的實現類
* dao層接口規範
1.需要繼承兩個接口(JpaRepository,JpaSpecificationExecutor)
2.需要提供響應的泛型
*
findOne(id) :根據id查詢
save(customer):保存或者更新(依據:傳遞的實體類對象中,是否包含id屬性)
delete(id) :根據id刪除
findAll() : 查詢全部
第三 springDataJpa的運行過程和原理剖析
1.通過JdkDynamicAopProxy的invoke方法創建了一個動態代理對象
2.SimpleJpaRepository當中封裝了JPA的操作(藉助JPA的api完成數據庫的CRUD)
3.通過hibernate完成數據庫操作(封裝了jdbc)
第四 複雜查詢
i.藉助接口中的定義好的方法完成查詢
findOne(id):根據id查詢
ii.jpql的查詢方式
jpql : jpa query language (jpq查詢語言)
特點:語法或關鍵字和sql語句類似
查詢的是類和類中的屬性
* 需要將JPQL語句配置到接口方法上
1.特有的查詢:需要在dao接口上配置方法
2.在新添加的方法上,使用註解的形式配置jpql查詢語句
3.註解 : @Query
iii.sql語句的查詢
1.特有的查詢:需要在dao接口上配置方法
2.在新添加的方法上,使用註解的形式配置sql查詢語句
3.註解 : @Query
value :jpql語句 | sql語句
nativeQuery :false(使用jpql查詢) | true(使用本地查詢:sql查詢)
是否使用本地查詢
iiii.方法名稱規則查詢