原創播客,如需轉載請注明出處。原文地址:http://www.cnblogs.com/crawl/p/7735616.html
----------------------------------------------------------------------------------------------------------------------------------------------------------
筆記中提供了大量的代碼示例,需要說明的是,大部分代碼示例都是本人所敲代碼並進行測試,不足之處,請大家指正~
本博客中所有言論僅代表博主本人觀點,若有疑惑或者需要本系列分享中的資料工具,敬請聯系 qingqing_crawl@163.com
-----------------------------------------------------------------------------------------------------------------------------------------------------------
前言:之前為大家詳細介紹了 JPA 的知識,之前提到 JPA 和 SpringData 結合才能發揮出無比巨大的威力。那么,今天樓主開始介紹 SpringData,寫此篇的目的主要是為了復習,如果能幫助到有需要的朋友,那再好不過了。
1. SpringData:Spring 的一個子項目。用於簡化數據庫訪問,支持NoSQL 和 關系數據存儲。其主要目標是使數據庫的訪問變得方便快捷。
2.JPA Spring Data:致力於減少數據訪問層 (DAO) 的開發量. 開發者唯一要做的,就只是聲明持久層的接口,其他都交給 Spring Data JPA 來完成!
二、Spring Data JPA 的 Helloworld
1.環境搭建:
3)加入 Spring 的 jar 包:spring-framework-4.0.0.RELEASE\required 目錄下所有
4)加入 Hibernate 和 JPA 的 jar 包:hibernate-release-4.2.4.Final\lib\required 和 hibernate-release-4.2.4.Final\lib\jpa 目錄下的所有
2.新建 Spring 的配置文件 applicationContext.xml,並進行數據源的配置
1) db.properties 文件
jdbc.user=root jdbc.password=qiqingqing jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql://localhost:3306/springdata
2)applicationContext.xml 文件中:
<!-- 配置自動掃描的包 -->
<context:component-scan base-package="com.software.springdata"></context:component-scan>
<!-- 1.配置數據源 -->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
</bean>
3)注意:希望大家能夠養成良好的單元測試的習慣,以便及時發現問題,及時解決:測試數據源是否獲取成功,新建一個 SpringDataTest 單元測試類
private ApplicationContext ctx = null; { ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); } //測試數據源是否獲取成功 @Test public void testDataSource() throws SQLException { DataSource dataSource = ctx.getBean(DataSource.class); System.out.println(dataSource.getConnection()); }
只要輸出數據庫連接的信息,那就證明我們數據源的的配置沒有問題了。
3.配置 JPA 的 EntityManagerFactory
<!-- 2.配置 JPA 的 EntityManagerFactory -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- 配置數據源 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 配置 JPA 提供商的適配器 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
</property>
<!-- 配置實體類所在包 -->
<property name="packagesToScan" value="com.software.springdata"></property>
<!-- 配置 JPA 實現產品的基本屬性 -->
<property name="jpaProperties">
<props>
<!-- 生成的數據表的列的映射策略 -->
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<!-- hibernate 基本屬性 -->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
測試 JPA 是否配置成功:
新建一個實體類 Person,添加必要的 JPA 注解,創建一個空的 JPA 的測試方法,執行此方法,查看是否生成數據表
package com.software.springdata; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; @Table(name="JPA_PERSONS") @Entity public class Person { private Integer id; private String lastName; private String email; private Date birth; private Integer addressId; @Column(name="ADD_ID") public Integer getAddressId() { return addressId; } public void setAddressId(Integer addressId) { this.addressId = addressId; } private Address address; @JoinColumn(name="ADDRESS_ID") @ManyToOne public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } @GeneratedValue @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name="LAST_NAME") public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Temporal(TemporalType.DATE) public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } @Override public String toString() { return "Person [id=" + id + ", lastName=" + lastName + ", email=" + email + ", birth=" + birth + "]"; } }
4.配置 JPA 的事務管理器和支持注解的事務
<!-- 3.配置 JPA 的事務管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!-- 4.配置支持注解的事務 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
5.配置 SpringData
1)加入 SpringData 的 jar 包:spring-data-jpa\required 目錄下所有(自創建的目錄),手動 Build Path
還要加入 SpringData 依賴的日志包 slf4j
2)加入 jpa 的命名空間,左下角 Namespaces 中添加
配置 SpringData 掃描的包和 EntityManagerFactory
<!-- 5.配置 Spring Data -->
<!-- 加入 jpa 的命名空間:左下角 Namespaces 中加入 -->
<!-- 配置 Spring Data 掃描的包和 EntityManagerFactory -->
<!-- base-package:掃描 Repository Bean 所在的 package -->
<jpa:repositories base-package="com.software.springdata"
entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
6.編寫 SpringData 的代碼,測試 HelloWorld
1)創建一個 PersonRepsotory 接口繼承 Repository 接口,聲明一個方法
2)編寫測試方法:需要在測試類中引入 PersonRepository 的實例,通過 ctx.getBean(PersonRepository.class); 獲取,以后不再贅述。
1 //測試 Spring Data 的 Helloworld 2 @Test 3 public void testSpringDataHelloworld() { 4 Person person = repository.getByLastName("AA"); 5 System.out.println(person); 6 }
這樣便會獲取 LastName 為 AA 的數據庫中的那條記錄。
三、Repository 接口
1. Repository 是一個空接口,即是一個標記接口。源碼

