JPA是什麼?
其實JPA也不是第一天聽說,Coding中也用了一段時間了,突然想起來JPA到底是什麼東西,想幹什麼? 似乎很高深的樣子^_^, 自己反而說不敢肯定地說這東西是什麼,感覺查了一把,原來就是三個字:ORM, 還好,沒有理解錯(慚愧,這段時間經常有點信心缺失的感覺)。 下面是一些官樣文字:
JPA是Java Persistence API的簡稱,中文名Java持久層API,是JDK 5.0註解或XML描述對象-關係表的映射關係,並將運行期的實體對象持久化到數據庫中。
應該是SUN提出來的。
JPA由EJB 3.0軟件專家組開發,作爲JSR-220實現的一部分。
這是一個概念框架,而不是實現。
Hibernate3.2+、TopLink 10.1.3以及OpenJPA都提供了JPA的實現
JPA包括以下3方面的技術:
- ORM映射元數據 JPA支持XML和JDK5.0註解兩種元數據的形式,元數據描述對象和表之間的映射關係
- API 用來操作實體對象,執行CRUD操作,框架在後臺替代我們完成所有的事情,開發者從繁瑣的JDBC和SQL代碼中解脫出來
- 查詢語言 通過面向對象而非面向數據庫的查詢語言查詢數據,避免程序的SQL語句緊密耦合
Hibernate和JPA的關係
一般用JPA時,使用的是 javax.persistence.xxx, 但是有些類,在Hibernate中也有定義,該用誰呢? 網上的說法是:當需要擴展的時候,Hibernate會提供自己的對象,但一般都是繼承了JPA的。
EntityManager是幹什麼的?
先看這篇文章JavaEE – JPA(4):EntityManager相關核心概念
public interface EntityManager {
public void persist(Object entity);
public <T> T merge(T entity);
public void remove(Object entity);
public <T> T find(Class<T> entityClass, Object primaryKey);
// ......
}
我的理解:EntityManager只是定義了持久化Entity的接口,通過這些接口可以保存和讀取對象,但是對象的管理是有另外一個叫PersistenceContext的對象來管理的,類似於內存池的管理,多個EM對象可以使用同一個PC對象。
遺留問題: 在SpringBoot中如何操作EntityManager?
SpringBoot中使用JPA+Mysql
- 項目文件配置
我的項目是用Gradle配置的
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile 'mysql:mysql-connector-java'
資源文件配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_db
username: root
password: xxxxx
jpa:
show-sql: true使用這個配置,沒有指定ddl屬性,對於mysql來說,不會做任何事情,不會自動創建表也不會刪除表,這其實是項目中常用的模式,先建表,再寫代碼^_^。
這裏還有個小插曲,在src/main目錄下建了一個resources目錄後,把application.yml放進去,運行起來報錯,似乎是沒有認到這個資源文件,一開始百思不得其解,後來無意中發現,要在build.gradle上點右鍵–refresh gradle project,然後eclipse就會resource目錄和src/main/java/顯示在一起,再運行就正常了。
Entity的定義和使用
一般的Entity類定義如下:
@Entity(name = "t_student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long id;
public String name;
我不太喜歡寫get,set,直接就把變臉定義爲public。
註解 | 作用 |
---|---|
@Entity | 指定表的名字, mysql上的結果來看,表名用大寫,小寫,混寫都沒有關係 |
@Id | 指定ID字段,JPA要求每一個實體Entity,必須有且只有一個主鍵,這個註解沒屬性需要設置 |
@GeneratedValue | 和@Id配合使用,指定主鍵的生成策略,詳見2, 一般來說就是自增長/序列號這種,JPA默認的是Auto,會自動根據使用的數據庫來確定策略,那麼mysql就是自增長,oracle就是序列了。 |
@Column | 指定字段名,有name, unique, nullable,insertable,table等屬性. 實踐中,大多可以不用這個註解來顯式指定字段名,JPA會根據默認規則去進行映射: 1. 表字段名爲單個單詞,比如name, 那麼entity屬性直接用字段名就好了; 2. 表字段名帶下劃線,比如target_name, 那麼entity屬性就是targetName 3. 在Repo中定義的方法中引用屬性名的時候,也是按此規則做反向解析。 |
Repository的概述
Repository真的是什麼都不幹,僅僅定了一個接口
public interface Repository<T, ID extends Serializable> {
}
接下來有幾個子類擴展了Repository,實現不同的操作。
類名 | 內容 |
---|---|
CrudRepository | 繼承知Repository,定義了基本的CRUD方法,我們也可以擴展,比如findByxxxx |
PagingAndSortingRepository | 繼承自CrudRepository,支持排序和分頁 |
JpaRepository | 繼承自PagingAndSortingRepository,QueryByExampleExecutor 擴展的功能待研究 |
##自定義Repository
有幾種方式來擴展Repository
- 直接用字段名來指定查詢條件,比如findBy, deleteBy, 可以使用的語法可以參考官方文檔4
@NamedQuery, @Query註解,自己寫sql語句
我喜歡用@Query, 這種方式不好的地方就是數據字段名改了的話得跟着改sql語句。
select的例子
@Query("select new com.test.Student (h.no, h.name) from t_student h") List<Student> getAllStudent();
有時候我們只是需要返回表中的部分字段,此時需要在entity中定義對應參數的構造方法。
update的例子
@Transactional @Modifying(clearAutomatically = true) @Query(value = "update t_student p set p.name=?2 where p.id=?1", nativeQuery = true) int updateName(long id, String name);
不想寫原生SQL怎麼辦?
對於簡單的場景,直接寫sql,簡單明瞭,但是從工程角度來說,維護性又差了一點, 比如表的字段名改了就要跟着改sql語句。
JPA提供了幾種方式來處理這個問題,這篇文章做了介紹,就個人來說,比較喜歡Querydsl。
QueryDsl
這篇文章,我覺得講得比較詳細spring boot-jpa整合QueryDSL來簡化複雜操作
環境的建立 如果用的是Gradle,可以參考我的另外一篇文章來配置環境。
今天在簡書上看到一位仁兄恆宇少年寫的QueryDsl系列文章,非常地詳細,本來我也想整理類似文章,看來用不着了,大家請移步:http://www.jianshu.com/p/99a5ec5c3bd5
Spring Boot JPA - 使用 Querydsl 處理複雜的操作 這篇文章也不錯
返回自定義字段
很多時候,我們的查詢是從幾個關聯表裏面各自取幾個字段作爲結果返回,但是這些字段怎麼映射爲DTO對象返回給用戶呢?
- 如果是JPA,那得自己寫SQL語句, 在其中new DTO,把結果字段作爲new的參數;
- 如果用QueryDsl,有Project方法來幫助完成這個轉換。
事務處理
使用起來比較簡單,App上加@EnableTransactionManagement,然後在需要的方法上加@Transactional就行了。
詳細內容可以參考catoop寫的這篇文章-Spring Boot 事務的使用
L1 Cache
普遍的說法是L1 Cache和EntityManage掛在一起的,那就有個問題了,我們的工程中,EM對象只有一個,如果每次查詢都把數據cache起來,那且不是數據量很大,而且現在大家都搞分佈式開發,同一個工程部署在多個機器上,在其中一臺機器上對一個對象做update,另外一臺機器上如果前面有該對象的cache,且不是不會知道這個變化,從而一直都是返回舊的對象? 這個疑問,在頭腦裏面想了很久,一直不太明白, 今天寫了點代碼來測試了一下,結果發現不會有這種情況,緩存是跟單次請求的執行過程或者說線程掛在一起的,一個外部請求過來,系統必然分配一個線程去執行,這一次執行過程中的結果會cache,結束以後感覺就釋放掉了。下一次即使是同樣的請求再來,又起一個線程來執行,是不會用到上次的cache結果的,會首先從數據庫中讀取數據。而且如果你執行過程中,啓動另外一個新線程,那這個線程的cache是獨立的, 這樣有可能會導致問題,反而需要注意:
1. 主線程讀取entity,結果會cache;
2. 啓動子線程,對該entity做update;
3. 再主線程中再次讀取,返回的是cache結果。
當然實際代碼中,這種寫法應該非常稀罕。
另外這個L1 Cache只有findOne這個方法執行的時候纔會自動把結果cache起來,我們自己寫的sql語句的結果是不會保存的。(findById,findAll感覺都沒有cache)。
本Markdown編輯器使用StackEdit修改而來,用它寫博客,將會帶來全新的體驗哦:
- Markdown和擴展Markdown簡潔的語法
- 代碼塊高亮
- 圖片鏈接和圖片上傳
- LaTex數學公式
- UML序列圖和流程圖
- 離線寫博客
- 導入導出Markdown文件
- 豐富的快捷鍵
快捷鍵
- 加粗
Ctrl + B
- 斜體
Ctrl + I
- 引用
Ctrl + Q
- 插入鏈接
Ctrl + L
- 插入代碼
Ctrl + K
- 插入圖片
Ctrl + G
- 提升標題
Ctrl + H
- 有序列表
Ctrl + O
- 無序列表
Ctrl + U
- 橫線
Ctrl + R
- 撤銷
Ctrl + Z
- 重做
Ctrl + Y
Markdown及擴展
Markdown 是一種輕量級標記語言,它允許人們使用易讀易寫的純文本格式編寫文檔,然後轉換成格式豐富的HTML頁面。 —— [ 維基百科 ]
使用簡單的符號標識不同的標題,將某些文字標記爲粗體或者斜體,創建一個鏈接等,詳細語法參考幫助?。
本編輯器支持 Markdown Extra , 擴展了很多好用的功能。具體請參考Github.
表格
Markdown Extra 表格語法:
項目 | 價格 |
---|---|
Computer | $1600 |
Phone | $12 |
Pipe | $1 |
可以使用冒號來定義對齊方式:
項目 | 價格 | 數量 |
---|---|---|
Computer | 1600 元 | 5 |
Phone | 12 元 | 12 |
Pipe | 1 元 | 234 |
定義列表
- Markdown Extra 定義列表語法:
- 項目1
- 項目2
- 定義 A
- 定義 B
- 項目3
- 定義 C
-
定義 D
定義D內容
代碼塊
代碼塊語法遵循標準markdown代碼,例如:
@requires_authorization
def somefunc(param1='', param2=0):
'''A docstring'''
if param1 > param2: # interesting
print 'Greater'
return (param2 - param1 + 1) or None
class SomeClass:
pass
>>> message = '''interpreter
... prompt'''
腳註
生成一個腳註1.
目錄
用 [TOC]
來生成目錄:
數學公式
使用MathJax渲染LaTex 數學公式,詳見math.stackexchange.com.
- 行內公式,數學公式爲:
Γ(n)=(n−1)!∀n∈N 。 - 塊級公式:
更多LaTex語法請參考 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/“>這兒.
UML 圖:
可以渲染序列圖:
或者流程圖:
離線寫博客
即使用戶在沒有網絡的情況下,也可以通過本編輯器離線寫博客(直接在曾經使用過的瀏覽器中輸入write.blog.csdn.net/mdeditor即可。Markdown編輯器使用瀏覽器離線存儲將內容保存在本地。
用戶寫博客的過程中,內容實時保存在瀏覽器緩存中,在用戶關閉瀏覽器或者其它異常情況下,內容不會丟失。用戶再次打開瀏覽器時,會顯示上次用戶正在編輯的沒有發表的內容。
博客發表後,本地緩存將被刪除。
用戶可以選擇 把正在寫的博客保存到服務器草稿箱,即使換瀏覽器或者清除緩存,內容也不會丟失。
注意:雖然瀏覽器存儲大部分時候都比較可靠,但爲了您的數據安全,在聯網後,請務必及時發表或者保存到服務器草稿箱。
瀏覽器兼容
- 目前,本編輯器對Chrome瀏覽器支持最爲完整。建議大家使用較新版本的Chrome。
- IE9以下不支持
- IE9,10,11存在以下問題
- 不支持離線功能
- IE9不支持文件導入導出
- IE10不支持拖拽文件導入
- 這裏是 腳註 的 內容. ↩