一、配置映射關係的xml方式
咱們知道,Hibernate是一個典型的ORM框架,用以解決對象和關係的不匹配。其思想就是將關係數據庫中表的記錄映射成爲對象,以對象形式展示,這樣一來,就能夠把對數據庫的操做轉化爲對對象的操做。
而ORM通常是採用xml的格式保存對象與關係數據表的映射,咱們也能夠從下面示例中看到hibernate中的一個映射配置表的表現形式:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.zker.model.job.SysJob" table="SYS_JOB" lazy="true">
<id name="jobId" column="JOB_ID">
<generator class="sequence">
<param name="sequence">SEQ_SYS_JOB</param>
</generator>
</id>
<property name="jobName" type="string" column="JOB_NAME" />
<property name="jobDesc" type="string" column="JOB_DESC" />
<property name="lastModity" type="timestamp" column="LAST_MODIFY" />
<!--與用戶關聯-->
<set name="sysUsers" table="SYS_USER">
<key column="JOB_ID" />
<one-to-many class="com.zker.model.user.SysUser" />
</set>
</class>
</hibernate-mapping>
其中對應的實體類和數據庫表結構以下:
public class SysJob {
/**職位的主鍵ID*/
private int jobId;
/**職位的名稱*/
private String jobName;
/**職位的描述*/
private String jobDesc;
/**職位的修改時間*/
private Timestamp lastModity;
/**職位所對應的用戶*/
private Set<SysUser> sysUsers = new HashSet<SysUser>();
public int getJobId() {
return jobId;
}
public void setJobId(int jobId) {
this.jobId = jobId;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getJobDesc() {
return jobDesc;
}
public void setJobDesc(String jobDesc) {
this.jobDesc = jobDesc;
}
public Timestamp getLastModity() {
return lastModity;
}
public void setLastModity(Timestamp lastModity) {
this.lastModity = lastModity;
}
public Set<SysUser> getSysUsers() {
return sysUsers;
}
public void setSysUsers(Set<SysUser> sysUsers) {
this.sysUsers = sysUsers;
}
}
二、註解的優勢
而完成這個ORM映射關係配置的方式,還有一種,就是使用註解。
- 充分利用 Java 的反射機制獲取類結構信息,這些信息能夠有效減小配置的工做
- 註釋和 Java 代碼位於一個文件中,有助於加強程序的內聚性,便於程序員開發
咱們能夠看以下的一個示例來感覺這種形式:
@Entity
@Table(name = "t_student")
@Domain(name = "學生")
@Generated
@DataIdentify(identifies = "number")
public class Student extends BaseDomain<Student> implements Addable, Modifiable<Student>, Deletable {
private String name;
private String number;
private Sex sex;
@Basic
@NotSemanticNull(groups = {Groups.Add.class, Groups.Update.class})
@Property(name = "姓名")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
...
}
三、經常使用註解的使用方法
3.1 類級別的註解
- @Entity 映射實體類
- @Table 映射數句庫表
@Entity(name = "tableName") -
必須,註解將一個類聲明爲一個實體bean,即指出該Java 類爲實體類,將映射到指定的數據庫表
- 屬性:
- name - 可選,對應數據庫中的一個表。若表名與實體類名相同,則能夠省略
@Table(name = "", catalog = "", schema = "") - 可選,一般和@Entity 配合使用,只能標註在實體的 class 定義處,表示實體對應的數據庫表的信息
- 屬性:
- name - 可選,表示表的名稱,默認的表名和實體名稱一致,只有在不一致的狀況下才須要指定表名
- catalog - 可選,表示Catalog 名稱,默認爲 Catalog("")
- schema - 可選 , 表示 Schema 名稱 , 默認爲Schema("")
3.2 屬性級別的註解
- @Id 映射生成主鍵
- @GeneratedValue 定義主鍵生成策略
- @SequenceGenerator 聲明瞭一個數據庫序列
- @Version 定義樂觀鎖
- @Basic 聲明屬性的存取策略
- @Column 映射表的列
- @Transient 定義暫態屬性
屬性級別的註解,都是放在其對應的getter前。
3.2.1 與主鍵相關注解
@Id -
必須,定義了映射到數據庫表的主鍵的屬性,一個實體只能有一個屬性被映射爲主鍵,置於 getXxx() 前
@GeneratedValue(strategy = GenerationType , generator="") - 可選,用於定義主鍵生成策略
- 屬性:
- strategy - 表示主鍵生成策略,取值有:
- GenerationType.AUTO 根據底層數據庫自動選擇(默認),若數據庫支持自動增加類型,則爲自動增加
- GenerationType.INDENTITY 根據數據庫的Identity字段生成,支持DB二、MySQL、MS、SQL Server、SyBase與HyperanoicSQL數據庫的Identity類型主鍵
- GenerationType.SEQUENCE 使用Sequence來決定主鍵的取值,適合Oracle、DB2等支持Sequence的數據庫,通常結合@SequenceGenerator使用(Oracle沒有自動增加類型,只能用Sequence)
- GenerationType.TABLE 使用指定表來決定主鍵取值,結合@TableGenerator使用
- generator - 表示主鍵生成器的名稱,這個屬性一般和ORM框架相關 , 例如:Hibernate 能夠指定 uuid 等主鍵生成方式
@SequenceGenerator — 註解聲明瞭一個數據庫序列
- 屬性:
- name - 表示該表主鍵生成策略名稱,它被引用在@GeneratedValue中設置的「gernerator」值中
- sequenceName - 表示生成策略用到的數據庫序列名稱
- initialValue - 表示主鍵初始值,默認爲0
- allocationSize - 每次主鍵值增長的大小,例如設置成1,則表示每次建立新記錄後自動加1,默認爲50
3.2.2 與非主鍵相關注解
@Version - 能夠在實體bean中使用@Version註解,經過這種方式可添加對樂觀鎖定的支持(見參考連接)
@Basic - 用於聲明屬性的存取策略:
- @Basic(fetch=FetchType.EAGER) 即時獲取(默認的存取策略)
- @Basic(fetch=FetchType.LAZY) 延遲獲取
@Column - 可將屬性映射到列,使用該註解來覆蓋默認值,@Column描述了數據庫表中該字段的詳細定義
- 屬性:
- name - 可選,表示數據庫表中該字段的名稱,默認情形屬性名稱一致
- nullable - 可選,表示該字段是否容許爲 null,默認爲 true
- unique - 可選,表示該字段是不是惟一標識,默認爲 false
- length - 可選,表示該字段的大小,僅對 String 類型的字段有效,默認值255
- insertable - 可選,表示在ORM框架執行插入操做時,該字段是否應出現INSETRT語句中,默認爲 true
- updateable - 可選,表示在ORM 框架執行更新操做時,該字段是否應該出如今UPDATE 語句中,默認爲 true。對於一經建立就不能夠更改的字段,該屬性很是有用,如對於 birthday 字段
- columnDefinition - 可選,表示該字段在數據庫中的實際類型。一般ORM 框架能夠根據屬性類型自動判斷數據庫中字段的類型,可是對於Date 類型仍沒法肯定數據庫中字段類型到底是 DATE,TIME 仍是 TIMESTAMP. 此外 ,String 的默認映射類型爲 VARCHAR, 若是要將 String 類型映射到特定數據庫的 BLOB或 TEXT 字段類型,該屬性很是有用
@Transient - 可選,表示該屬性並不是一個到數據庫表的字段的映射,ORM框架將忽略該屬性,若是一個屬性並不是數據庫表的字段映射,就務必將其標示爲@Transient,不然ORM框架默認其註解爲 @Basic
3.3 映射實體類的關聯關係
單向一對多:一方有集合屬性,包含多個多方,而多方沒有一方的引用。
用戶--->電子郵件
單向多對一:多方有一方的引用,一方沒有多方的引用。
論文類別---> 類別
雙向一對多:兩邊都有多方的引用,方便查詢。
班級---> 學生
雙向多對一:兩邊都有多方的引用,方便查詢。
單向多對多:須要一箇中間表來維護兩個實體表。
論壇--->文章
單向一對一:數據惟一,數據庫數據也是一對一。
艦船---> 水手
主鍵相同的一對一:使用同一個主鍵,省掉外鍵關聯。
客戶---> 地址
3.3.1 關聯映射的一些共有屬性
@JoinColumn - 可選,用於描述一個關聯的字段。
@JoinColumn和@Column相似,介量描述的不是一個簡單字段,而是一個關聯字段,例如描述一個 @ManyToOne 的字段。(即用來
定義外鍵在咱們這個表中的屬性名,例如實體Order有一個User user屬性來關聯實體User,則Order的user屬性爲一個外鍵
)
- 屬性:
- name - 該字段的名稱,因爲@JoinColumn描述的是一個關聯字段,如ManyToOne, 則默認的名稱由其關聯的實體決定
@OneToOne、@OneToMany、@ManyToOne、ManyToMany 的共有屬性:
- fetch - 配置加載方式。取值有:
- Fetch.EAGER - 及時加載,多對一默認是Fetch.EAGER
- Fetch.LAZY - 延遲加載,一對多默認是Fetch.LAZY
- cascade - 設置級聯方式,取值有:
- CascadeType.PERSIST - 保存 - 調用JPA規範中的persist(),不適用於Hibernate的save()方法
- CascadeType.REMOVE - 刪除 - 調用JPA規範中的remove()時,適用於Hibernate的delete()方法
- CascadeType.MERGE - 修改 - 調用JPA規範中merge()時,不適用於Hibernate的update()方法
- CascadeType.REFRESH - 刷新 - 調用JPA規範中的refresh()時,適用於Hibernate的flush()方法
- CascadeType.ALL - 所有 - JPA規範中的全部持久化方法
- targetEntity - 配置集合屬性類型,如:@OneToMany(targetEntity=Book.class)
@OneToOne – 表示一個一對一的映射
主表類A與從表類B的主鍵值相對應
主表:
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
public B getB(){
return b;
}
從表:無
主表A中有一個從表屬性是B類型的b
主表:
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name="主表外鍵") //這裏指定的是數據庫中的外鍵字段。
public B getB(){
return b;
}
從表:無
主表A中有一個從表屬性是B類型的b,同時,從表B中有一個主表屬性是A類型的a
主表:
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name="主表外鍵") //這裏指定的是數據庫中的外鍵字段。
public B getB(){
return b;
}
從表:
@OneToOne(mappedBy = "主表類中的從表屬性")
public 主表類 get主表類(){
return 主表對象
}
@ManyToOne - 表示一個多對一的映射,該註解標註的屬性一般是數據庫表的外鍵
單向多對一:多方有一方的引用,一方沒有多方的引用
在多方
@ManyToOne(targetEntity=XXXX.class) //指定關聯對象
@JoinColumn(name="") //指定產生的外鍵字段名
// 示例
// 訂單 Order 和用戶 User 是一個 ManyToOne 的關係
// 在 Order 類中定義
@ManyToOne()
@JoinColumn(name="USER")
public User getUser() {
return user;
}
@OneToMany - 描述一個一對多的關聯,該屬性應該爲集合類型,在數據庫中並無實際字段
單向一對多:一方有集合屬性,包含多個多方,而多方沒有一方的引用
@OneToMany 默認會使用鏈接表作一對多關聯
添加@JoinColumn(name="xxx_id") 後,就會使用外鍵關聯,而不使用鏈接表了
雙向一對多:
1)在多方
@ManyToOne
@JoinColumn(name="本身的數據庫外鍵列名")
2)在一方
@OneToMany(mappedBy="多端的關聯屬性名") //mappedBy至關於inverse,維護外鍵的控制權,不能和JoinColumn同時使用
@JoinColumn(name="對方的數據庫外鍵列名")
注意:對於外鍵的維護,若是是雙向一對多,但願雙方都可以維護外鍵,則不能使用mappedBy,而應該雙方都使用@JoinColumn
@ManyToMany - 可選,描述一個多對多的關聯
- 屬性:
- targetEntity - 表示多對多關聯的另外一個實體類的全名,例如:package.Book.class
- mappedBy - 用在雙向關聯中,把關係的維護權翻轉。
單向多對多關聯:
在主控方加入@ManyToMany註解便可。
雙向多對多關聯:
兩個實體間互相關聯的屬性必須標記爲@ManyToMany,並相互指定targetEntity屬性。
有且只有一個實體的@ManyToMany註解須要指定mappedBy屬性,指向targetEntity的集合屬性名稱。
3.3.2 關聯映射的其餘補充
@JoinTable其實同時也是配合
@ManyToMany使用的,
@ManyToMany註釋表示該對象是多對多關係的一端,而後利用@JoinTable來定義關聯關係(利用中間表來創建聯繫,
緣由戳這裏),其中name屬性指定中間表名稱,j
oinColumns定義中間表與該表的外鍵關係,inverseJoinColumns屬性定義了中間表與另一端的外鍵關係。
@JoinTable - 定義關聯表, 該關聯表包含了指回實體表的外鍵(經過@JoinTable.joinColumns) 以及指向目標實體表的外鍵(經過@JoinTable.inverseJoinColumns)
e.g.
以下表示:該屬性對應字段爲"resource_type",該字段實際在另一張名"r_role_x_resource_type"表中,與該表("p_role")經過"role_id"進行外鍵連接
(class Role --> table "p_role")
@ElementCollection
@JoinTable(name = "r_role_x_resource_type", joinColumns = @JoinColumn(name = "role_id"))
@Column(name = "resource_type")
@JSONField(serialize = false)
@Cache(usage = CacheConcurrencyStrategy.NONE)
public List<Class<? extends Resourceable>> getResourceTypeList() {
return resourceTypeList;
}
e.g.
以下表示:關聯的表爲「r_role_x_permission",其中關聯外鍵爲"role_id",該表又經過"permission_id"連接第三張表,即目標實體表
(class Role --> table "p_role")
@ManyToMany
@JoinTable(name = "r_role_x_permission", joinColumns = @JoinColumn(name = "role_id"),
inverseJoinColumns = @JoinColumn(name = "permission_id"))
public List<Permission> getPermissionList() {
return permissionList;
}
四、其餘註解
@DiscriminatorValue - 一張表對應一整棵類繼承樹時,該類別對應的「表part」
首先參考這篇文章,很重要:
hibernate映射繼承關係(一):一張表對應一整棵類繼承樹,從文中能夠知道,用一個表來存儲對應的整個類別的數據,好比有Cat和Animal,Cat是Animal的子類,我僅用Animal一個表來存儲Animal和Cat的字段和數據,而不是分紅兩個表。那麼當我進行映射關係的時候,假如我要Cat類映射到Animal中Cat的部分,如何處理?在Animal中定義一個字段用來區分不一樣的表,好比Animal表中我額外增長字段名爲Type,那麼在Animal這一張表中,咱們本屬於Animal表內容的,該字段咱們設置爲animal,本屬於Cat表的,該字段咱們設置爲cat。你能夠理解爲,新增長字段來用以在同一個表中區分不一樣類別的內容。
因此對應在註解上的使用的一個映射關係表示,就是這樣的:對於」父類「,即準備用來囊括全部內容的那個表,咱們須要定義這個對應的類爲 @DiscriminatorColumn(name = "xxx", discriminatorType = DiscriminatorType.xxx) ,這裏的
name就是指定表中用來區別各種內容的字段
,而對於」子類「,咱們須要註解標明@DiscriminatorValue(xxx),這裏的xxx即對應了父類中的 「區別用字段」 裏的標識。
舉例來講,就是假如咱們但願將Animal和Cat的內容都只存儲在Animal這張表裏,那麼爲了區份內容,咱們對於Animal這個表新增某字段如 type;Animal的類,註解爲@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING) ,同時設置@DiscriminatorValue("animal");Cat extends Animal,Cat的類,註解爲@DiscriminatorValue(「cat");那麼Animal這個表中,字段type中,爲animal的元組映射Animal類,爲cat的元組映射Cat類。
而這種方式,多用於數據庫字典概念。
@Transient
若是某個屬性不須要被持久化,能夠加上 @javax.persistence.Transient 註解或者使用 java 的 transient 關鍵字。
@Lob
實體BLOB、CLOB類型的註解:
- BLOB類型屬性聲明爲byte[]或者java.sql.Blob,多用來直接將文件存儲在數據庫字段中(如圖片);
- CLOB類型的屬性聲明爲String或java.sql.Clob (詳可見參考連接中《Hibernate的Annotation實體BLOB、CLOB類型註解》)
五、參考連接