Spring Data JPA 的基本使用

1    第3-4課: Spring Data JPA 的基本使用

Spring Data JPA 是 Spring Boot 體系中約定優於配置的最佳實現,大大簡化了項目中數據庫的操做。從本課開始將會從 JPA 的由來開始講解,什麼是 JPA、Spring Boot JPA 的實現,以及如何使用。java

1.1     概念

1.1.1   JPA 由來

ORM 框架可以將 Java 對象映射到關係數據庫中,可以直接持久化複雜的 Java 對象。ORM 框架的出現,可讓開發者從數據庫編程中解脫出來,把更多的精力放在了業務模型與業務邏輯上。目前比較流行的 ORM 框架有 Hibernate、MyBatis、TopLink、Spring JDBC 等。mysql

在 JPA 規範以前,因爲沒有官方的標準,使得各 ORM 框架之間的 API 差異很大,使用了某種 ORM 框架的系統會嚴重受制於該 ORM 的標準。基於此,Sun 引入新的 JPA ORM,主要的緣由有:其一,簡化現有 Java EE 和 Java SE 應用開發工做;其二,Sun 但願整合 ORM 技術,實現統一的 API 調用接口。git

1.1.2   JPA 是什麼

JPA(Java Persistence API)是 Sun 官方提出的 Java 持久化規範。它爲 Java 開發人員提供了一種對象 / 關聯映射工具來管理 Java 應用中的關係數據。它的出現主要是爲了簡化現有的持久化開發工做和整合 ORM 技術,結束如今 Hibernate、TopLink、JDO 等 ORM 框架各自爲營的局面。github

值得注意的是,JPA 是在充分吸取了現有的 Hibernate、TopLink、JDO 等 ORM 框架的基礎上發展而來的,具備易於使用、伸縮性強等優勢。從目前的開發社區的反應上看,JPA 受到了極大的支持和讚賞,其中就包括了 Spring 與 EJB 3.0 的開發團隊。spring

注意:JPA 是一套規範,不是一套產品,那麼像 Hibernate、TopLink、JDO 它們是一套產品,若是說這些產品實現了這個 JPA 規範,那麼咱們就能夠稱他們爲 JPA 的實現產品。sql

1.1.3   Spring Data JPA

Spring Data JPA 是 Spring 基於 ORM 框架、JPA 規範的基礎上封裝的一套 JPA 應用框架,可讓開發者用極簡的代碼便可實現對數據的訪問和操做。它提供了包括增、刪、改、查等在內的經常使用功能,且易於擴展,學習並使用 Spring Data JPA 能夠極大提升開發效率。Spring Data JPA 其實就是 Spring 基於 Hibernate 之上構建的 JPA 使用解決方案,方便在 Spring Boot 項目中使用 JPA 技術。數據庫

Spring Data JPA 讓咱們解脫了 DAO 層的操做,基本上全部 CRUD 均可以依賴於它實現。編程

1.2     快速上手

1.2.1   添加依賴

<dependency>
<groupId></groupId>   org.Springframework.boot
<artifactId></artifactId>   spring-boot-starter-data-jpa
</dependency>
<dependency>
<groupId></groupId>   mysql
<artifactId></artifactId>   mysql-connector-java
</dependency>

1.2.2   添加配置文件

testtruetruespring.datasource.url=jdbc:mysql://localhost:3306/?serverTimezone=UTC&useUnicode=&characterEncoding=utf-8&useSSL=
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
 
spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
#SQL 輸出
truespring.jpa.show-sql=
#format 一下 SQL 進行輸出
truespring.jpa.properties.hibernate.format_sql=

hibernate.hbm2ddl.auto 參數的做用主要用於:自動建立、更新、驗證數據庫表結構,有四個值。服務器

  • create:每次加載 Hibernate 時都會刪除上一次生成的表,而後根據 model 類再從新來生成新表,哪怕兩次沒有任何改變也要這樣執行,這就是致使數據庫表數據丟失的一個重要緣由。
  • create-drop:每次加載 Hibernate 時根據 model 類生成表,可是 sessionFactory 一關閉,表就自動刪除。
  • update:最經常使用的屬性,第一次加載 Hibernate 時根據 model 類會自動創建起表的結構(前提是先創建好數據庫),之後加載 Hibernate 時根據 model 類自動更新表結構,即便表結構改變了,但表中的行仍然存在,不會刪除之前的行。要注意的是當部署到服務器後,表結構是不會被立刻創建起來的,是要等應用第一次運行起來後纔會。
  • validate :每次加載 Hibernate 時,驗證建立數據庫表結構,只會和數據庫中的表進行比較,不會建立新表,可是會插入新值。

其中:session

  • dialect 主要是指定生成表名的存儲引擎爲 InneoDB
  • show-sql 是否在日誌中打印出自動生成的 SQL,方便調試的時候查看