2.若我們定義的接口實現了 Repository,則該接口會被 IOC 容器識別為一個 Repository Bean,納入到 IOC 容器中,進而可以在該接口中定義一些符合規范的方法
3.還可以通過 @RepositoryDefinition 注解來替代繼承 Repository 接口

4.Repository 接口的實現類:
1)CrudRepository: 繼承 Repository,實現了一組 CRUD 相關的方法
2)PagingAndSortingRepository: 繼承 CrudRepository,實現了一組分頁排序相關的方法
3)JpaRepository: 繼承 PagingAndSortingRepository,實現一組 JPA 規范相關的方法
4)自定義的 XxxxRepository 需要繼承 JpaRepository,這樣的 XxxxRepository 接口就具備了通用的數據訪問控制層的能力。
注:JpaSpecificationExecutor: 不屬於Repository體系,實現一組 JPA Criteria 查詢相關的方法
四、SpringData 的方法定義規范SpringData 的方法定義規范
1.不能隨便聲明,需要符合一定的規范
2.查詢的方法以 find、read、get 開頭
3.涉及條件查詢時條件的屬性要以關鍵字連接,條件屬性首字母大寫
1)SpringData 支持的關鍵字


2)舉例:
1 //WHERE lastName like %? AND id < ? 2 List<Person> getByLastNameEndingWithAndIdLessThan(String lastName, Integer age); 3 4 //WHERE lastName like ?% AND id > ? 5 List<Person> getByLastNameStartingWithAndIdGreaterThan(String lastName, Integer age); 6 7 //WHERE email IN(?,?,?) OR birth < ? 8 List<Person>getByEmailInOrBirthLessThan
(List<String> emails, Date date);
測試:
1 //測試 SpringData 支持的關鍵字 2 @Test 3 public void testKeyWords() { 4 List<Person> persons = repository.getByLastNameEndingWithAndIdLessThan("A", 5); 5 System.out.println(persons); 6 7 persons = repository.getByLastNameStartingWithAndIdGreaterThan("A", 1); 8 System.out.println(persons); 9 10 persons = repository.getByEmailInOrBirthLessThan(Arrays.asList("aa@163.com", "cc@163.com", 11 "ee@163.com"), new Date()); 12 System.out.println(persons.size()); 13 }
4.支持級聯查詢,若當前類有符合條件的查詢,則優先使用,而不使用級聯查詢,若需要使用級聯查詢,則屬性之間需要使用 _ (下划線)來連接
1)創建 Address 類,添加對應的 JPA 注解,在 Person 類中添加 Address 類的引用,使用 @ManyToOne 映射,生成對應的數據表


2)創建測試方法:此時為級聯查詢


3)為 Person 類添加 addressId 屬性后,若再進行相同的測試,則不是級聯查詢了,而查詢條件為 Person 類中的 addressId 屬性。若要再進行級聯查詢,需要在屬性之間需要使用 _ (下划線)來連接


