實體對象以及註解介紹<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
具有ORM元數據的領域對象稱爲實體(Entity),按JPA的規範,實體具備以下的條件:
必須使用javax.persistence.Entity註解或者在XML映射文件中有對應的元素;
必須具有一個不帶參的構造函數,類不能聲明爲final,方法和需要持久化的屬性也不能聲明爲final;
如果遊離狀的實體對象需要以值的方式進行傳遞,如通Session bean的遠程業務接口傳遞,則必須實現Serializable接口;
需要持久化的屬性,其訪問修飾符不能是public,它們必須通過實體類方法進行訪問。
使用註解元數據
基本註解:
例子:
@Entity(name = "T_TOPIC") ①
public class Topic implements Serializable ...{
@Id ②-1
@GeneratedValue(strategy = GenerationType.TABLE) ②-2
@Column(name = "TOPIC_ID") ②-3
private int topicId;
@Column(name = "TOPIC_TITLE", length = 100) ③
private String topicTitle;
@Column(name = "TOPIC_TIME") @Temporal(TemporalType.DATE) ④
private Date topicTime;
@Column(name = "TOPIC_VIEWS")
private int topicViews;
...
}
解釋:
① Entity標明該類(Topic)爲一個實體類,它對應數據庫中的表表名是T_TOPIC,這裏也可以寫成: @Entity
@Table(name = "T_TOPIC") 其作用都是一樣的
②-1 Id標明該屬性對應數據表中的主鍵
②-2 GeneratedValue通過strategy屬性指明主鍵生成策略,默認情況下,JPA自動選擇一個最適合底層數據庫的主鍵生成策略。在javax.persistence.GenerationType中定義了以下幾種可供選擇的策略:
1) IDENTITY:表自增鍵字段,Oracle不支持這種方式;
2) AUTO: JPA自動選擇合適的策略,是默認選項;
3) SEQUENCE:通過序列產生主鍵,通過@SequenceGenerator註解指定序列名,MySql不支持這種方式;
4) TABLE:通過表產生主鍵,框架藉由表模擬序列產生主鍵,使用該策略可以使應用更易於數據庫移植。
②-3 Column標明這個屬性是數據表中的一列,該列的名字是TOPIC_ID
③ Column的一個屬性length指明的是該屬性的允許的長度。(個人認爲設定該屬性只是對於程序中操作該屬性時增加了一驗證過程,對數據庫中該列原來的設置並沒有影響,但是length屬性指定的值必須不能大於數據庫創建表時給該列限制的最大長度否則會出錯)
④ Temporal(TemporalType.DATE):如果屬性是時間類型,因爲數據表對時間類型有更嚴格的劃分,所以必須指定具體時間類型。在javax.persistence.TemporalType枚舉中定義了3種時間類型:
1) DATE :等於java.sql.Date
2) TIME :等於java.sql.Time
3) TIMESTAMP :等於java.sql.Timestamp
繼承關係註解:
對繼承關係進行註解,必須在父類中聲明繼承實體的映射策略。
例子:
@Entity(name = "T_TOPIC")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) ① @DiscriminatorColumn(name = "TOPIC_TYPE", discriminatorType =
DiscriminatorType.INTEGER, length = 1) ②
@DiscriminatorValue(value="1")③
public class Topic implements Serializable ...{ … }
解釋:
① Inheritance通過strategy屬性指明實體的繼承策略。
在javax.persistence.InheritanceType定義了3種映射策略:
1) SINGLE_TABLE:父子類都保存到同一個表中,通過字段值進行區分。
2) JOINED:父子類相同的部分保存在同一個表中,不同的部分分開存放,通過表連接獲取完整數據;
3) TABLE_PER_CLASS:每一個類對應自己的表,一般不推薦採用這種方式。
② DiscriminatorColumn如果繼承策略採用第一種繼承策略,則需要指明區分父子類的字段,DiscriminatorColumn就是用來指明區分字段的註解。
③DiscriminatorValue 同樣的採用第一種繼承策略通過字段區分父子類,則用這個註解給該實體的區分字段賦值在這裏賦的值爲”1”.
關聯關係註解:
例子:
@Entity @DiscriminatorValue(value="2") ①
public class PollTopic extends Topic ...{②繼承於Topic實體
private boolean multiple; ③
@Column(name = "MAX_CHOICES")
private int maxChoices; @OneToMany(mappedBy="pollTopic",cascade=CascadeType.ALL) ④
private Set options = new HashSet();
//省略get/setter方法
}
解釋:
① 通過@DiscriminatorValue將區分字段TOPIC_TYPE的值爲2。由於PollTopic實體繼承於Topic實體,其它的元數據信息直接從Topic獲得。
④ OneToMany指定了一個一對多的關聯關係,mappedBy屬性指定“Many”方類引用“One”方類的屬性名;cascade屬性指明瞭級聯方式(如果這裏不指定爲CascadeType.ALL的話,那麼有關聯關係的兩個對象在做保存和刪除操作時要分別來進行)建議:儘可能使用cascade=CascadeType.ALL來減少持久化操作的複雜性和代碼量
注意:JPA規範規定任何屬性都默認映射到表中,所以雖然我們沒有給③處的multiple屬性提供註解信息,但JPA將按照 默認的規則對該字段進行映射:字段名和屬性名相同,類型相同。如果我們不希望將某個屬性持久化到數據表中,則可以通過@Transient註解顯式指定:
@Transient
private boolean tempProp1;
@Entity(name="T_POLL_OPTION")
Public class PollOption implements Serializable ...{
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name = "OPTION_ID")
private int optionId;
@Column(name = "OPTION_ITEM")
private String optionItem;
@ManyToOne ①
@JoinColumn(name="TOPIC_ID", nullable=false) ②
private PollTopic pollTopic;
}
解釋:
① ManyToOne描述了多對一的關聯關係,他是對該類引用的”One”類(PollTopic)的屬性(pollTopic)進行註解的。
② JoinColumn指定關聯”One”(PollTopic)實體所對應表的“外鍵”。
Lob字段的註解:
在JPA中Lob類型類型的持久化很簡單,僅需要通過特殊的Lob註解就可以達到目的。
例子:
@Lob ①-1
@Basic(fetch = FetchType.EAGER) ①-2
@Column(name = "POST_TEXT", columnDefinition = "LONGTEXT NOT NULL") ①-3
private String postText;
@Lob
@Basic(fetch = FetchType. LAZY) ②-2
@Column(name = "POST_ATTACH", columnDefinition = "BLOB") ②-3
private byte[] postAttach;
解釋:
①-1 JPA 通過@Lob將屬性標註爲Lob類型 ;
①-2 通過@Basic指定Lob類型數據的獲取策略,FetchType.EAGER表示非延遲 加載,而FetchType. LAZY表示延遲加載 ;
①-3 通過@Column的columnDefinition屬性指定數據表對應的Lob字段類型。
使用XML元數據
除了使用註解提供元數據信息外,JPA也允許我們通過XML提供元數據信息。按照JPA的規範,如果你提供了XML元數據描述信息,它將覆蓋實體類中的註解元數據信息。XML元數據信息以 orm.xml命名,放置在類路徑的META-INF目錄下。
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="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.baobaotao.domain</package>
<entity class="Topic">
②Topic實體配置
<table name="T_TOPIC" />
<attributes>
<id name="topicId">
<column name="TOPIC_ID"/>
<generated-value strategy="TABLE" />
</id>
<basic name="topicTitle">
<column name="TOPIC_TITLE" length="30" />
</basic>
<basic name="topicTime">
<column name="TOPIC_TIME" />
<temporal>DATE</temporal>
</basic>
<basic name="topicViews">
<column name="TOPIC_VIEWS" />
</basic>
</attributes>
</entity>
<entity class="PollTopic">
②PollTopic實體配置
<discriminator-value>2</discriminator-value>
<attributes>
<basic name="maxChoices">
<column name="MAX_CHOICES" />
</basic>
<one-to-many name="options" mapped-by="pollTopic">
<cascade>
<cascade-all/>
</cascade>
</one-to-many>
</attributes>
</entity>
<entity class="PollOption">
②PollOption實體配置
<table name="T_POLL_OPTION" />
<attributes>
<id name="optionId">
<column name="OPTION_ID" />
<generated-value strategy="TABLE" />
</id>
<basic name="optionItem">
<column name="OPTION_ITEM"/>
</basic>
<many-to-one name="pollTopic" >
<join-column name="TOPIC_ID" nullable="false"/>
</many-to-one>
</attributes>
</entity>
<entity class="Post">
②Post實體配置
<table name="T_POST" />
<attributes>
<id name="postId">
<column name="POST_ID" />
<generated-value strategy="TABLE" />
</id>
<basic name="postText" fetch="EAGER">
<column name="POST_TEXT" column-definition="LONGTEXT NOT NULL"/>
<lob/>
</basic>
<basic name="postAttach" fetch="LAZY">
<column name="POST_ATTACH" column-definition="BLOB"/>
<lob/>
</basic>
</attributes>
</entity>
</entity-mappings>
使用這個orm.xml來描述實體信息的話,這裏並沒有標明兩個繼承類之間的關係,其繼承信息將從實體類反射信息獲取。
到這裏我們的實體描述結束了,當然我們只是做了比較簡單的描述,對於那些複雜的信息描述並沒有進行講述。實體描述結束了,有人會問如果我要來操作這些實體該怎麼操作?這就是我們接下來要講述的問題。
EntityManager介紹
實體對象由實體管理器進行管理,JPA使用javax.persistence.EntityManager代表實體管理器。實體管理器和持久化上下文關聯,持久化上下文是一系列實體的管理環境,我們通過EntityManager和持久化上下文進行交互。
有兩種類型的實體管理器:
容器型:容器型的實體管理器由容器負責實體管理器之間的協作,在一個JTA事務中,一個實體管理器的持久化上下文的狀態會自動廣播到所有使用EntityManager的應用程序組件中。Java EE應用服務器提供的就是管理型的實體管理器;
應用程序型:實體管理器的生命週期由應用程序控制,應用程序通過javax.persistence.EntityManagerFactory的createEntityManager創建EntityManager實例。
EntityManager的創建過程圖:
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
我們在程序中的創建EntityManager的代碼:
EntityManagerFactory currentManagerFactory = Persistence.createEntityManagerFactory(persistenceUtilName);
EntityManager em = currentManagerFactory.createEntityManager();
實體的狀態
實體對象擁有以下4個狀態,這些狀態通過調用EntityManager接口方法發生遷移:
1) 新建態:新創建的實體對象,尚未擁有持久化主鍵,沒有和一個持久化上下文關聯起來。
2) 受控態:已經擁有持久化主鍵並和持久化上下文建立了聯繫;
3) 遊離態:擁有持久化主鍵,但尚未和持久化上下文建立聯繫;
4) 刪除態:擁有持久化主鍵,已經和持久化上下文建立聯繫,但已經被安排從數據庫中刪除。
通過EntityManager中的接口方法可以改變實體對象的狀態:
a) void persist(Object entity)
通過調用EntityManager的persist()方法,新實體實例將轉換爲受控狀態。這意謂着當persist ()方法所在的事務提交時,實體的數據將保存到數據庫中。如果實體已經被持久化,那麼調用persist()操作不會發生任何事情。如果對一個已經刪除的 實體調用persist()操作,刪除態的實體又轉變爲受控態。如果對遊離狀的實體執行persist()操作,將拋出 IllegalArgumentException。
在一個實體上調用persist()操作,將廣播到和實體關聯的實體上,執行相應的級聯持久化操作;
b) void remove(Object entity)
通過調用remove()方法刪除一個受控的實體。如果實體聲明爲級聯刪除(cascade=REMOVE 或者cascade=ALL ),被關聯的實體也會被刪除。在一個新建狀態的實體上調用remove()操作,將被忽略。如果在遊離實體上調用remove()操作,將拋出 IllegalArgumentException,相關的事務將回滾。如果在已經刪除的實體上執行remove()操作,也會被忽略;
c) void flush()
將受控態的實體數據同步到數據庫中;
d) T merge(T entity)
將一個遊離態的實體持久化到數據庫中,並轉換爲受控態的實體;
e) T find(Class entityClass, Object primaryKey)
以主鍵查詢實體對象,entityClass是實體的類,primaryKey是主鍵值;
f) Query
JPA使用javax.persistence.Query接口代表一個查詢實例,Query實例由EntityManager通過指定查詢語句構建。該接口擁有衆多執行數據查詢的接口方法:
◆Object getSingleResult():執行SELECT查詢語句,並返回一個結果;
◆List getResultList() :執行SELECT查詢語句,並返回多個結果;
◆Query setParameter(int position, Object value):通過參數位置號綁定查詢語句中的參數,如果查詢語句使用了命令參數,則可以使用Query setParameter(String name, Object value)方法綁定命名參數;
◆Query setMaxResults(int maxResult):設置返回的最大結果數;
◆int executeUpdate():如果查詢語句是新增、刪除或更改的語句,通過該方法執行更新操作;
還有就是關於JPA的查詢語言,JPA的查詢語言是面向對象而非面向數據庫的,它以面向對象的自然語法構造查詢語句,其查詢語言類似於HQL語句,在這裏就不再贅述。