Spring Data JPA 入門,有這一篇就夠了!

最近對 Spring Data JPA 進行了一些研究,基本上是參考 Spring Data JPA - Reference Documentation 官方文檔,總結分享出來,你們能夠了解一下 Spring Data JPA 的基本用法。
html




01Introduction

java



官方定義:git

Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.github


要解釋清楚 Spring Data JPA 是什麼,那麼須要一步步提及:redis


  • 期初 JAVA 須要經過各個數據庫廠商提供的API進行數據庫的訪問,後來 JAVA 提出了 JDBC,程序直接使用 JDBC 這套規範就能夠跟各個數據庫進行對接;spring

  • 接着誕生了 ORM 技術,簡化了 Java 對象的持久化工做,出現了 Hibernate、 TopLink 等 ORM 框架;mongodb

  • Sun 公司在 JDK1.5 的時候,吸取了 Hibernate、 TopLink等 ORM 框架的優勢,提出了 Java 持久化規範:JPA ;數據庫

  • Hibernate 在 3.2 的時候提供了 JPA 的實現,其他的 JPA 的供應商還有諸如 OpenJPA、 Toplink等;Spring 在作持久化這一塊的工做,開發了 Spring-data-xxx 這一系列包,如:Spring-data-jpa, Spring-data-redis, Spring-data-mongodb 等等,這些都是 Spring 提供的基於 JPA 和其餘一些 NOSQL 的 Repository。api


Spring Data JPA 是在 JPA 規範的基礎下提供了 Repository 層的實現,可是使用哪一款 ORM 須要你本身去決定;相比咱們更爲熟悉的 Hibernate 和 MyBatis, Spring Data JPA 能夠看作更高層次的抽象。app




02Dependencies



具體步驟能夠參考官網:[spring-data-jpa/quick-start]


<dependencies>        <dependency>            <groupId>org.springframework.data</groupId>            <artifactId>spring-data-jpa</artifactId>        </dependency></dependencies>


若是要在Spring Boot下使用Spring Data JPA的話,須要引入:


<dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter-data-jpa</artifactId></dependency>




03Working with Spring Data Repositories



讓咱們看一個簡單的例子,實現增刪查改的功能;從例子中咱們能夠發現,能夠經過方法名稱的定義,就能夠達到 SQL 的效果:


好比 findByName(String name) 就至關於:


select * from user where name = ?


public interface UserCrudRepository                   extends CrudRepository<User, String>{        User findOne(String userid);        List<User> findByName(String name);        List<User> findByNameAndAgeLessThan(String name, int age);        void deleteByNameAndAgeLessThan(String name, int age);        List<User> findDistinctByName(String name);        List<User> findByNameIgnoreCase(String name);        User findFirstByOrderByUseridDesc();}




04Repositories



Spring Date JPA提供了幾個接口:


  • Repository:最頂層的接口,是一個空的接口,目的是爲了統一全部Repository的類型,且能讓組件掃描的時候自動識別。
  • CrudRepository:是Repository的子接口,提供CRUD的功能。
  • PagingAndSortingRepository :是CrudRepository的子接口,添加分頁和排序的功能。
  • JpaRepository :是PagingAndSortingRepository的子接口,增長了批量操做等功能。


以上四個 XxxRepositoty 愈來愈具體、功能愈來愈豐富,從使用的角度應該是繼承本身用到的最小集,如需擴展再作調整,嫌麻煩的話直接繼承 JpaRepository 也不要緊。




05Query creation



能夠直接使用 CrudRepository 中的 findOne 和 findAll 方法:



