Spring Data JPA

對Spring Data JPA進行了一些研究,基本上是參考Spring Data JPA - Reference Documentation官方文檔。php

IntroductionDependenciesWorking with Spring Data RepositoriesRepositoriesQuery methodsQuery creationNamingStrategyUsing @QueryUsing SortUsing PageableSave methodsDelete methodsWhy use Spring Date JPAjava

Introduction

官方定義
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.git

要解釋清楚 Spring Data JPA 是什麼,那麼須要一步步提及:
期初 JAVA 須要經過各個數據庫廠商提供的API進行數據庫的訪問,後來 JAVA 提出了 JDBC,程序直接使用 JDBC 這套規範就能夠跟各個數據庫進行對接;
接着誕生了 ORM 技術,簡化了 Java 對象的持久化工做,出現了 Hibernate、 TopLink 等 ORM 框架;
Sun 公司在 JDK1.5 的時候,吸取了 Hibernate、 TopLink等 ORM 框架的優勢,提出了 Java 持久化規範:JPA;github

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

http://stackoverflow.com/questions/16148188/spring-data-jpa-versus-jpa-whats-the-difference
Long story short, then, Spring Data JPA provides a definition to implement repositories that is supported under the hood by referencing the JPA specification, using the provider you define.redis

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

Dependencies

具體步驟能夠參考官網:[spring-data-jpa/quick-start](https://spring.io/projects/spring-data-jpa)mongodb

<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>
複製代碼

Working with Spring Data Repositories

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

好比 findByName(String name) 就至關於 select * from user where name = ?

public interface UserCrudRepository extends CrudRepository<UserString>{

    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();
}
複製代碼

Repositories

Spring Date JPA提供了幾個接口:

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

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

Query methods

Query creation

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

public interface UserCrudRepository extends CrudRepository<UserString>{
    /**
     * CrudRepository 中存在的方法,這裏能夠不寫此方法 
     */

    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<UserString>{
    /**
     * 按照姓名查詢,非主鍵:
     * @param name
     * @return
     */

    List<User> findByName(String name);

    List<User> queryByName(String name);

    /**
     * 按照姓名查詢,而且年齡小於入參:
     * @param name
     * @return
     */

    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 = "")標籤進行關聯。

NamingStrategy

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

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

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

@Entity
@Table(name = "USER")
@Data
public 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
複製代碼

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

  • spring.jpa.hibernate.naming.physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    不作任何修改,userId→ userId,userid → userid
  • spring.jpa.hibernate.naming.physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
    將駝峯修改爲下劃線,userId → user_id,若是不作配置,默認這個策略
@Entity
@Table(name = "USER")
@Data
public class User {
  @Id
  private String userId;  //不須要在使用@Column(name = "user_id"),會自動轉成 user_id

  private String userName; //自動轉成 user_name
}
複製代碼

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

@Entity
@Table(name = "USER")
@Data
public 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 接口),在裏面去增長前綴名稱。

Using @Query

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

public interface UserCrudRepository extends CrudRepository<UserString>{
    //@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<UserString>{
    @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);
}
複製代碼

Using Sort

public interface UserCrudRepository extends CrudRepository<UserString>{
    List<User> findByOrderByUseridDesc()//按 Userid 倒序
}
複製代碼

也能夠繼承 PagingAndSortingRepository ,使用 Sort 進行排序:

public interface UserPageRepository extends PagingAndSortingRepository<UserString{  
    List<User> findAll(Sort sort);
}
複製代碼

Using Pageable

須要extends PagingAndSortingRepository或更高級的JpaRepository。

public class UserPageRepositoryTest {
  @Test
  public void findPageable() throws Exception 
{
    Page<User> usersPageOne = this.repository.findAll(new PageRequest(02)); 

    //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<UserString>{
    User findFirstByOrderByUseridDesc();

    User findFirstByGenderOrderByUseridDesc(String gender);
}
複製代碼

Save methods

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

Delete methods

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

在 JpaRepository 接口中,提供了 deleteInBatch() 的方法:
Deletes the given entities in a batch which means it will create a single {@link Query}.

Why 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);
}
複製代碼

會點代碼的大叔 | 文【原創】


會點代碼的大叔
會點代碼的大叔
相關文章
相關標籤/搜索