Hibernate+JPA(轉)


近年來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實例

 

EntityManagerAPI

 

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);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM