JPA中的映射關系
jpa中維護one to one ,one to many, many to one ,many to many 四種映射關系。
在每個關系中,雙方中的一方在其表中擁有連接列。那么一方稱為所有方(owning side) 或者關系的所有者。
不具有連接列的一方稱之為非所有方(non-owning)或者反方
所有權對於映射很重要,因為用於定義映射到數據庫序列的物理注解(例如,@JoinColumn總是在關系的所有方定義)。如果它們不存在,那么值的默認值將從所有方的特性的角度來考慮。
多對一映射總是在關系的所有方之上,所以如果在擁有多對一方的關系中發現一個@JoinColumn,那么這將是所有方的位置。
注解@JoinColumn,需要指定連接列的名稱,可以使用name元素。
舉個例子來說: 學生和課程的關系定為一對多,
外鍵維護在多的一方,此時課程表映射的實體就是owning-side,學生表就是inverse-side
課程表

學生表

關系映射的使用到的注解說明
@JoinColumn
標記一個列指定實體的關聯關系的。可以和@OneToMany或者@ManyToOne搭配使用

@OneToMany
在一的一方定義一對多的關聯關系,並且如果關聯關系時雙向的,mappedBy屬性必須用來標注,在擁有關聯關系的實體一方中表示關系的字段名,也就是使用mappedBy屬性是不維護關聯關系的一方,值是擁有關聯關系一方中標識關系的字段名
使用了mappedBy屬性后,不能在使用@JoinColumn注解,會拋異常

@ManyToOne 沒有mappedBy屬性.
@OneToOne /@ManyToMany
都是在實體的字段上表示對應的關聯關系,在表示雙向關聯關系時候,都必須使用mappedBy屬性
單向多對一映射
成員和班級是多對一映射,並且外鍵列定義在成員表中,那么創建成員實體的時候,需要使用@JoinColumn標注
成員是關系的擁有者。也就是關系為多的一方持有
@JoinColumn的值是數據庫中的外鍵名稱,@JoinColumn和@ManyToOne搭配維護了關聯關系,此時是單向的關聯
Partner實體類
-
@Entity
-
@Table(name=
"partner_info")
-
public
class Partner {
-
private Integer partner_id;
-
private String partner_name;
-
private Grade grade;
-
@Id
-
@Column(name=
"partner_id")
-
@GeneratedValue(strategy=GenerationType.IDENTITY)
-
public Integer getPartner_id() {
-
return partner_id;
-
}
-
public void setPartner_id(Integer partner_id) {
-
this.partner_id = partner_id;
-
}
-
@Column(name=
"partner_name")
-
public String getPartner_name() {
-
return partner_name;
-
}
-
public void setPartner_name(String partner_name) {
-
this.partner_name = partner_name;
-
}
-
@JoinColumn(name=
"team_id")
-
@ManyToOne
-
public Grade getGrade() {
-
return grade;
-
}
-
public void setGrade(Grade grade) {
-
this.grade = grade;
-
}
-
}
Grade實體類
-
@Entity
-
@Table(name=
"grade_info")
-
public
class Grade {
-
private Integer teamId;
-
private String teamName;
-
@Id
-
@Column(name=
"team_id")
-
@GeneratedValue(strategy=GenerationType.IDENTITY)
-
public Integer getTeamId() {
-
return teamId;
-
}
-
public void setTeamId(Integer teamId) {
-
this.teamId = teamId;
-
}
-
@Column(name=
"team_name",columnDefinition=
"varchar(50) not null ")
-
public String getTeamName() {
-
return teamName;
-
}
-
public void setTeamName(String teamName) {
-
this.teamName = teamName;
-
}
-
}
測試下:
-
public static void main(String[] args) {
-
EntityManagerFactory factory = Persistence.createEntityManagerFactory(
"jpa");
-
EntityManager entityManager = factory.createEntityManager();
-
EntityTransaction transaction = entityManager.getTransaction();
-
transaction.begin();
-
//執行insert操作
-
Grade grade =
new Grade();
-
grade.setTeamName(
"初中1班");
-
-
Partner p1 =
new Partner();
-
p1.setPartner_name(
"張三風");
-
-
Partner p2 =
new Partner();
-
p2.setPartner_name(
"李四關");
-
-
p1.setGrade(grade);
-
p2.setGrade(grade);
-
-
entityManager.persist(grade);
-
entityManager.persist(p1);
-
entityManager.persist(p2);
-
transaction.commit();
-
entityManager.close();
-
factory.close();
-
}
在實體創建中,字段的定義和數據庫表的字段類型、長度、是否非空一致,不一致容易出問題。
如果出現問題,可以試着將數據庫的表刪除,交給JPA自己創建,持久化的過程會自動創建表。JPA會自動建表和外鍵關系.
每一個建立的實體都是javaBean風格的。
數據庫中原來不存在表partner_info和grade_info,通過JPA將會在數據庫中將生成對應的表,並且生成外鍵關系
JPA自動建表的策略需要配置: hibernate.ddl-auto 屬性
hibernate.ddl-auto節點的值有幾個create、create-drop、update、validate、none
create:每次加載hibernate會自動創建表,以后啟動會覆蓋之前的表,所以這個值基本不用,嚴重會導致的數據的丟失。
create-drop : 每次加載hibernate時根據model類生成表,但是sessionFactory一關閉,表就自動刪除,下一次啟動會重新創建。
update: 加載hibernate時根據實體類model創建數據庫表,這是表名的依據是@Entity注解的值或者@Table注解的值,sessionFactory關閉表不會刪除,且下一次啟動會根據實體 model更新結構或者有新的實體類會創建新的表。
validate:啟動時驗證表的結構,不會創建表
none:啟動時不做任何操作
表的字段字段類型可以使用@Column注解的columnDefinition 指定。

