Spring Data JPA 是 Spring Boot 體系中約定優於配置的最佳實現,大大簡化了項目中數據庫的操做。從本課開始將會從 JPA 的由來開始講解,什麼是 JPA、Spring Boot JPA 的實現,以及如何使用。java
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
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
Spring Data JPA 是 Spring 基於 ORM 框架、JPA 規範的基礎上封裝的一套 JPA 應用框架,可讓開發者用極簡的代碼便可實現對數據的訪問和操做。它提供了包括增、刪、改、查等在內的經常使用功能,且易於擴展,學習並使用 Spring Data JPA 能夠極大提升開發效率。Spring Data JPA 其實就是 Spring 基於 Hibernate 之上構建的 JPA 使用解決方案,方便在 Spring Boot 項目中使用 JPA 技術。數據庫
Spring Data JPA 讓咱們解脫了 DAO 層的操做,基本上全部 CRUD 均可以依賴於它實現。編程
<dependency>
<groupId></groupId> org.Springframework.boot
<artifactId></artifactId> spring-boot-starter-data-jpa
</dependency>
<dependency>
<groupId></groupId> mysql
<artifactId></artifactId> mysql-connector-java
</dependency>
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 參數的做用主要用於:自動建立、更新、驗證數據庫表結構,有四個值。服務器
其中:session
@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=GenerationType
,
generator="")
可選,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) 註解。建立的 Repository 只要繼承 JpaRepository 便可,就會幫咱們自動生成不少內置方法。另外還有一個功能很是實用,能夠根據方法名自動生產 SQL,好比 findByUserName 會自動生產一個以 userName 爲參數的查詢方法,好比 findAll 會自動查詢表裏面的全部數據等。
publicinterface UserRepository extends JpaRepository<User, Long> {
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,就會擁有了不少經常使用的數據庫操做方法。
建立好 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 的增、刪、改爲功。
咱們能夠將 Spring Data JPA 查詢分爲兩種,一種是 Spring Data JPA 默認實現的,另外一種是須要根據查詢的狀況來自行構建。
預生成方法就是咱們上面看到的那些方法,由於繼承了 JpaRepository 而擁有了父類的這些內容。
(1)繼承 JpaRepository
publicinterface UserRepository extends JpaRepository<User, Long> {
}
(2)使用默認方法
Test@
public void testBaseQuery() {
userRepository.findAll();
1l userRepository.findById();
userRepository.save(user);
delete userRepository.(user);
userRepository.count();
1l userRepository.existsById();
// ...
}
全部父類擁有的方法均可以直接調用,根據方法名也能夠看出它的含義。
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) |
經過這節課的學習發現使用 JPA 大大解放了咱們對數據庫的操做,常用的 SQL 大部分都已經被預生成,直接使用便可。另外 JPA 還有一個特色,那就是不再用關心數據庫的表結構了,須要更改的時候只須要修改對應 Model 的屬性便可。在微服務架構中,由於服務拆分得愈來愈小,微服務內部只關心本身的業務,須要複雜查詢的場景會愈來愈少,在微服務架構中更推薦使用 JPA 技術。