近年來ORM(Object-Relational Mapping)對象關係映射,即實體對象和數據庫表的映射)技術市場人聲音鼎沸,異常熱鬧, Sun在充分吸取現有的優秀ORM框架設計思想的基礎上,制定了新的JPA(Java Persistence API)規範。JPA Java Persistence API,是Java EE 5的標準ORM接口,也是ejb3規範的一部分。java
那麼什麼是JPA呢?JPA是經過JDK5.0註解或XML描述對象-關係表的映射關係,並將運行期實體對象持久化到數據庫中去。mysql
Hibernate與JPA的關係及其實現機制sql
JPA和Hibernate之間的關係,能夠簡單的理解爲JPA是標準接口,Hibernate是實現。那麼Hibernate是如何實現與JPA 的這種關係的呢。Hibernate主要是經過三個組件來實現的,及hibernate-annotation、hibernate- entitymanager和hibernate-core。數據庫
hibernate-annotation是Hibernate支持annotation方式配置的基礎,它包括了標準的JPA annotation以及Hibernate自身特殊功能的annotation。服務器
hibernate-core是Hibernate的核心實現,提供了Hibernate全部的核心功能。session
hibernate-entitymanager實現了標準的JPA,能夠把它當作hibernate-core和JPA之間的適配器,它並不直接提供ORM的功能,而是對hibernate-core進行封裝,使得Hibernate符合JPA的規範。app
下面重點介紹一下hibernate-entitymanager包的主要類及實現。框架
HibernatePersistence.java,實現了JPA的PersistenceProvider接口,它提供 createEntityManagerFactory和createContainerEntityManagerFactory兩個方法來建立 EntityManagerFactory對象,這兩個方法底層都是調用的EJB3Configuration對象的 buildEntityManagerFactory方法,來解析JPA配置文件persistence.xml,,並建立 EntityManagerFactory對象。ide
EntityManagerFactory對象的實現是EntityManagerFactoryImpl類,這個類有一個最重要的******* 屬性就是Hibernate的核心對象之一SessionFactory。這個類最重要的方法是createEntityManager,來返回 EntityMnagaer對象,而sessionFactory屬性也傳入了該方法。函數
EntityManager對象的實現是EntityManagerImpl類,這個類繼承自AbstractEntityManagerImpl 類,在AbstractEntityManager類中有一個抽象方法getSession來得到Hibernate的Session對象,正是在這個 Session對象的實際支持下,EntityManagerImpl類實現了JPA的EntityManager接口的全部方法,並完成實際的ORM操 做。
此外,hibernate-entitymanager包中還有QueryImpl類利用EntityManagerImpl的支持實現了JPA的 Query接口;TransactionImpl利用EntityManagerImpl的支持實現了JPA的EntityTransaction接口。
至此,Hibernate經過hibernate-entitymanager包完成了對於JPA的所有支持工做。
這裏咱們要先談一下什麼叫實體(Entity),按照JPA規範,具備ORM元數據的領域對象就叫作實體。它應具有一下條件:
1.必須使用javax.persistence.Entity註解或XML映射文件中有對應的<entity>元素;
2.必須具備一個不帶參數的構造函數,類不能聲明爲final,方法和須要持久化的屬性也不能聲明爲final;
3.若是遊離態的實體對象須要以值的方式進行傳遞(如經過Session bean的遠程業務接口傳遞),則必須實現Serializable接口;
4.須要持久化的屬性,起訪問修飾符不能是public,它必須經過實體類方法進行訪問。
實體的狀態
實體共有4種狀態:
一、 新建態:新建立的實體對象,還沒有擁有持久化主鍵,沒有和一個持久化上下文關聯起來
二、 受控態:已經擁有持久化主鍵和持久化上下文創建了聯繫
三、 遊離態:擁有持久化主鍵,但還沒有和持久化上下文創建聯繫
四、 刪除態:擁有持久化主鍵,已經和持久化上下文創建了聯繫,但已經被安排從數據庫中刪除
下面咱們來嘗試對一個域對象進行JPA註解,使其成爲一個實體類:
@Entity(name=」T_TEST」)
public class Test implements Serializable{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
@Column(name=」 id」)
******* int testId;
@Column(name=」uname」,length=100)
******* String uname;
@Column(name=」password」)
******* String password;
@Column(name=」time」)
@Temporal(TemporalType.Date)
******* Date loginTime;
//省略get/setter方法
}
@下面對以上代碼中所涉及的JPA註解進行一下說明
@Entity:將領域對象標註爲一個實體類,表示該類須要持久化到數據庫中,默認狀況下類名即表名,經過name屬性顯式指定表名,如:name=」T_TEST」表示將Test保存到表T_TEST表中。
@Id:對應的屬性是表的主鍵
@GeneratedValue:主鍵的產生策略,經過strategy屬性進行指定,默認狀況下,JPA自動選擇一個最適合底層數據庫的主鍵生成策略,如SqlServer對應的identity:
mysql對應的auto increment,在java.persistence.GenerationType中定義了幾種能夠供選擇的策略:
1. :表自動增加字段,Oracle不支持這種方式;Identity
2. :JPA自動選擇合適的策略,是默認選項;AUTO
3. :經過序列產生主鍵,經過@SequenceGenerator註解指定序列名,Mysql不支持這種方式。Sequence
4. :經過表產生主鍵,框架藉由表模擬產生主鍵,使用該策略可使用更易於數據庫的移植。TABLE
@Colunm(name=」uname」):屬性對應的表字段。咱們並不須要指定表字段的類型,由於JPA 會根據反射從實體屬性中獲取類型;若是是字符串類型,咱們能夠指定字段長度,以即可以自動生成DDL語句。
@Temporal(TemporalType.DATE):若是屬性是時間類型,由於數據表對時間類型有更嚴格的劃分,因此必須指定具體時間類型,在java.persistence.TemporalType枚舉中定義了三種時間類型:
Date:等於java.sql.Date;
Time:等於java.sql.Time;TimeStamp:等於java.sql.Timestamp。
JPA對於具備父子關係的類,對於父類必須聲明繼承實體的映射策略,對於繼承實體,java.persistence.InheritanceType定義了3種映射策略:
SINGLE_TABLE:父子類都保存在同一個表中,經過字段值進行區分。
JOINED:父子類相同的部分保存在同一個表中,不一樣的部門分開存放,經過鏈接不一樣的表獲取完整數據。
TABLE_PER_CLASS:每個類對應本身的表,通常不推薦採用這種方式。
下面咱們來看看實際的列子是怎麼運用的。
父類Test
@Entity(name=」test」)
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)//指定繼承策略
@DiscriminatorColumn(name=」types」,discriminatorType=DiscriminatorType.INTEGER,length=1)//指定區分字段爲types,類型爲Integer長度爲1
@DiscriminatorValue(value=」1」)//對應具體實體的值
public class Test implements Serializable{
…..
}
子類Child
@Entity
@DiscriminatorValue(value=」2」)
public class Child extends Test{
//若是咱們不但願JPA將該屬性持久化到數據庫,則採用該註解
@Transient
******* String tempStr;
@Lob //lob類型的字段
@Basic(fetch=FetchType.Lazy) //採用延遲加載,FetchType.EAGER不採用
@Column(name=」postattach」,columnDefinition=」LONGTEXT NOT NULL」)對應字段類型
******* String postAttach;
}
能夠看到經過字段types來區分父子類數據,也是至關方便的。至於JPA提供的關聯關係好比說一對多,多對一,多對多,也有相應的註解進行關聯,有興趣的朋友能夠參考相關幫助文檔。
以上講述的都是JPA中以註解形式進行持久化,下面咱們來看下采用XML元數據的形式,XML元數據信息以orm.xml命名,放置在類路徑的META-INF 目錄下。若是你提供了 XML 元數據描述信息,它將覆蓋實體類中的註解元數據信息
<?xml version=」1.0」 encoding=」UTF-8」?>
<entity-mappings xmlns=」http://java.sun.com/xml/ns/persistence/orm」
xmlns=」http://www.w3.org/2001/XMLSchema-instance」
xsi:schemaLocation=」
http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd」 version=」1.0」>
<package>com.test</package>
<entity class=」Test」>
<table name=」test」/>
<attributes>
<id name=」id」>
<column name=」id」/>
<generated-value strategy=」TABLE」/>
</id>
<basic name=」uname」>
<column name=」uname」 length=」30」/>
</basic>
<basic name=」logintime」>
<column name=」 logintime」/>
<temporal>DATE
</temporal>
</basic>
</attributes>
</entity>
<entity-mappings>
能夠看到JPA元數據採用XML形式也是至關簡單易懂的。
JPA重要API
JPA接口位於javax.persistence和javax.persistence.spi兩個包中,javax.persistence包 中大部分API都是註解類、EntityManager、Query等持久化操做接口。而javax.persistence.spi包中的4個API, 是JPA的服務層接口
EntityManager
實體對象由實體管理器進行管理,經過EntityManager和持久化上下文進行交互
實體管理器有兩種:
容器類:容器型的實體管理器由容器負責試題管理器之間的協做,Java EE應用服務器提供的就是管理型的實體管理器。
應用程序型:實體管理器的生命週期由應用程序控制,應用程序經過javax.persistence.EntityManagerFactoty的creaeEntityManager建立EntityManager實例
EntityManager的API
void persist(Object entity)
經過persist方法,新實體實例將轉換爲受控狀態,就是說,當persist()方法所在的事務提交時,實體的數據保存到數據庫中。
若是實體已經被持久化,那麼調用persist()方法不會發生任何事情。
若是對一個已經刪除的實體調用persist()方法,刪除態的實體又轉變爲受控態
若是對遊離狀態的實體執行persist()操做,拋出IllegalArgumentException
一個實體調用persist()方法後,全部與之關聯的實體,都將執行持久化操做
void remove(Object entity)
刪除一個受控態的實體。
若是實體聲明爲級聯刪除(cascade=REMOVE或者cascade=ALL),被關聯的實體也會被刪除
在一個新建態或刪除態的實體上調用remove()方法,將被忽略
在遊離態的實體上調用remove()方法,將拋出IllegalArgumentException,相關事務將回滾
void flush()
將受控態的實體數據同步到數據庫中
T merge(T entity)
將一個遊離態的實體持久化到數據庫中,並轉換爲受控態的實體
T find(Class entityClass.Object primaryKey)
以主鍵查詢實體對象,entityClass是實體的類,primaryKey是主鍵值
Eg:Topic t = em.find(Topic.class,1);