1.2.3   實體類

@Entity
publicclass User {
 
@Id   
@GeneratedValue   
private   Long id;
@Columnfalsetrue   (nullable =,unique =)
private   String userName;
@Columnfalse   (nullable =)
private   String passWord;
@Columnfalsetrue   (nullable =,unique =)
private   String email;
@Columntruetrue   (nullable =,unique =)
private   String nickName;
@Columnfalse   (nullable =)
private   String regTime;
 
//省略 getter settet 方法、構造方法   
 
}

下面對上面用的註解作一個解釋。

  • @Entity(name="EntityName") 必須,用來標註一個數據庫對應的實體,數據庫中建立的表名默認和類名一致。其中,name 爲可選,對應數據庫中一個表,使用此註解標記 Pojo 是一個 JPA 實體。
  • @Table(name=""catalog=""schema="") 可選,用來標註一個數據庫對應的實體,數據庫中建立的表名默認和類名一致。一般和 @Entity 配合使用,只能標註在實體的 class 定義處,表示實體對應的數據庫表的信息。
  • @Id 必須,@Id 定義了映射到數據庫表的主鍵的屬性,一個實體只能有一個屬性被映射爲主鍵。
  • @GeneratedValue(strategy=GenerationTypegenerator="") 可選,strategy: 表示主鍵生成策略,有 AUTO、INDENTITY、SEQUENCE 和 TABLE 4 種,分別表示讓 ORM 框架自動選擇,generator: 表示主鍵生成器的名稱。
  • @Column(name = "user_code" nullable = false length=32) 可選,@Column 描述了數據庫表中該字段的詳細定義,這對於根據 JPA 註解生成數據庫表結構的工具。name: 表示數據庫表中該字段的名稱,默認情形屬性名稱一致;nullable: 表示該字段是否容許爲 null,默認爲 true;unique: 表示該字段是不是惟一標識,默認爲 false;length: 表示該字段的大小,僅對 String 類型的字段有效。 
  • @Transient可選,@Transient 表示該屬性並不是一個到數據庫表的字段的映射,ORM 框架將忽略該屬性。
  • @Enumerated 可選,使用枚舉的時候,咱們但願數據庫中存儲的是枚舉對應的 String 類型,而不是枚舉的索引值,須要在屬性上面添加 @Enumerated(EnumType.STRING) 註解。

1.2.4   Repository 構建

建立的 Repository 只要繼承 JpaRepository 便可,就會幫咱們自動生成不少內置方法。另外還有一個功能很是實用,能夠根據方法名自動生產 SQL,好比 findByUserName 會自動生產一個以 userName 爲參數的查詢方法,好比 findAll 會自動查詢表裏面的全部數據等。

publicinterface UserRepository extends JpaRepository<UserLong> {
User findByUserName(String userName)   ;
User findByUserNameOrEmail(String username,String email)   ;
}   

咱們只須要在對應的 Repository 中建立好方法,使用的時候直接將接口注入到類中調用便可。在 IDEA 中打開類 UserRepository,在這個類的大括號內的區域右鍵單擊,選擇 Diagrams | Show Diagram 選項,便可打開類圖,以下:

 

經過上圖咱們發現 JpaRepository 繼承 PagingAndSortingRepository 和 QueryByExampleExecutor,PagingAndSortingRepository 類主要負責排序和分頁內容,QueryByExampleExecutor 提供了不少示例的查詢方法,以下:

publicinterface QueryByExampleExecutor<T> {
S findOne(Example<S> example)//根據實例查找一個對象    <S extends T>;  
Iterable<S> findAll(Example<S> example)//根據實例查找一批對象    <S extends T>;    
Iterable<S> findAll(Example<S> example, Sort sort)//根據實例查找一批對象,且排序    <S extends T>; 
Page<S> findAll(Example<S> example, Pageable pageable)//根據實例查找一批對象,且排序和分頁    <S extends T>; 
long count(Example<S> example)//根據實例查找,返回符合條件的對象個數    <S extends T>; 
boolean exists(Example<S> example)//根據實例判斷是否有符合條件的對象    <S extends T>; 
}

所以,繼承 JpaRepository 的會自動擁有上述這些方法和排序、分頁功能。查看源碼咱們發現 PagingAndSortingRepository 又繼承了 CrudRepository。CrudRepository 的源碼以下:

@NoRepositoryBean
publicinterface CrudRepository<T, ID> extends Repository<T, ID> {
S save(S entity)    <S extends T>;
Iterable<S> saveAll(Iterable<S> entities)    <S extends T>;
Optional<T> findById(ID id)   ;
boolean existsById(ID id)   ;
Iterable<T> findAll()   ;
Iterable<T> findAllById(Iterable<ID> ids)   ;
long count()   ;
void deleteById(ID id)   ;
void delete(T entity)   ;
void deleteAll(Iterable<? extends T> entities)   ;
void deleteAll()   ;
}