五、@Query 注解
1.使用 @Query 注解可以自定義 JPQL 語句實現更加靈活的查詢
定義方法:
1 //查詢 id 值最大的 Person 2 //使用 @Query 注解可以自定義 JPQL 語句實現更加靈活的查詢 3 @Query("SELECT p FROM Person p WHERE p.id = (SELECT max(p2.id) FROM Person p2)") 4 Person getMaxIdPerson();
測試方法:
//測試 @Query 注解 @Test public void testQueryAnnotation() { Person person = repository.getMaxIdPerson(); System.out.println(person); }
2.為 @Query 注解傳遞參數的兩種方式
1)使用占位符:
1 //為 @Query 注解傳遞參數的方式1:使用占位符, 此方式要求形參與定義的 JPQL 的參數位置一致 2 @Query("SELECT p FROM Person p WHERE p.lastName = ?1 AND p.email = ?2") 3 List<Person> testQueryAnnotationParam1(String lastName, String email);
測試:
1 //測試向 @Query 注解傳參 2 @Test 3 public void testQueryAnnotationParam1() { 4 List<Person> persons = repository.testQueryAnnotationParam1("AA", "aa@163.com"); 5 System.out.println(persons); 6 }
2)使用命名參數:
1 //為 @Query 注解傳遞參數的方式2:使用命名參數, 此方式形參與定義的 JPQL 的參數位置不必一致 2 @Query("SELECT p FROM Person p WHERE p.lastName = :lastName AND p.email = :email") 3 List<Person> testQueryAnnotationParam2(@Param("email") String email, @Param("lastName") String lastName);
1 //測試向 @Query 注解傳參 2 @Test 3 public void testQueryAnnotationParam2() { 4 List<Person> persons = repository.testQueryAnnotationParam2("aa@163.com", "AA"); 5 System.out.println(persons); 6 }
3.帶有 LIKE 關鍵字的 @Query 注解
1)需要在測試方法中手動加上 %,不推薦
1 //帶有 LIKE 關鍵字的 @Query 注解 2 @Query("SELECT p FROM Person p WHERE p.lastName LIKE ?1 AND p.email LIKE ?2") 3 List<Person> testQueryAnnotationLikeParam(String lastName, String email);
1 //測試帶有 LIKE 關鍵字的 @Query 注解 2 @Test 3 public void testQueryAnnotationLikeParam() { 4 List<Person> persons = repository.testQueryAnnotationLikeParam("%A%", "%aa%"); 5 System.out.println(persons); 6 }
2)SpringData 允許在占位符上添加 %
1 //帶有 LIKE 關鍵字的 @Query 注解:SpringData 允許在占位符上添加 % 2 @Query("SELECT p FROM Person p WHERE p.lastName LIKE %?1% AND p.email LIKE %?2%") 3 List<Person> testQueryAnnotationLikeParam2(String lastName, String email);
1 //測試帶有 LIKE 關鍵字的 @Query 注解 2 @Test 3 public void testQueryAnnotationLikeParam2() { 4 List<Person> persons = repository.testQueryAnnotationLikeParam2("A", "aa"); 5 System.out.println(persons); 6 }
3)SpringData 允許在命名參數上添加 %
//帶有 LIKE 關鍵字的 @Query 注解:SpringData 允許在命名參數上添加 % @Query("SELECT p FROM Person p WHERE p.lastName LIKE %:lastName% AND p.email LIKE %:email%") List<Person> testQueryAnnotationLikeParam3(@Param("lastName") String lastName, @Param("email") String email);
1 //測試帶有 LIKE 關鍵字的 @Query 注解 2 @Test 3 public void testQueryAnnotationLikeParam3() { 4 List<Person> persons = repository.testQueryAnnotationLikeParam3("A", "aa"); 5 System.out.println(persons); 6 }
4.使用 @Query 執行本地 SQL 查詢,在 @Query 注解中添加參數 nativeQuery=true
//本地 SQL 查詢:設置 nativeQuery=true 即可執行本地查詢 @Query(value="SELECT count(id) FROM jpa_persons", nativeQuery=true) long getTotalCount();
1 //測試本地 SQL 查詢 2 @Test 3 public void testNativeQuery() { 4 long count = repository.getTotalCount(); 5 System.out.println(count); 6 }
六、@Modifying 注解
1.使用 @Modifying 配合 @Query 可以完成 UPDATE 和 DELETE 操作
2.可以通過自定義 JPQL 完成 UPDATE 和 DELETE 操作,注:JPQL 不支持使用 INSERT
3.在 @Query 中編寫 JPQL 語句,但必須使用 @Modifying 注解修飾,以通知 SpringData 此操作是一個 UPDATE 或 DELETE 操作
4.UPDATE 或 DELETE 操作需要使用事務,所以需要定義 service 層,在 service 層方法上添加事務操作
5.示例:
1 /* 2 * 可以通過自定義 JPQL 完成 UPDATE 和 DELETE 操作,注:JPQL 不支持使用 INSERT 3 * 在 @Query 中編寫 JPQL 語句,但必須使用 @Modifying 注解修飾,以通知 SpringData 此操作是一個 UPDATE 或 DELETE 操作 4 * UPDATE 或 DELETE 操作需要使用事務,所以需要定義 service 層,在 service 層方法上添加事務操作 5 * 默認情況下,SpringData 的每個方法上都有事務,但都是只讀事務,他們不能完成修改操作 6 */ 7 @Modifying 8 @Query("UPDATE Person p SET p.email = :email WHERE p.id = :id") 9 void updatePersonEmail(@Param("id") Integer id, @Param("email") String email);