public interface UserCrudRepository                   extends CrudRepository<User, String>{    /**     * CrudRepository 中存在的方法,這裏能夠不寫此方法     * CrudRepository中存在的方法:     * https://github.com/spring-projects/spring-data-commons/blob/master/src/main/java/org/springframework/data/repository/CrudRepository.java#L27     */    User findOne(String userid);    /**     * 查詢全部 能夠不寫此方法     * 不寫,須要在查詢完成後強轉:List<User> userAfterDel = (List<User>) this.repository.findAll();     * 寫:List<User> userAfterDel = this.repository.findAll();     */    //List<User> findAll();}


也能夠對查詢方法進行擴展,擴展的格式爲:find…By, read…By, query…By, get…By, count…By 。


public interface UserCrudRepository                    extends CrudRepository<User, String>{    /**     * 按照姓名查詢,非主鍵 更多用法參考:     * http://docs.spring.io/spring-data/jpa/docs/1.10.6.RELEASE/reference/html/#new-features.1-10-0     * Table 4. Supported keywords inside method names     *     * @param name     * @return     */    List<User> findByName(String name);     List<User> queryByName(String name);     List<User> findByNameAndAgeLessThan(String name, int age);     List<User> findDistinctByName(String name);     List<User> findByNameIgnoreCase(String name);}


若是數據庫字段帶有[ _ ]符號的,JAVA的Entity類中對應的屬性不能帶有[ _ ],須要經過@Column(name = "")標籤進行關聯。


若是JAVA的Entity類中屬性按照駝峯格式命名,也須要經過@Column(name = "")標籤進行關聯。




06Naming Strategy



解決上面的第二點問題:若是 JAVA 的 Entity 類中屬性按照駝峯格式命名,也須要經過 @Column(name = "") 標籤進行關聯。


JPA 中的 NamingStrategy 接口(最早是 Hibernate 中提出),實現接口中的表名與列名命名函數,能夠完成本身的命名策略設定。


舉個例子,咱們常常會遇到這樣的問題,表的列明都是帶的:user_id,user_name…,若是 User.java 中的屬性都是不帶  的,那麼須要使用 @Column 標籤進行和數據庫字段的綁定:


@Entity@Table(name = "USER")@Datapublic class User {  @Column(name = "user_id")  @Id  private String userId;   @Column(name = "user_name")  //若是這樣的字段不少,也是一個比較大的工做量  private String userName;}


而經過 NamingStrateg y接口的實現,能夠統一的完成駝峯轉下劃線的功能,userId → user_id 、 userName → user_name;


Spring Boot提供了Configure JPA properties,咱們能夠能夠直接在application.yml中進行配置:


spring.jpa.hibernate.naming.physical-strategy = com.example.MyPhysicalNamingStrategy



解決上面的第二點問題:若是 JAVA 的 Entity 類中屬性按照駝峯格式命名,也須要經過 @Column(name = "") 標籤進行關聯。


在Hibernate5中,提供了兩種映射策略:


--不作任何修改,userId→ userIduserid → useridspring.jpa.hibernate.naming.physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl        
--將駝峯修改爲下劃線,userIduser_id,若是不作配置,默認這個策略spring.jpa.hibernate.naming.physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy  


@Entity@Table(name = "USER")@Datapublic class User {  @Id  private String userId;  //不須要在使用@Column(name = "user_id"),會自動轉成 user_id   private String userName; //自動轉成 user_name}



同時會有一個問題,若是數據庫中表的列名規則不統一,好比有些帶下劃線,有些不帶下劃線的話,那麼在編寫Entity的時候,帶下劃線的字段寫成駝峯格式,不帶下劃線的字段寫成全小寫,看起來也比較怪異。


@Entity@Table(name = "USER")@Datapublic class User {  @Id  private String userId;  //不須要在使用@Column(name = "user_id")   private String userName;   private String cardid;  //列名爲 cardid ,不帶下劃線,因此這裏不能寫成駝峯格式,可是從代碼風格上看,感受比較怪異}


若是有更特殊的要求,例如數據庫中全部的表或者字段都帶固定格式的前綴,CSC_USER 表,字段有CSC_USER_ID, CSC_USER_NAME,能夠本身去實現 NamingStrategy 接口(若是是 Spring Boot 的話,須要實現 PhysicalNamingStrategy 接口),在裏面去增長前綴名稱。




07Naming Strategy



使用 @Query Annotation 綁定SQL語句(這樣寫,看起來是否是更直觀):


public interface UserCrudRepository                   extends CrudRepository<User, String>{    //@Query("select u from User u where u.name = ?1")    //@Query("select u from User u where 1=1 and u.name = ?1")    //@Query("select u.userid from User u where 1=1 and u.name = ?1")    //@Query(value="select * from User u where u.name = ?1", nativeQuery = true)    List<User> findByName(String name);}




使用 @Param Annotation 能夠將參數中的名字和query中的名字進行綁定:


public interface UserCrudRepository                    extends CrudRepository<User, String>{    @Query("select u from User u where u.name = :name and u.gender = :gender")    List<User> findUsersByNameAndGender(@Param("name")String name , @Param("gender")String gender);}




08Using Sort



public interface UserCrudRepository                    extends CrudRepository<User, String>    //可使用 UserCrudRepository    //在方法使用 OrderBy    List<User> findByOrderByUseridDesc();}


public interface UserPageRepository                   extends PagingAndSortingRepository<User, String> {      //使用 Sort     List<User> findAll(Sort sort);}




09Using Pageable



須要繼承PagingAndSortingRepository 或更高級的 JpaRepository 。


public class UserPageRepositoryTest {  @Test  public void findPageable() throws Exception {    Page<User> usersPageOne = this.repository.findAll(new PageRequest(0, 2)); //1.PageRequest(int page, int size) 2.page從0開始     assertThat(usersPageOne.getContent().size()).isEqualTo(2);    assertThat(usersPageOne.getContent().get(0).getUserId()).isEqualTo("1");    assertThat(usersPageOne.hasPrevious()).isEqualTo(false);  //判斷是否有上一頁     Page<User> usersPageTwo = this.repository.findAll(usersPageOne.nextPageable());  //查詢下一頁    assertThat(usersPageTwo.getContent().size()).isEqualTo(2);    assertThat(usersPageTwo.getContent().get(0).getUserId()).isEqualTo("3");  }}


也能夠經過 [first] 或 [top] 關鍵字,取得結果集的前N條數據。


public interface UserCrudRepository                    extends CrudRepository<User, String>{    User findFirstByOrderByUseridDesc();    User findFirstByGenderOrderByUseridDesc(String gender);}




10Save and Delete methods



保存一個 Entity 須要使用 CrudRepository.save(…) 方法,包括 Insert 和 Update。


Delete 方法基本能夠參考 Query 方法,除了 delete() 和 deleteAll() 以外,還可使用 deleteBy… 的方式,一樣也可使用 @query 標籤。


在 JpaRepository 接口中,提供了 deleteInBatch() 的方法:


Deletes the given entities in a batch which means it will create a single {@link Query}.




11Why use Spring Date JPA



能夠看出 Spring Data JPA 和經常使用的 Hibernate 和 MyBatis 相比,在編碼上更簡潔,減小 Boilerplate Code


實際上交易型的微服務應用的查詢一般只是明細查詢或簡單的列表查詢,而真正的複雜查詢或者數據分析一般應是另建應用或者全文檢索或者 ODS/DW 之類、並從交易型微服務同步數據,而這一部分是走 MyBatis 仍是 NOSQL 另說。


Feign 雖然解決的不是 Spring Data Repository 同一個領域的問題,可是實現哲學是一致的,經過接口、COC 儘可能減小 Rest 調用的 Boilerplate Code。


@FeignClient(url = "https://api.github.com")interface GitHubClient {      @RequestMapping(method = RequestMethod.GET, value = "/repos/{owner}/{repo}/contributors")    List<Contributor> contributors(@RequestParam("owner") String owner, @RequestParam("repo") String repo); }
相關文章
相關標籤/搜索