本文主要講解 springData Jpa
入門相關知識, 瞭解JPA規範與Jpa的實現,搭建springboot+dpringdata jpa環境實現基礎增刪改操做,適合新手學習,老鳥繞道~java
ORM(Object-Relational Mapping)顧名思義就是表示對象關係映射。在面向對象的軟件開發中,咱們確定是須要和數據庫進行交互的,那麼這裏就存在一個問題如何將數據庫中的表與咱們代碼中的對象映射起來尼,咱們只要有一套程序可以作到創建對象與數據庫的關聯,操做對象就能夠直接操做數據庫數據,就能夠說這套程序實現了ORM對象關係映射mysql
簡單的說:ORM就是創建實體類和數據庫表之間的關係,從而達到操做實體類就至關於操做數據庫表的目的。web
目前市面上主流的ORM框架有Hibernate、Mybatis、Spring Data Jpa等,spring
Mybatis
框架是一個半自動的ORM框架,自己並非徹底面向對象的思想,可是得益於sql與代碼的解耦,能夠更靈活的操做sql與優化sql,可是同時也帶有複雜的映射文件,在國內目前仍是很是主流的。JPA
的全稱是 Java Persistence API
, 即 Java
持久化 API
,是 SUN
公司推出的一套基於ORM的規範,內部是由一系列的接口和抽象類構成。sql
JPA
經過 JDK 5.0
註解描述對象-關係表的映射關係,並將運行期的實體對象持久化到數據庫中。數據庫
JPA
是 JCP
組織發佈的 Java EE
標準之一,所以任何聲稱符合 JPA
標準的框架都遵循一樣的架構,提供相同的訪問 API
,這保證了基於 JPA
開發的企業應用可以通過少許的修改就可以在不一樣的 JPA
框架下運行。編程
JPA
框架中支持大數據集、事務、併發等容器級事務,這使得 JPA 超越了簡單持久化框架的侷限,在企業應用發揮更大的做用。設計模式
簡單方便springboot
JPA的主要目標之一就是提供更加簡單的編程模型:在JPA框架下建立實體和建立Java 類同樣簡單,沒有任何的約束和限制,只須要使用 javax.persistence.Entity
進行註釋, JPA
的框架和接口也都很是簡單,沒有太多特別的規則和設計模式的要求,開發者能夠很容易的掌握。JPA基於非侵入式原則設計,所以能夠很容易的和其它框架或者容器集成微信
查詢能力
JPA的查詢語言是面向對象而非面向數據庫的,它以面向對象的天然語法構造查詢語句,能夠當作是 Hibernate HQL
的等價物。 JPA
定義了獨特的 JPQL(Java Persistence Query Language)
, JPQL
是 EJB QL
的一種擴展,它是針對實體的一種查詢語言,操做對象是實體,而不是關係數據庫的表,並且可以支持批量更新和修改、 JOIN、GROUP BY、HAVING
等一般只有 SQL
纔可以提供的高級查詢特性,甚至還可以支持子查詢。
高級特性
JPA
中可以支持面向對象的高級特性,如類之間的繼承、多態和類之間的複雜關係,這樣的支持可以讓開發者最大限度的使用面向對象的模型設計企業應用,而不須要自行處理這些特性在關係數據庫的持久化。
Spring Data JPA
是 Spring
基於 ORM
框架、 JPA
規範的基礎上封裝的一套 JPA
應用框架,可以使開發者用極簡的代碼便可實現對數據庫的訪問和操做。它提供了包括增刪改查等在內的經常使用功能,且易於擴展!學習並使用 Spring Data JPA
能夠極大提升開發效率!
Spring Data JPA
讓咱們解脫了 DAO
層的操做,基本上全部 CRUD
均可以依賴於它來實現, 在實際的工做工程中,推薦使用 Spring Data JPA + ORM
(如: hibernate
)完成操做,這樣在切換不一樣的ORM框架時提供了極大的方便,同時也使數據庫層操做更加簡單,方便解耦JPA
是一套規範,內部是有接口和抽象類組成的。
hibernate
是一套成熟的ORM框架,並且 Hibernate
實現了 JPA
規範,因此也能夠稱 hibernate
爲 JPA
的一種實現方式,咱們使用 JPA
的 API
編程,意味着站在更高的角度上看待問題(面向接口編程)
Spring Data JPA
是 Spring
提供的一套對JPA操做更加高級的封裝,是在 JPA
規範下的專門用來進行數據持久化的解決方案。
springboot
腳手架構建初始環境,咱們不要 web
模塊,只須要 spring data jpa
、 mysql
便可spingboot
的強大在這裏體現了,爲咱們快速構建了開箱即用的環境,咱們在 application.yml
文件中添加咱們須要的配置信息spring: datasource: url: jdbc:mysql:///db?serverTimezone=GMT username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 數據庫沒有表時自動構建, database: mysql # 指定數據庫類型 generate-ddl: true # 自動生成 show-sql: true # 現實sql到控制檯 database-platform: org.hibernate.dialect.MySQL8Dialect # 數據庫方言 DataBbase枚舉內獲取
DeptEntity
, 這裏用來 lombok
插件省去了 get/set
方法,對於 @Column
註解是能夠放在 get
方法上的@Entity @Table(name = "dept", schema = "db", catalog = "") @ToString @Data public class DeptEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "DEPTNO", nullable = false) private int deptno; // name : 數據庫中的列名 nullable : 是否爲空 length:長度 @Column(name = "DNAME", nullable = true, length = 14) private String dname; @Column(name = "LOC", nullable = true, length = 13) private String loc; @Column(name = "flag", nullable = true) private Integer flag; @Column(name = "type", nullable = true, length = 20) private String type; }
Dao
層接口,須要繼承 JPA
的接口 JpaRepository
, 代碼以下:public interface DeptRepository extends JpaRepository<DeptEntity,Integer> { }
咱們的 dao 層是一個接口,沒有具體的實現,在繼承的接口中已經定義了一些經常使用的方法供咱們使用,後面詳細介紹
@SpringBootTest public class SpringJpaTest { @Autowired private DeptRepository deptRepository; @Test public void jpaTest(){ List<DeptEntity> list = deptRepository.findAll(); System.out.println(list); } }
Hibernate: select deptentity0_.deptno as deptno1_0_, deptentity0_.dname as dname2_0_, deptentity0_.flag as flag3_0_, deptentity0_.loc as loc4_0_, deptentity0_.type as type5_0_ from dept deptentity0_ [DeptEntity(deptno=10, dname=張三, loc=NEW YORK, flag=10, type=真實類型), DeptEntity(deptno=20, dname=RESEARCH, loc=DALLAS, flag=20, type=11), DeptEntity(deptno=50, dname=zonghebu, loc=CHICAGO, flag=null, type=null), DeptEntity(deptno=60, dname=??, loc=123, flag=null, type=22), DeptEntity(deptno=61, dname=??, loc=123, flag=null, type=null), DeptEntity(deptno=62, dname=??, loc=123, flag=null, type=null), DeptEntity(deptno=72, dname=??, loc=123, flag=null, type=null)]
同時能夠看到sql的語句,這是
Hibernate
幫咱們生成的,總的來講Spring Data Jpa
的實現是依賴了Hibernate
對Jpa
規範的實現,
@Test public void query(){ Sort deptno = Sort.by(Sort.Direction.DESC,"deptno"); List<DeptEntity> all = deptRepository.findAll(deptno); System.out.println(all); } @Test public void insert(){ DeptEntity entity = new DeptEntity(); entity.setDname("質量控制部門"); entity.setFlag(1); entity.setType("test"); //保存同時將保存結果返回 DeptEntity save = deptRepository.save(entity); System.out.println(save); }
增長方法打印:
Hibernate: insert into dept (dname, flag, loc, type) values (?, ?, ?, ?)
DeptEntity(deptno=73, dname=質量控制部門, loc=null, flag=1, type=test)
其餘方法再也不贅述都基本類似,這些方法都是繼承自 JpaRepository
接口中
在父接口中定義了一套經常使用的查詢, 相對比較簡單,以下:
Optional<T> findById(ID id); Iterable<T> findAllById(Iterable<ID> ids); List<T> findAll(Sort sort); List<T> findAllById(Iterable<ID> ids);
jpql : jpa query language (jpq查詢語言)
特色:
語法或關鍵字和sql語句相似
查詢的是類和類中的屬性
須要將JPQL語句配置到接口方法上
1.特有的查詢:須要在dao接口上配置方法
2.在新添加的方法上,使用註解的形式配置jpql查詢語句
3.註解 : @Query
代碼實現以下:
在dao接口中自定義方法並添加註解
/** * 經過 deptno查詢 * 使用@Query註解來編寫jpql 語句 是面向對象的操做 * sql: select * from dept where deptno = ? * jpql: select * from DeptEntity where deptno = ?1 * * 在jpql中佔位符好指定索引,與參數列表對應,從1開始 * @param id * @return */ @Query(value=" from DeptEntity where deptno = ?1") DeptEntity queryDept(Integer id); /** * DML 操做, 須要加@Modifying 註解 * 在多個條件時要注意佔位符下標的數字要和參數列表對應 * 須要注意,DML 語句須要事務配置,須要加 @Transactional 註解,通常在業務層,而再也不數據層, * @param name * @param id */ @Query(value="update DeptEntity set dname=?1 where deptno=?2") @Modifying void updateName(String name,Integer id); // JAVA
測試方法代碼:
@Test public void queryDept(){ DeptEntity entity = deptRepository.queryDept(10); System.out.println(entity); } /** * 這裏須要加 @Transactional 註解來管理事務 */ @Test @Transactional public void updateName(){ deptRepository.updateName("測試",10); }
特有的查詢:須要在dao接口上配置方法
在新添加的方法上,使用註解的形式配置sql查詢語句
註解 :
@Query
value :jpql語句 | sql語句 nativeQuery :false(使用jpql查詢) | true(使用本地查詢:sql查詢) 是否使用本地查詢
示例代碼:
dao接口代碼:
/** * 須要添加 nativeQuery 參數來設置是都是sql查詢, 默認是false ,是jpql查詢 * @param num * @return */ @Query(value="select * from dept where flag = ?",nativeQuery=true) List<DeptEntity> queryList(Integer num);
測試代碼:
@Test public void nativeQuery(){ List<DeptEntity> list = deptRepository.queryList(10); System.out.println(list); }
是對jpql查詢,更加深刻一層的封裝
咱們只須要按照SpringDataJpa提供的方法名稱規則定義方法,不須要再配置jpql語句,完成查詢
規則:
findBy開頭: 表明查詢
對象中屬性名稱(首字母大寫)
含義:根據屬性名稱進行查詢
接口代碼:
/** * 方法名的約定: * findBy : 查詢 * 對象中的屬性名(首字母大寫) : 查詢的條件 * Dname * * 默認狀況 : 使用 等於的方式查詢 * 特殊的查詢方式 * * findAllByDname -- 根據名稱查詢 * * 再springdataJpa的運行階段 * 會根據方法名稱進行解析 findBy from xxx(實體類) * 屬性名稱 where dname = * * 1.findBy + 屬性名稱 (根據屬性名稱進行完成匹配的查詢=) * 2.findBy + 屬性名稱 + 「查詢方式(Like | isnull)」 * findAllByDnameLike * 3.多條件查詢 * findBy + 屬性名 + 「查詢方式」 + 「多條件的鏈接符(and|or)」 + 屬性名 + 「查詢方式」 */ List<DeptEntity> findAllByDname(String name); List<DeptEntity> findAllByDnameAndAndFlag(String name,Integer num); List<DeptEntity> findAllByDnameLike(String name);
測試代碼省略
這三個方法乍一看都是同樣的功能,都是經過主鍵返回一個實體,可是再實際使用中仍是有區別的,尤爲在一對一的關係中咱們若是直接用 getOne
查詢,會出現 下面錯誤:
org.hibernate. LazyInitializationException: could not initialize proxy - no Session
下面咱們簡單說一下這三個方法:
getOne是懶加載,須要在 springboot
增長這個配置: spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
,yml中也是同樣配置。但這種方式不太友好,建議不要使用。
每次初始化一個實體的關聯就會建立一個臨時的 session
來加載,每一個臨時的 session
都會獲取一個臨時的數據庫鏈接,開啓一個新的事物。這就致使對底層鏈接池壓力很大,並且事物日誌也會被每次 flush
.
設想一下:假如咱們查詢了一個分頁
list
每次查出1000條,這個實體有三個lazy
關聯對象, 那麼,恭喜你,你至少須要建立3000
個臨時session+connection+transaction
.
getOne
來自 JpaReposiroty
接口,對於傳入的標識則返回一個實體的引用;且取決於該方法的實現,可能會出現 EntityNotFoundException
,並會拒絕一些無效的標識;
findById
來自 CrudRepository
接口,經過它的 id
返回一個實體;
findById返回一個Optional對象;
findOne
來自 QueryByExampleExecutor
接口, 返回一個經過 Example
匹配的實體或者 null
;
findOne返回一個Optional對象,能夠實現動態查詢
本文由AnonyStar 發佈,可轉載但需聲明原文出處。
仰慕「優雅編碼的藝術」 堅信熟能生巧,努力改變人生
歡迎關注微信公帳號 :coder簡碼 獲取更多優質文章
更多文章關注筆者博客 :IT簡碼