七、Repository 接口的子接口
1、CrudRepository 接口;測試此接口的 save(Iterable<S> entities) 方法進行批量保存
1)使自定義的接口繼承 CrudRepository 接口

2)Service 層:需要事務

3)測試:

2. PagingAndSortingRepository 接口
1)實現分頁操作(帶排序):(只讀事務,不需要再 service 層中編寫)

//測試 PagingAndSortRepository 的 findAll(Pageable pageable) 方法,進行分頁 @Test public void testPagingAndSortingRepository() { //pageNo 從 0 開始 int pageNo = 3 - 1; int pageSize = 5; //Sort 封裝了排序信息,Order 指明具體是根據哪一個屬性進行升序或者降序 Order order1 = new Order(Direction.DESC, "id"); Order order2 = new Order(Direction.ASC, "email"); Sort sort = new Sort(order1, order2); //Pageable 接口通常使用其 PageRequest 實現類,其中封裝了需要分頁的信息 PageRequest pageable = new PageRequest(pageNo, pageSize, sort); Page<Person> page = pagingAndSortingRepository.findAll(pageable); System.out.println("總共有 " + page.getTotalElements() + " 條記錄"); System.out.println("總共有 " + page.getTotalPages() + " 頁"); System.out.println("當前頁為:" + (page.getNumber() + 1)); System.out.println("當前的 List: " + page.getContent()); System.out.println("當前頁的總記錄數為:" + page.getNumberOfElements()); }
3. JpaRepository 接口:測試此接口的 saveAndFlush() 方法,此方法相當於 JPA 中的 merge() 方法,詳見 JPA 筆記


補:JpaSpecificationExecutor 接口,不屬於Repository體系,實現一組 JPA Criteria 查詢相關的方法 ,即帶條件的分頁查詢
1)自定義的接口必須實現 Repository 或 Repository 的子接口 還有 JpaSpecificationExecutor 接口


八、自定義 Repository 方法:為某一個 Repository 上添加自定義方法
1.步驟:
1)定義一個接口: 聲明要添加的, 並自實現的方法

2)提供該接口的實現類: 類名需在要聲明的 Repository(以 PersonRepository 為例) 后添加 Impl, 並實現方法

3)聲明 Repository 接口(即 PersonRepository 接口), 並繼承 1) 聲明的接口

4)測試使用:

----------------------------------------------------------------------------------------------------------------
相關鏈接:
JPA + SpringData 操作數據庫原來可以這么簡單 ---- 深入了解 JPA - 1
JPA + SpringData 操作數據庫原來可以這么簡單 ---- 深入了解 JPA - 2
JPA + SpringData 操作數據庫原來可以這么簡單 ---- 深入了解 JPA - 3