如果表創建失敗,是映射表沒有定義正確
,

持久化順序,是先持久化一的一方,再去持久化多的一方,這樣不會多執行update語句
單向一對一映射
員工Employee到停車位ParkingSpace的關系是一對一關系,一對一映射在一個數據庫表中存在一個連接列(外鍵列),需要在擁有外鍵列的一方實體中使用@JoinColumn注解中指定列名,並使用@OneToOne注解標識映射關系

-
@Entity
-
@Table(name=
"employee")
-
public
class Employee {
-
@Id
-
@Column(name=
"id")
-
@GeneratedValue(strategy=GenerationType.IDENTITY)
-
private Integer id;
-
@Column(name=
"name")
-
private String name;
-
@Column(name=
"salary",columnDefinition=
"DECIMAL(10,2)")
-
private BigDecimal salary;
-
@JoinColumn(name=
"pspace_id")
-
@OneToOne
-
private ParkingSpace ps;
-
public Integer getId() {
-
return id;
-
}
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
public String getName() {
-
return name;
-
}
-
public void setName(String name) {
-
this.name = name;
-
}
-
public BigDecimal getSalary() {
-
return salary;
-
}
-
public void setSalary(BigDecimal salary) {
-
this.salary = salary;
-
}
-
public ParkingSpace getPs() {
-
return ps;
-
}
-
public void setPs(ParkingSpace ps) {
-
this.ps = ps;
-
}
-
}
-
@Entity
-
@Table(name=
"parking_space")
-
public
class ParkingSpace{
-
@Id
-
@Column(name=
"id")
-
@GeneratedValue(strategy=GenerationType.IDENTITY)
-
private Integer id;
-
@Column(name=
"lot")
-
private Integer lot;
-
@Column(name=
"location")
-
private String location;
-
public Integer getId() {
-
return id;
-
}
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
public String getLocation() {
-
return location;
-
}
-
public void setLocation(String location) {
-
this.location = location;
-
}
-
public Integer getLot() {
-
return lot;
-
}
-
public void setLot(Integer lot) {
-
this.lot = lot;
-
}
-
}
雙向一對一關系
現在員工已經指向停車位,只要停車位實體在指向員就構成雙向的關系

