Hibernate 學習經驗之談-(四)關聯對映
摘要:
(待編寫)
Hibernate 是一個ORM 框架,它的重點就是物件之間的關係模型,維護物件之間的關係是hibernate 的核心內容,也是這個框架中相對比較難的一點。如果能夠靈活地運用好這個框架,必須熟悉物件之間的聯絡。Hibernate 中的主要有三種物件的關係:One to One(一對一關聯對映);One to Many (一對多關聯對映);Many to Many (關聯對映)。 每一種對映又分單向和雙向。在資料庫中關係表之間的關係只有一種,就是外來鍵關聯,hibernatre的物件對映就是建立在資料庫外來鍵關聯之上。
要掌握hibernate的關聯對映,必須要分析hibernate 在使用物件關聯時生成的DDL語句,理解每一種對映在資料庫中的對應處理方式。
單向關聯與雙向關聯的區別
物件通過組合的方式來表達的類之間的關係,並在hbm檔案或者註解上做出明確的說明。
單向可以理解為在兩個有關聯的物件實體中,只在一方中說明關聯關係,物件只能在一方導向到另一方。
雙向可以理解為在兩個有關聯的物件實體中,雙方都有說明關聯關係的配置,物件之間可以互相訪問到對方。
導向的意思是說在物件的關聯欄位訪問到關聯的物件的資訊,雙向和單向在資料庫層級的實現基本一樣。
對於設定了雙向關聯的實體,最好的做法就是在其中一方使用mappedBy 指定主導關係,不然會出現冗餘的資料欄位。原則是在1-1 可以任意設一方,1-N 在N方設定mappedBy,N-N 的在任意一方設定。mappedBy 是關聯註解的一個屬性,mappedBy="xxx",xxx 為主導關係的getXXX對應的值。例如mappedBy=“person” person 對應的不是下文中的person 屬性,而是對應getter 中getXXX,的“XXX”位置的值,person 對應getPerson();
一.One to One(一對一關聯對映)
1.解析:
例如,一個人只能有一張身份證,一張身份證只能對應一個人;這兩個物件之間就是屬於一對一;一對一是三種關係中最簡單的一種。
2.種類:主鍵關聯,唯一外來鍵關聯;
2.1 主鍵關聯:兩個物件採用一樣的主鍵值來表明他們之間是屬於一對一的關係,這種方法是基本不用的。
2.2 外來鍵關聯:兩個物件採用外來鍵來表示兩個之間的聯絡。
3.配置方式(只講外來鍵關聯)
3.1單向配置
Person
package com.hwj.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.Table; import org.hibernate.annotations.DynamicUpdate; @Table(name="t_person") @Entity @DynamicUpdate public class Person { private int id; private String nickName; //關聯欄位 private IDCard idCard; @Id @GeneratedValue public int getId() { return id; } @OneToOne public IDCard getIdCard() { return idCard; } public String getNickName() { return nickName; } public void setId(int id) { this.id = id; } public void setIdCard(IDCard idCard) { this.idCard = idCard; } public void setNickName(String nickName) { this.nickName = nickName; } }
IDCard
<pre name="code" class="java">package com.hwj.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.DynamicUpdate;
@Entity
@Table(name="t_idcard")
@DynamicUpdate
public class IDCard {
private int id;
private String name;
private String idNo;
@Id
@GeneratedValue
public int getId() {
return id;
}
public String getIdNo() {
return idNo;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setIdNo(String idNo) {
this.idNo = idNo;
}
public void setName(String name) {
this.name = name;
}
}
hibernate 生成的sql語句
Hibernate:
create table t_idcard (
id integer not null auto_increment,
idNo varchar(255),
name varchar(255),
primary key (id)
)
Hibernate:
create table t_person (
id integer not null auto_increment,
nickName varchar(255),
idCard_id integer,
primary key (id)
)
Hibernate:
alter table t_person
add constraint FK_iwf0ex84epvoh2ndq1axpceg9
foreign key (idCard_id)
references t_idcard (id)
3.2 雙向配置
Person
package com.hwj.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.DynamicUpdate;
@Table(name="t_person")
@Entity
@DynamicUpdate
public class Person {
private int id;
private String nickName;
//關聯欄位
private IDCard idCard;
@Id
@GeneratedValue
public int getId() {
return id;
}
@OneToOne(mappedBy="pserson")
public IDCard getIdCard() {
return idCard;
}
public String getNickName() {
return nickName;
}
public void setId(int id) {
this.id = id;
}
public void setIdCard(IDCard idCard) {
this.idCard = idCard;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
}
IDCard
package com.hwj.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.DynamicUpdate;
@Entity
@Table(name="t_idcard")
@DynamicUpdate
public class IDCard {
private int id;
private String name;
private String idNo;
private Person pserson;
@Id
@GeneratedValue
public int getId() {
return id;
}
public String getIdNo() {
return idNo;
}
public String getName() {
return name;
}
@OneToOne
public Person getPserson() {
return pserson;
}
public void setId(int id) {
this.id = id;
}
public void setIdNo(String idNo) {
this.idNo = idNo;
}
public void setName(String name) {
this.name = name;
}
public void setPserson(Person pserson) {
this.pserson = pserson;
}
}
hibernate 生成的sql語句
Hibernate:
create table t_idcard (
id integer not null auto_increment,
idNo varchar(255),
name varchar(255),
pserson_id integer,
primary key (id)
)
Hibernate:
create table t_person (
id integer not null auto_increment,
nickName varchar(255),
primary key (id)
)
Hibernate:
alter table t_idcard
add constraint FK_pdvviqxfv7qwech66rqm08830
foreign key (pserson_id)
references t_person (id)
總結,使用主要使用外來鍵關聯,在雙方設定了雙向關聯的時候,進行mappedBy 設定。
二.One to Many 與 Many to One
其實一對多,還有多對一,都只是在不同的角度來看,但是對映產生的結果可能會不一樣。這裡使用Group 組與 User 成員來做舉例。
1.單向對映
1.1 One to Many (一對多 )
Group 對映
package com.hwj.model;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.UpdateTimestamp;
@Entity
@Table(name="t_group")
@DynamicUpdate
public class Group {
private int id;
private String name;
private List<User> users;
@Id
@GeneratedValue
public int getId() {
return id;
}
public String getName() {
return name;
}
@OneToMany
@JoinColumn(name="group_id")
public List<User> getUsers() {
return users;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
User 的對映
package com.hwj.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.DynamicUpdate;
@Entity
@Table(name="t_user")
@DynamicUpdate
public class User {
private int id;
private String name;
@Id
@GeneratedValue
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
產生的sql語句
Hibernate:
create table t_group (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
Hibernate:
create table t_user (
id integer not null auto_increment,
name varchar(255),
group_id integer,
primary key (id)
)
Hibernate:
alter table t_user
add constraint FK_e5f24mh6aryt9hsy99oydps6g
foreign key (group_id)
references t_group (id)
注意,假如在一對多關聯對映中只使用了 One to Many 的話,hibernate 就會發生把One to Many 當成 many to many 方式生成sql 語句,生成的語句中會存在中間表。但是在關聯欄位新增@joinColumn(name="外來鍵名稱") ,j就可以避免這種情況生成正確的sql語句。1.2.many to one
注:many to one 配置方式十分方便,只要在多的一方在關聯欄位加上 @ManyToOne 就可以正確在多一方生成外來鍵約束。
2.雙向關聯
注;雙向關聯中需要在one 的一方設定mappedBy ,在many to one 的一方沒有這個屬性。設定完後就是實現了對資料的雙向導航。
三.Many to Many (關聯對映)
1.單向對映
只要在關聯欄位上新增@ManyToMany 就可以實現多對多單向對映。
2.雙向對映
雙向對映的時候一定要設定在任意一方設定mappedBy 這樣才能不會執行重複的sql語句。
3.表生成
多對多表對映是採用中間表的方式來實現對映的,中間表字段的修改可以使用@JoinTable來實現。這個註解可以修改中間表的表名,表中的外來鍵欄位。
@JoinTable(
joinColumns={@JoinColumn(name="本物件的外來鍵引用欄位名")}, //為陣列的原因是考慮到聯合主鍵的情況
inverseJoinColumns={@JoinColumn(name="另外的一方的物件外來鍵引用的欄位名")} //為陣列的原因是考慮到聯合主鍵的情況
)