從 CrudRepository 的源碼能夠看出 CrudRepository 內置了咱們最經常使用的增、刪、改、查的方法,方便咱們去使用,由於 JpaRepository 繼承了 PagingAndSortingRepository,PagingAndSortingRepository 繼承了 CrudRepository,因此繼承 JpaRepository 的類也默認擁有了上述方法。

所以使用 JPA 操做數據庫時,只須要構建的 Repository 繼承了 JpaRepository,就會擁有了不少經常使用的數據庫操做方法。

1.2.5   測試

建立好 UserRepository 以後,當業務代碼中須要使用時直接將此接口注入到對應的類中,在 Spring Boot 啓動時,會自動根據註解內容建立實現類並注入到目標類中。

@RunWith(SpringRunner.class)
@SpringBootTest
publicclass UserRepositoryTests {
 
@Resource   
private   UserRepository userRepository;
 
@Test   
public void test()     {
new        Date date =Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG);       
        String formattedDate = dateFormat.format(date);
 
new"aa""aa@126.com""aa""aa123456"        userRepository.save(User(,,,,formattedDate));
new"bb""bb@126.com""bb""bb123456"        userRepository.save(User(,,,,formattedDate));
new"cc""cc@126.com""cc""cc123456"        userRepository.save(User(,,,,formattedDate));
 
9        Assert.assertEquals(,userRepository.findAll().size());
"bb""bb""cc@126.com"        Assert.assertEquals(,userRepository.findByUserNameOrEmail(,).getNickName());
"aa1"        userRepository.delete(userRepository.findByUserName());
    }
 
}

上述測試方法簡單測試了 JPA 的報錯和查詢功能,測試用例執行成功表示 JPA 的增、刪、改爲功。

1.3     基本查詢

咱們能夠將 Spring Data JPA 查詢分爲兩種,一種是 Spring Data JPA 默認實現的,另外一種是須要根據查詢的狀況來自行構建。

1.3.1   預生成方法

預生成方法就是咱們上面看到的那些方法,由於繼承了 JpaRepository 而擁有了父類的這些內容。

(1)繼承 JpaRepository

publicinterface UserRepository extends JpaRepository<UserLong> {
}

(2)使用默認方法

Test@
public void testBaseQuery() {
    userRepository.findAll();
1l    userRepository.findById();
    userRepository.save(user);
delete    userRepository.(user);
    userRepository.count();
1l    userRepository.existsById();
// ...   
}

全部父類擁有的方法均可以直接調用,根據方法名也能夠看出它的含義。

1.3.2   自定義查詢

Spring Data JPA 能夠根據接口方法名來實現數據庫操做,主要的語法是 findXXBy、readAXXBy、queryXXBy、countXXBy、getXXBy 後面跟屬性名稱,利用這個功能僅須要在定義的 Repository 中添加對應的方法名便可,使用時 Spring Boot 會自動幫咱們實現,示例以下。

根據用戶名查詢用戶:

User findByUserName(String userName);

也能夠加一些關鍵字 And、or:

StringStringUser findByUserNameOrEmail(username,email);

修改、刪除、統計也是相似語法:

LongdeleteById(Long id);
LongcountByUserName(String userName)

基本上 SQL 體系中的關鍵詞均可以使用,如 LIKE 、IgnoreCase、OrderBy:

List<User> findByEmailLike(String email);
 
User findByUserNameIgnoreCase(String userName);
 
List<User> findByUserNameOrderByEmailDesc(String email);

能夠根據查詢的條件不斷地添加和拼接,Spring Boot 均可以正確解析和執行,其餘使用示例能夠參考下表。

具體的關鍵字,使用方法和生產成 SQL 以下表所示

Keyword

Sample

JPQL snippet

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstnameIs,findByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age ⇐ ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNull

findByAgeIsNull

… where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1 (parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1 (parameter bound with prepended %)

Containing

findByFirstnameContaining

… where x.firstname like ?1 (parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> age)

… where x.age not in ?1

TRUE

findByActiveTrue()

… where x.active = true

FALSE

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

1.4     總結

經過這節課的學習發現使用 JPA 大大解放了咱們對數據庫的操做,常用的 SQL 大部分都已經被預生成,直接使用便可。另外 JPA 還有一個特色,那就是不再用關心數據庫的表結構了,須要更改的時候只須要修改對應 Model 的屬性便可。在微服務架構中,由於服務拆分得愈來愈小,微服務內部只關心本身的業務,須要複雜查詢的場景會愈來愈少,在微服務架構中更推薦使用 JPA 技術。

點擊這裏下載源碼

相關文章
相關標籤/搜索