首先,必須清楚包含連接列的一方,決定了是關系的所有者,可是在雙向一對一關系中,兩個映射均是一對一映射,兩方
都可以是所有者,最后,關系的擁有者只能有一個,連接列只能在一方,另一方只能指向關系的擁有者
ParkingSpace實體,添加@OneToOne表示映射關系,並且添加mappBy元素表示關系的所有方是Employee。
ParkingSpace修改如下;
-
@Entity
-
@Table(name=
"parking_space")
-
public
class ParkingSpace{
-
@Id
-
@Column(name=
"id")
-
@GeneratedValue(strategy=GenerationType.IDENTITY)
-
private Integer id;
-
@Column(name=
"lot")
-
private Integer lot;
-
@Column(name=
"location")
-
private String location;
-
@OneToOne(mappedBy=
"ps")
-
private Employee el;
-
public Employee getEl() {
-
return el;
-
}
-
public void setEl(Employee el) {
-
this.el = el;
-
}
-
...........
雙向一對一關聯的規則;
1. @JoinColumn注解只能放置在映射到包含連接列的表的實體上。
2. mappedBy元素應該在沒有定義連接列的實體的@OneToOne注解中指定,就是沒有持有關系的一方使用
3. 雙向一對一映射關系中只能有一方使用mappedBy屬性
集合值關聯
當源實體引用一個或者多個目標實體實例時候,將使用一個多值關聯(many-value association)或者關聯集合。體現在一對多和多對多關聯關系中
1. 一對多映射
員工Employee和部門Department的關系,如果使用一對多映射表示,本質上是雙向的

一個關系是雙向的,意味着存在兩個映射關系。雙向的一對多映射意味着一個回源的多對一映射。
當一個源實體中有任意數量的目標實體存儲在它的集合屬性中,沒有可以擴展的方式可以用於在數據庫表中存儲這些
它所映射到的引用。如何在單行中存儲任意數量的外鍵?這是行不通的,因此,必須讓集合中的實體表能夠指向會
源實體表的外鍵。因而一對多是雙向的。
-
@Entity
-
@Table(name=
"department_info")
-
public
class DepartmentInfo {
-
@Id
-
@Column(name=
"id")
-
@GeneratedValue(strategy=GenerationType.AUTO)
-
private Integer id;
-
@Column(name=
"name")
-
private String name;
-
@OneToMany(mappedBy=
"department")
-
private List<Employee> list =
new ArrayList<Employee>();
-
public Integer getId() {
-
return id;
-
}
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
public String getName() {
-
return name;
-
}
-
public void setName(String name) {
-
this.name = name;
-
}
-
public List<Employee> getList() {
-
return list;
-
}
-
public void setList(List<Employee> list) {
-
this.list = list;
-
}
-
}
-
@Entity
-
@Table(name=
"employee")
-
public
class Employee {
-
@Id
-
@Column(name=
"id")
-
@GeneratedValue(strategy=GenerationType.IDENTITY)
-
private Integer id;
-
@Column(name=
"name")
-
private String name;
-
@Column(name=
"salary",columnDefinition=
"DECIMAL(10,2)")
-
private BigDecimal salary;
-
@JoinColumn(name=
"department_id")
-
@ManyToOne
-
private DepartmentInfo department;
-
public Integer getId() {
-
return id;
-
}
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
public String getName() {
-
return name;
-
}
-
public void setName(String name) {
-
this.name = name;
-
}
-
public BigDecimal getSalary() {
-
return salary;
-
}
-
public void setSalary(BigDecimal salary) {
-
this.salary = salary;
-
}
-
public DepartmentInfo getDepartment() {
-
return department;
-
}
-
public void setDepartment(DepartmentInfo department) {
-
this.department = department;
-
}
-
}
持久化先持久化一的一方,並且從持有關系的一方設值。
-
DepartmentInfo di =
new DepartmentInfo();
-
di.setName(
"java開發部2");
-
Employee e1 =
new Employee();
-
e1.setName(
"張三1");
-
e1.setSalary(
new BigDecimal(
110));
-
Employee e2 =
new Employee();
-
e2.setName(
"李四1");
-
e2.setSalary(
new BigDecimal(
200));
-
e1.setDepartment(di);
-
e2.setDepartment(di);
-
//di.getList().add(e1); 持久化,只能從擁有關系的一方設值
-
//di.getList().add(e2);
-
entityManager.persist(di);
-
entityManager.persist(e1);
-
entityManager.persist(e2);
關於部門實體,有幾點需要注意
1. 使用明確類型的集合類來存儲,如果並不指定存儲的類型,就必須使用targetEntity指示指向的類的信息。

2. 定義雙向一對多關系時候,必須記住多的一方是關系的所有方,必須在那一方定義連接列,一對多的映射是反方,必須使用mappedBy元素
多對多映射
多對多是雙向的關系映射,關系雙方都是多對多關系,並且雙方均沒有連接列,需要借助中間表。因為每一個雙向關系都必須具有所有方和反方,必須兩個實體中挑選一個作為關系的持有者,反方使用mappedBy屬性指向持有關系的一方。
使用@ManyToMany在實體的集合屬性上表示映射關系。

-
@Entity
-
@Table(name=
"employee")
-
public
class Employee {
-
@Id
-
@Column(name=
"id")
-
@GeneratedValue(strategy=GenerationType.IDENTITY)
-
private Integer id;
-
@Column(name=
"name")
-
private String name;
-
@Column(name=
"salary",columnDefinition=
"DECIMAL(10,2)")
-
private BigDecimal salary;
-
@JoinTable(name=
"employee_project_inner",
-
//中間表product_id字段
-
joinColumns={
@JoinColumn(name=
"employee_id",referencedColumnName=
"id")},
-
inverseJoinColumns={
@JoinColumn(name=
"project_id",referencedColumnName=
"id")}
-
)
-
@ManyToMany
-
private List<ProjectInfo> projects =
new ArrayList<ProjectInfo>();
-
public Integer getId() {
-
return id;
-
}
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
public String getName() {
-
return name;
-
}
-
public void setName(String name) {
-
this.name = name;
-
}
-
public BigDecimal getSalary() {
-
return salary;
-
}
-
public void setSalary(BigDecimal salary) {
-
this.salary = salary;
-
}
-
public List<ProjectInfo> getProjects() {
-
return projects;
-
}
-
public void setProjects(List<ProjectInfo> projects) {
-
this.projects = projects;
-
}
-
}
@JoinTable說明;
joinColumns元素描述關系所有方在中間表的連接列,inverseJoinColumns元素指定了反方在中間表的連接列。
反方,使用mappedBy屬性指向關系擁有的一方。
-
@Entity
-
@Table(name=
"project_info")
-
public
class ProjectInfo {
-
@Id
-
@Column(name=
"id")
-
@GeneratedValue(strategy=GenerationType.IDENTITY)
-
private Integer id;
-
@Column(name=
"name")
-
private String name;
-
@ManyToMany(mappedBy=
"projects")
-
private List<Employee> employees =
new ArrayList<Employee>();
-
public Integer getId() {
-
return id;
-
}
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
public String getName() {
-
return name;
-
}
-
public void setName(String name) {
-
this.name = name;
-
}
-
}
單向集合映射
當一個實體到目標實體存在一對多映射,但是@OneToMany注解不包括mappedBy元素時,就認為它存在目標實體的單向關系,此時,兩個實體都不存在連接列,是單向的映射關系
需要借助中間表,存儲映射關系

,同樣的當多對多關系中,一方沒有映射到另一方時候,就是一個單向的關系,也要借助連接表。
唯一的區別是,只有兩個實體類型中的一個會使用該表來加載器相關的實體,或者更新它已存儲新增的實體關聯
創建實體:
目標實體中不會存在集合特性,源實體的@OneToMany注解中不存在mappedBy屬性,
就是phone中沒有集合屬性,employee中@OneToMany注解不用mappedBy屬性
-
@Entity
-
@Table(name=
"employee")
-
public
class Employee {
-
@Id
-
@Column(name=
"id")
-
@GeneratedValue(strategy=GenerationType.IDENTITY)
-
private Integer id;
-
@Column(name=
"name")
-
private String name;
-
@Column(name=
"salary",columnDefinition=
"DECIMAL(10,2)")
-
private BigDecimal salary;
-
@JoinTable(name=
"employee_phone_inner",
-
//中間表product_id字段
-
joinColumns={
@JoinColumn(name=
"employee_id",referencedColumnName=
"id")},
-
inverseJoinColumns={
@JoinColumn(name=
"phone_id",referencedColumnName=
"id")}
-
)
-
@OneToMany
-
private List<Phone> phones =
new ArrayList<Phone>();
//.. 省略get/set方法
-
@Entity
-
@Table(name=
"phone")
-
public
class Phone {
-
@Id
-
@Column(name=
"id")
-
@GeneratedValue(strategy=GenerationType.IDENTITY)
-
private Integer id;
-
@Column(name=
"name")
-
private String num;
-
public Integer getId() {
-
return id;
-
}
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
public String getNum() {
-
return num;
-
}
-
public void setNum(String num) {
-
this.num = num;
-
}
-
}
