SpringDataJpaSpring Data JPA 讓我們解脫了DA0層的操作,基本上所有CRUD都可以依賴於它來實現,在實際的工作工程中,推薦使用Spring Data JPA+ORM(如:hibernate)完成操作,這樣在切換不同的ORM框架時提供了極大的方便,同時也使資料庫層操作更加簡 ...
SpringDataJpaSpring Data JPA
讓我們解脫了DA0層的操作,基本上所有CRUD都可以依賴於它來實現,在實際的工作工程中,推薦使用Spring Data JPA+ORM(如:hibernate)完成操作,這樣在切換不同的ORM框架時提供了極大的方便,同時也使資料庫層操作更加簡單,方便解耦。
把JPA規範的代碼封裝起來,真正進行查詢的還是hibernate或mybatis >>(封裝了jdbc操作),然後進行查詢或操作資料庫。
SpringDataJpa入門操作(搭建環境)
創建工程,導入坐標
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>cn.itcast</groupId> 8 <artifactId>jpa-day02</artifactId> 9 <version>1.0-SNAPSHOT</version> 10 11 <properties> 12 <spring.version>5.0.2.RELEASE</spring.version> 13 <hibernate.version>5.0.7.Final</hibernate.version> 14 <s1f4j.version>1.6.6</s1f4j.version> 15 <log4j.version>1.2.12</log4j.version> 16 <c3pe.version>0.9.1.2</c3pe.version> 17 <mysql.version>5.1.6</mysql.version> 18 </properties> 19 20 <dependencies> 21 <!--junit單元測試--> 22 <dependency> 23 <groupId>junit</groupId> 24 <artifactId>junit</artifactId> 25 <version>4.9</version> 26 <scope>test</scope> 27 </dependency> 28 <!--spring beg--> 29 <dependency> 30 <groupId>org.aspectj</groupId> 31 <artifactId>aspectjweaver</artifactId> 32 <version>1.6.8</version> 33 </dependency> 34 <dependency> 35 <groupId>org.springframework</groupId> 36 <artifactId>spring-aop</artifactId> 37 <version>${spring.version}</version> 38 </dependency> 39 <dependency> 40 <groupId>org.springframework</groupId> 41 <artifactId>spring-context</artifactId> 42 <version>${spring.version}</version> 43 </dependency> 44 <dependency> 45 <groupId>org.springframework</groupId> 46 <artifactId>spring-context-support</artifactId> 47 <version>${spring.version}</version> 48 </dependency> 49 <!--spring對ORm框架的支持包--> 50 <dependency> 51 <groupId>org.springframework</groupId> 52 <artifactId>spring-orm</artifactId> 53 <version>${spring.version}</version> 54 </dependency> 55 <dependency> 56 <groupId>org.springframework</groupId> 57 <artifactId>spring-beans</artifactId> 58 <version>${spring.version}</version> 59 </dependency> 60 <dependency> 61 <groupId>org.springframework</groupId> 62 <artifactId>spring-core</artifactId> 63 <version>${spring.version}</version> 64 </dependency> 65 66 <!--spring end--> 67 68 <!--hibernate beg--> 69 <dependency> 70 <groupId>org.hibernate</groupId> 71 <artifactId>hibernate-core</artifactId> 72 <version>${hibernate.version}</version> 73 </dependency> 74 <dependency> 75 <groupId>org.hibernate</groupId> 76 <artifactId>hibernate-entitymanager</artifactId> 77 <version>${hibernate.version}</version> 78 </dependency> 79 <dependency> 80 <groupId>org.hibernate</groupId> 81 <artifactId>hibernate-validator</artifactId> 82 <version>5.2.1.Final</version> 83 </dependency> 84 85 <!--hibernate end--> 86 87 <!--c3p0 beg--> 88 <dependency> 89 <groupId>c3p0</groupId> 90 <artifactId>c3p0</artifactId> 91 <version>0.9.1.2</version> 92 </dependency> 93 94 <!--log end--> 95 <dependency> 96 <groupId>log4j</groupId> 97 <artifactId>log4j</artifactId> 98 <version>1.2.17</version> 99 </dependency> 100 <dependency> 101 <groupId>org.slf4j</groupId> 102 <artifactId>slf4j-api</artifactId> 103 <version>1.7.25</version> 104 </dependency> 105 <dependency> 106 <groupId>org.slf4j</groupId> 107 <artifactId>slf4j-log4j12</artifactId> 108 <version>1.6.6</version> 109 </dependency> 110 <!--log end--> 111 112 <dependency> 113 <groupId>mysq1</groupId> 114 <artifactId>mysql-connector-java</artifactId> 115 <version>5.1.6</version> 116 </dependency> 117 <!-- spring data jpa 的坐標 --> 118 <dependency> 119 <groupId>org.springframework.data</groupId> 120 <artifactId>spring-data-jpa</artifactId> 121 <version>1.9.0.RELEASE</version> 122 </dependency> 123 <dependency> 124 <groupId>org.springframework</groupId> 125 <artifactId>spring-test</artifactId> 126 <version>4.2.4.RELEASE</version> 127 </dependency> 128 129 <!--el beg 使用spring data jpa必須引入--> 130 <dependency> 131 <groupId>javax.el</groupId> 132 <artifactId>javax.el-api</artifactId> 133 <version>2.2.4</version> 134 </dependency> 135 <dependency> 136 <groupId>org.glassfish.web</groupId> 137 <artifactId>javax.el</artifactId> 138 <version>2.2.4</version> 139 </dependency> 140 <dependency> 141 <groupId>org.projectlombok</groupId> 142 <artifactId>lombok</artifactId> 143 <version>1.16.22</version> 144 </dependency> 145 </dependencies> 146 </project>項目所需的依賴坐標
配置Spring的配置文件
1 <!--創建entityManagerFactory對象交給Spring容器管理--> 2 <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> (許可權定位類名) 3 <property name="dataSource" ref="dataSource"/> (依賴註入) 4 <property name="packagesToScan " value="cn.itcast.domain"/> (配置的掃描的包,實體類所在的包) 5 <property name="persistenceProvider"> (JPA的實現廠家) 6 <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/> 7 </property> 8 9 <!--jpa的供應商適配器--> 10 <property name="jpaVendorAdapter"> 11 <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 12 <!--配置是否自動創建資料庫表--> 13 <property name="generateDdl" value="false"/> 14 <!--指定資料庫類型--> 15 <property name="database" value="MYSQL"/> 16 <!--資料庫方言:支持的特有語法(不同資料庫有不同的語法)--> 17 <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/> 18 <!--是否顯示sql--> 19 <property name="showSql" value="true"/> 20 </bean> 21 </property> 22 23 <!--jpa的方言 :高級特性(配置了誰就擁有了誰的高級特性)--> 24 <property name="jpaDialect"> 25 <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean> 26 </property> 27 </bean> 28 <!--2.創建資料庫連接池--> 29 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 30 <property name="user" value="root"></property> 31 <property name="password" value="123456"></property> 32 <property name="jdbcUrl" value="jdbc:mysq1:///jpa"></property> 33 <property name="driverClass" value="com.mysql.jdbc.Driver"></property> 34 </bean> 35 (以下的不需要特別記憶)>>> 36 <!--3.整合spring dataJpa--> 37 <jpa:repositories base-package="cn.itcast.dao" transaction-manager-ref="transactionManager" 38 entity-manager-factory-ref="entityManagerFactory"></jpa:repositories> 39 <!--4.配置事務管理器--> 40 <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 41 <property name="entityManagerFactory" ref="entityManagerFactory"></property> 42 </bean> 43 <!--5.聲明式事務--> 44 <!--6.配置 包掃描--> 45 <context:component-scan base-package="cn.itcast"></context:component-scan> 46 </beans>
編寫符合SpringDataJpa規範的dao層介面
1 package cn.itcast.dao; 2 3 import cn.itcast.domain.Customer; 4 5 import org.springframework.data.jpa.repository.JpaRepository; 6 import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 7 /** 8 * 符合springDataJpa的dao層介面規範 9 * JpaRepository<操作的實體類類型,實體類中主鍵屬性的類型) >>>封裝了基本的CRUD操作 10 * JpaSpecificationExecutor<操作的實體類類型> >>>封裝了複雜查詢() 11 */ 12 public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {}
完成客戶的增刪改查操作
根據id查詢
- findOne
em.find() 立即載入
- getOne
@Transactional:保證getOne正常運行
em.getReference() 延遲載入 >>返回的是一個客戶的動態代理對象,什麼時候用、什麼時候查詢
1 @Test 2 public void testFindOne() { 3 Customer customer = customerDao.findOne(3l); 4 System.out.println(customer); 5 }
保存和更新(sava)
- 如果沒有id主鍵屬性:>> 保存
1 @Test 2 public void testSave() { 3 Customer customer = new Customer(); 4 customer.setCustname("黑馬程式員"); 5 customer.setCustaddress("北京"); 6 customer.setCustindustry("IT教育"); 7 customerDao.save(customer); 8 }
- 存在id主鍵屬性: >> 根據id查詢數據,更新數據
1 @Test 2 public void testUpdate() { 3 Customer customer = new Customer(); 4 customer.setCustid(1l); 5 customer.setCustname("播客"); 6 customer.setCustindustry("黑馬程式員很厲害"); 7 customerDao.save(customer); 8 }
根據id刪除(delete)
1 @Test 2 public void testDelete() { 3 Customer customer = new Customer(); 4 customerDao.delete(1l); 5 }
查詢所有客戶(findAll)
1 @Test 2 public void findAll(){ 3 List<Customer> list = customerDao.findAll(); 4 for (Customer customer : list) { 5 System.out.println(customer); 6 }
SpringDataJpa的運行過程和原理剖析
1. 通過JdkDynamicAopProxy的 invoke 方法創建了一個動態代理對象
2. simpleJpaRepository當中封裝了 JPA的操作(藉助JPA的api完成資料庫的CRUD)
3. 通過 hibernate完成資料庫操作(封裝了jdbc)
複雜查詢
-
藉助介面中的定義好的方法完成查詢
findone(id):根據id查詢
測試統計查詢:查詢客戶的總數量
1 @Test 2 public void testdount() { 3 long count = customerDao.count();//查詢全部的客戶數量System.out.println(count); 4 System.out.println(count); 5 }
測試:判斷id為3的客戶是否存在
- 可以查詢以下id為3的客戶
如果值為空,代表不存在,如果不為空,代表存在
- 判斷資料庫中id為3的客戶的數量
如果數量為0,代表不存在(false),如果大於0,代表存在(true)
1 @Test 2 public void testExists() { 3 boolean exists = customerDao.exists(3l); 4 System.out.println(exists); 5 }
-
jpql的查詢方式♦♦
jpql:jpa query language( jpq查詢語言 )
特點:語法或關鍵字和sql語句類似
查詢的是類和類中的屬性
需要將JPQL語句配置到介面方法上
1 .特有的查詢:需要在dao介面上配置方法
2. 在新添加的方法上,使用註解的形式配置jpql查詢語句
3. 註解:@Query
案例:根據客戶名稱查詢名稱 >> 使用Jpql的形式查詢
CustomerDao
1 public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> { 2 /** 3 * 案例:根據客戶名稱查詢名稱 4 * 使用Jpql的形式查詢 5 * jpql: from Customer where custName = ? 6 */ 7 @Query(value = "from Customer where custname = ?") 8 public Customer findJpql(String custname); 9 }
JpqlTest
1 @Test 2 public void testFindJpql(){ 3 Customer customer = customerDao.findJpql("傳智"); 4 System.out.println(customer); 5 }
案例:根據客戶名稱和客戶id查詢客戶 >> 使用Jpql的形式查詢
- 對於多個占位符參數: 賦值的時候,預設的情況下,占位符的位置需要和方法參數中的位置保持一致 [ custName >> String name custId >> Long id ]
@Query(value="from Customer where custName=? and custId=?")
public Customer findCustNameAndId (String name , Long id );
- 也可以指定占位符參數的位置 : ?索引的方式,指定此占位的取值來源
@Query (value=" from Customer where custName = ?2 and custId = ?1 ")
public Customer findCustNameAndId ( Long id , String name );
CustomerDao
1 @Query(value = "from Customer where custname = ? and custid = ?") 2 public Customer findCustNameAndId(String custname, long id);
JpqlTest
1 @Test 2 public void testCustNameAndId(){ 3 Customer customer = customerDao.findCustNameAndId("黑馬",2l); 4 System.out.println(customer); 5 }
使用jpql完成更新操作案例:根據id更新更新2號客戶的名稱,將名稱改為“黑馬程式員”
CustomerDao
1 @Query(value = " update Customer set custname = ?2 where custid = ?1 ") //代表的是進行查詢 2 @Modifying //當前執行的是一個更新操作 3 //更新不需要返回值 選擇void 4 public void UpdateCustomer( long custid,String custname);
JpqlTest
1 @Test 2 @Transactional //添加對事務的支持 3 @Rollback(value = false) //設置是否自動回滾 4 public void testUpdateCustomer(){ 5 customerDao.UpdateCustomer(2l,"黑馬程式員"); 6 }
執行結束後,預設回滾事務,可以通過@Rollback 設置是否自動回滾 >> false | true
-
SQL查詢方式.
1 .特有的查詢:需要在dao介面上配置方法
2. 在新添加的方法上,使用註解的形式配置sql查詢語句
3. 註解:@Query
value: jsql | sql
nativeQuery : fals(使用jpql查詢) l true(使用本地查詢:sql查詢)
CustomerDao
1 @Query(value = "select * from cst_customer",nativeQuery = true) 2 public List<Object[]>findSql();
JpqlTest
1 @Test 2 public void testfindSql(){ 3 List<Object[]> list = customerDao.findSql(); 4 for (Object[] obj : list) { 5 System.out.println(Arrays.toString(obj));//每一個裡面都還是object數組,所以需要藉助Arrays.toString()方法列印數組 6 }
-
方法命名規則查詢
>> 1: findBy +屬性名(首字母大寫)
public Customer findByCustname(String custname);
1 @Test 2 public void testNaming(){ 3 Customer customer = customerDao.findByCustname("傳智"); 4 System.out.println(customer); 5 }
>> 2: findBy + 屬性名(首字母大寫)+ “查詢方式”
public List<Customer> findByCustnameLike(String custname);
1 @Test 2 public void testfindByCustnameLike(){ 3 List<Customer> list = customerDao.findByCustnameLike("黑馬%"); 4 for (Customer customer : list) { 5 System.out.println(customer); 6 } }
>> 3: findBy + 屬性名(首字母大寫)+ “查詢方式” + “多條件的連接符(and | or)” + 屬性名 + “查詢方式”
使用客戶名稱模糊匹配和客戶所屬行業精準匹配的查詢
public List<Customer> findByCustnameLikeAndCustindustry(String custname, String custindustry); //結果可能是一個或多個,所以選擇List<Customer>
1 @Test 2 public void testFindByCustnameLikeAndCustindustry(){ 3 List<Customer> list = customerDao.findByCustnameLikeAndCustindustry("黑馬%","it教育"); 4 for (Customer customer : list) { 5 System.out.println(customer); 6 } }
Specifications動態查詢
- root :查詢的根對象(查詢的任何屬性都可以從根對象中獲取)
- cirteriaQuery:頂層查詢對象,自定義查詢方式(瞭解,一般不用)
- cirteriaBuilder:查詢的構造器,封裝了很多查詢條件
public Predicate toPredicate(Root<Customer> root , CriteriaQuery<?> query , CriteriaBuilder cb) { } //封裝查詢條件
查詢客戶名為 “傳智” 的客戶
1 @Test
2 public void testSpec() {
實現Specification介面( 提供泛型 :查詢的對象屬性)
3 Specification<Customer> spec = new Specification<Customer>() {
實現toPredicate方法(構造查詢條件)
4 public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
5 //1.獲取比較的屬性
6 Path<Object> custname = root.get("custname");
7 //2.構造查詢條件
8 Predicate predicate = cb.equal(custname, "傳智"); //進行精準的匹配(custname:比較的屬性 , “傳智”:比較的屬性取值)
9 return predicate;
10 }};
11 Customer customer = customerDao.findOne(spec);
12 System.out.println(customer);
13 }
查詢客戶名為 “黑馬2” 並且行業為 "it教育" 的的客戶
1 @Test 2 public void testSpec1() { 3 Specification<Customer> spec = new Specification<Customer>() { 4 public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) { 5 //1.獲取比較的屬性 6 Path<Object> custname = root.get("custname"); 7 Path<Object> custindustry = root.get("custindustry"); 8 //2.構造查詢條件 9 Predicate p1 = cb.equal(custname, "黑馬2"); 10 Predicate p2 = cb.equal(custindustry, "it教育");
and(與關係):滿足條件1並且滿足條件2 or(或關係):滿足條件1或滿足條件2 11 Predicate and = cb.and(p1, p2); 12 return and; 13 } 14 }; 15 Customer customer = customerDao.findOne(spec); 16 System.out.println(customer); 17 }
案例:完成根據客戶名稱的模糊匹配,返回客戶列表 >>客戶名稱以 “傳智播客” 開頭
預設: equal:直接得到 path對象(屬性),然後進行比較即可
gt(大於),lt(小於),ge(大於等於),le(小於等於),like:得到path對象,根據path指定比較的參數類型,再去進行比較
指定參數類型:path.as(類型的位元組碼對象)
1 @Test 2 public void testSpec2() { 3 Specification<Customer> spec = new Specification<Customer>() { 4 public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) { 5 Path<Object> custname = root.get("custname"); //查詢屬性:客戶名 6 Predicate predicate = cb.like(custname.as(String.class), "傳智播客%"); //查詢方式:模糊匹配 7 return predicate; 8 } 9 }; 10 List<Customer> list = customerDao.findAll(spec); 11 for (Customer customer : list) { 12 System.out.println(customer); 13 } }
添加排序 >>創建排序對象,需要調用構造方法實例化sort對象
第一個參數:排序的順序(倒序,正序)
- Sort.Direction.DESC:倒序
- Sort.Direction.ASC:升序
第二個參數:排序的屬性名稱
1 Sort sort = new Sort(Sort.Direction.DESC, "custid");
2 List<Customer> list = customerDao.findAll(spec, sort);
分頁查詢
- 不帶參數的分頁查詢
創建PageRequest的過程中,需要調用他的構造方法傳入兩個參數
第一個參數:當前查詢的頁數(從e開始)
第二個參數:每頁查詢的數量
1 @Test
2 public void testSpec3() {
3 Specification spec = null; //不帶參數
4 Pageable pageable = new PageRequest(0, 2);
5 //分頁查詢
6 Page<Customer> page = customerDao.findAll(null, pageable);
7 System.out.println(page.getContent());//得到數據集合列表
8 System.out.println(page.getTotalElements());//得到總條數
9 System.out.println(page.getTotalPages());//得到總頁數
10 }
- 帶參數分頁查詢
1 @Test 2 public void testSpec4() { 3 Specification spec = new Specification() { 4 public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) { 5 Path custname = root.get("custname"); 6 Predicate predicate = criteriaBuilder.equal(custname, "傳智播客"); 7 return predicate; 8 }}; 9 Pageable pageable = new PageRequest(0, 2); 10 //分頁查詢 11 Page<Customer> page = customerDao.findAll(spec, pageable); 12 System.out.println(page.getContent());//得到數據集合列表 13 System.out.println(page.getTotalElements());//得到總條數 14 System.out.println(page.getTotalPages());//得到總頁數 15 }