1. 程式人生 > >JPA中對映關係詳細說明(一對多,多對一,一對一、多對多)、@JoinColumn、mappedBy說明

JPA中對映關係詳細說明(一對多,多對一,一對一、多對多)、@JoinColumn、mappedBy說明

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