近年來ORM(Object-Relational Mapping)對象關系映射,即實體對象和數據庫表的映射)技術市場人聲音鼎沸,異常熱鬧, Sun在充分吸收現有的優秀ORM框架設計思想的基礎上,制定了新的JPA(Java Persistence API)規范。JPA Java Persistence API,是Java EE 5的標准ORM接口,也是ejb3規范的一部分。
那么什么是JPA呢?JPA是通過JDK5.0注解或XML描述對象-關系表的映射關系,並將運行期實體對象持久化到數據庫中去。
Hibernate與JPA的關系及其實現機制
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所有的核心功能。
hibernate-entitymanager實現了標准的JPA,可以把它看成hibernate-core和JPA之間的適配器,它並不直接提供ORM的功能,而是對hibernate-core進行封裝,使得Hibernate符合JPA的規范。
下面重點介紹一下hibernate-entitymanager包的主要類及實現。
HibernatePersistence.java,實現了JPA的PersistenceProvider接口,它提供createEntityManagerFactory和createContainerEntityManagerFactory兩個方法來創建EntityManagerFactory對象,這兩個方法底層都是調用的EJB3Configuration對象的buildEntityManagerFactory方法,來解析JPA配置文件persistence.xml,,並創建EntityManagerFactory對象。
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種狀態:
1、 新建態:新創建的實體對象,尚未擁有持久化主鍵,沒有和一個持久化上下文關聯起來
2、 受控態:已經擁有持久化主鍵和持久化上下文建立了聯系
3、 游離態:擁有持久化主鍵,但尚未和持久化上下文建立聯系
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);