天天看點

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料

一個簡單的eclipselink  + jpa  + mysql 的用法示例

基本環境準備

    我們先來看看一個具體的JPA工程示例。要運作這個示例,我們需要如下的類庫和軟體安裝配置好:

    類庫: EclipseLink, mysql-connector-j

    資料庫: Mysql

    開發環境:eclipse

    因為JPA是一個公開的規範,是以有不同的實作。我們需要一個JPA的類庫。這裡我們引用了如下類庫:EclipseLink  (可以讓eclipse 幫我們下載下傳去)。

    在下載下傳EclipseLink包後,解壓。我們需要将裡面jlib目錄下的eclipselink.jar以及jlib/jpa目錄裡的java.persistence.x.jar引入到工程中就可以。

後面我們定義代碼裡的映射關系對應的底層實作就是由這兩個包實作。 既然要實作ORM,肯定少不了資料庫。這裡我們用了比較傳統的mysql資料庫。是以少不了也需要引用mysql的jdbc驅動:mysql-connector-javax.jar。

    準備好了這些之後,我們建立一個JPAProject的java工程。整體的項目結構如下圖:

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料

    這裡為了友善工程引用類庫,首先将這些庫檔案拷貝到工程下面的一個lib目錄裡,再将這些庫引入到工程中。

persistence.xml

    在前面配置好基本的類庫之後我們還有一個需要關心的就是,既然我們是ORM,就有一個要映射到資料庫的地方。這裡是怎麼映射到資料庫的呢?具體又映射到哪個資料庫呢?這些都是在persistence.xml檔案定義的。我們在建立好工程之後的src目錄裡建立一個META-INF的目錄,然後在這個目錄裡建立一個persistence.xml檔案。程式連接配接資料庫和對象映射的配置資訊就是通過讀取這個目錄下的檔案來實作的。我們來看一個persistence.xml的示例:

Xml代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence   
  4.     http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"  
  5.     version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">  
  6.     <persistence-unit name="EmployeeService" transaction-type="RESOURCE_LOCAL">  
  7.         <class>model.PersonInformation</class>  
  8.         <properties>  
  9.             <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />  
  10.             <property name="javax.persistence.jdbc.url"  
  11.                 value="jdbc:mysql://localhost:3306/simpleDb" />  
  12.             <property name="javax.persistence.jdbc.user" value="root" />  
  13.             <property name="javax.persistence.jdbc.password" value="root" />  
  14.             <!-- EclipseLink should create the database schema automatically -->  
  15.             <property name="eclipselink.ddl-generation" value="create-tables" />  
  16.             <property name="eclipselink.ddl-generation.output-mode"  
  17.                 value="database" />  
  18.         </properties>  
  19.     </persistence-unit>  
  20. </persistence>  

    這裡都是xml的配置項,看起來也很容易懂。在一個<persistence-unit>的單元裡定義了這個unit的名字以及詳細的資料庫連接配接驅動,資料庫使用者名,密碼。前面<class>裡面定義的是需要映射到資料庫的具體實體類。比如model.Employee就對應于我們在package model裡定義的Employee類。因為我們要連的資料庫是mysql,這裡的javax.persistence.jdbc.driver值被設為com.mysql.jdbc.Driver。而我們具體要連接配接的資料庫名字在javax.persistence.jdbc.url對應的值裡面定義了,為simpleDb。為了後面實際程式運作的時候能夠讀寫這個庫,我們需要事先在資料庫裡建立simpleDb。

    OK,有了這些基本的設定。我們就能連上資料庫進行實際的操作了。

實體對象定義

    現在,我們需要定義一個可以具體執行個體化到資料庫裡的對象。我們定義一個PersonInformation的類如下:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package model;  
  2. import javax.persistence.Entity;  
  3. import javax.persistence.Id;  
  4. @Entity  
  5. public class PersonInformation {  
  6.     @Id  
  7.     private int id;  
  8.     private String name;  
  9.     private int age;  
  10.     public int getId() {  
  11.         return id;  
  12.     }  
  13.     public void setId(int id) {  
  14.         this.id = id;  
  15.     }  
  16.     public String getName() {  
  17.         return name;  
  18.     }  
  19.     public void setName(String name) {  
  20.         this.name = name;  
  21.     }  
  22.     public int getAge() {  
  23.         return age;  
  24.     }  
  25.     public void setAge(int age) {  
  26.         this.age = age;  
  27.     }  
  28. }  

    這裡隻是很簡單的一個entity bean對象定義。我們針對每個可以操作的對象定義了get, set方法。這裡比較有意思的地方是我們用了兩個annotation,一個是@Entity,一個是@Id。這裡的@Entity表示一個可以序列化映射的的對象。如果我們希望這個對象被映射到資料庫中的某個表,則必須要加上這個annotation。而@Id則表示對應表的主鍵。我們建一個表要求有對應的主鍵。這裡指定id為主鍵。如果我們不指定主鍵的話則運作的時候會出錯。有興趣的可以嘗試一下。

    有了這些定義之後,我們就可以來使用他們了。這裡是通過他們通路資料庫的代碼:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package main;  
  2. import javax.persistence.EntityManager;  
  3. import javax.persistence.EntityManagerFactory;  
  4. import javax.persistence.Persistence;  
  5. import model.PersonInformation;  
  6. public class Main {  
  7.     private static final String PERSISTENCE_UNIT_NAME = "EmployeeService";  
  8.     private static EntityManagerFactory factory;  
  9.     public static void main(String[] args) {  
  10.         factory = Persistence.createEntityManagerFactory(  
  11.                 PERSISTENCE_UNIT_NAME);  
  12.         EntityManager em = factory.createEntityManager();  
  13.         em.getTransaction().begin();  
  14.         PersonInformation person = new PersonInformation();  
  15.         person.setId(1);  
  16.         person.setAge(30);  
  17.         person.setName("fred");  
  18.         em.persist(person);  
  19.         em.getTransaction().commit();  
  20.         em.close();  
  21.     }  
  22. }  

    第一個需要建立的對象是EntityManagerFactory, 這裡通過Persistence.createEntityManagerFactory方法。而這個"EmployeeService"是哪裡來的呢?我們看前面的persistence.xml檔案,那裡有<persistence-unit name="EmployeeService" transaction-type="RESOURCE_LOCAL">這個定義。EmployeeService就是對應到這裡一個persistence-unit的名字。他們必須一緻。實際上,如果我們需要通路多個庫的話,在配置檔案裡也可以定義多個persistence-unit。有了這個factory之後我們再建立一個EntityManager對象。

    為了使得對象的建立成為一個事務來送出,我們通過em.getTransaction().begin(); em.getTransaction().commit();這兩個方法來完成整個資料插入的過程。

    現在運作程式,我們再去檢視資料庫的話,則會發現如下的資訊:

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料

    回顧一下我們前面定義的PersonInformation對象的資訊,我們定義的字段都是小寫格式的。映射到資料庫裡包括表名和表裡面字段名都是大寫格式的。前面我們指定了id字段為主鍵之後,對應的表裡這個字段就有了primary key not null的限制。

修改

    從前面的運作結果,我們可以看到一些實體對象和資料庫表之間的映射關系。可是在實際情況中,有時候我們希望資料庫表名或者裡面的字段名能夠更加可以定制化一點。不是簡單的将對象裡的字段變成大寫的。這樣可以增加一點可讀性,那我們該怎麼修改呢?

    很顯然,我們需要指定對象應該對應哪個表名以及每個屬性應該對應什麼名字。這是修改後的代碼:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package model;  
  2. import javax.persistence.Column;  
  3. import javax.persistence.Entity;  
  4. import javax.persistence.GeneratedValue;  
  5. import javax.persistence.GenerationType;  
  6. import javax.persistence.Id;  
  7. import javax.persistence.Table;  
  8. @Entity  
  9. @Table(name="PersonInformation")  
  10. public class PersonInformation {  
  11.     @Id  
  12.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  13.     @Column(name="id")  
  14.     private int id;  
  15.     @Column(name="name")  
  16.     private String name;  
  17.     @Column(name="age")  
  18.     private int age;  
  19.     public int getId() {  
  20.         return id;  
  21.     }  
  22.     public void setId(int id) {  
  23.         this.id = id;  
  24.     }  
  25.     public String getName() {  
  26.         return name;  
  27.     }  
  28.     public void setName(String name) {  
  29.         this.name = name;  
  30.     }  
  31.     public int getAge() {  
  32.         return age;  
  33.     }  
  34.     public void setAge(int age) {  
  35.         this.age = age;  
  36.     }  
  37. }  

    和前面的代碼比起來,我們這裡增加了幾個描述屬性。一個是@Table,這裡通過它來設定對應的資料庫表名字是什麼。@Column這個用來設定對應的資料庫字段名。還有一個比較有意思的地方就是我們在id字段增加了@GeneratedValue(strategy = GenerationType.IDENTITY)這個描述屬性。它表示什麼意思呢?它表示這個主鍵的值可以自動來生成,而後面的GenerationType.IDENTITY表明它的生成方式是自動增長,類似于auto increment.

    針對前面修改之後我們再來看資料庫的運作結果:

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料

    這樣,經過前面這些步驟的描述,我們已經能夠完成一個簡單的JPA示例了。也是以知道了将對象如何映射到資料庫表中。

各種關系映射

    前面的示例是簡單的将一個實體對象映射到資料庫中。在很多實際的情況下,我們并不是簡單的一個對象映射,往往對象和對象之間還存在着一定的關聯關系。我們在做ORM的時候,也需要針對不同的情況進行考慮。這個就牽涉到資料庫中間表或者實體對象之間的關系。總的來說,實體對象之間的關系無非為以下幾種:一對一,一對多,多對多。而對于多對一的情況來說,從另外一個角度來看也是一種一對多的關系。

一對一

    我們都知道,對于不管是哪種對應的關系,實際上他們在對應到資料庫的實作裡,我們是可以有幾種不同的映射實作方式的。我們以一對一的關系開始。假設我們有一個Employee的實體對象。它同時有一個屬性是Address。這個Address和它是一對一的映射關系。那麼在資料庫的實作裡,我們該怎麼考慮呢?

    我們實際上有幾種實作方式。一種很簡單,既然他們反正是一對一的關系,我們完全可以把他們當成一個表來使。也就是說這個Address雖然是Employee的屬性,但是他們所有的屬性放到一個表裡是完全沒問題的。還有一種就是我們可以定義兩個分離的表,他們之間通過外鍵關聯。當然,除了這種,我們也可以定義一個單獨的映射表來儲存他們之間的映射關系。

    現在我們來看看以上各種方式的實作:

合并成一個表

    我們定義的Employee對象如下:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package model;  
  2. import javax.persistence.Embedded;  
  3. import javax.persistence.Entity;  
  4. import javax.persistence.Id;  
  5. import javax.persistence.Table;  
  6. @Entity  
  7. @Table(name="Employee")  
  8. public class Employee {  
  9.     @Id  
  10.     private int id;  
  11.     private String name;  
  12.     private long salary;  
  13.     @Embedded  
  14.     private Address address;  
  15.     public Address getAddress() {  
  16.         return address;  
  17.     }  
  18.     public void setAddress(Address address) {  
  19.         this.address = address;  
  20.     }  
  21.     public Employee() {}  
  22.     public Employee(int id) { this.id = id; }  
  23. // ...    
  24. }  

    這裡出于篇幅的限制,省略了那些屬性的get, set方法。

Address類的定義實作如下:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package model;  
  2. import javax.persistence.Access;  
  3. import javax.persistence.AccessType;  
  4. import javax.persistence.Column;  
  5. import javax.persistence.Embeddable;  
  6. @Embeddable  
  7. public class Address {  
  8.     private String street;  
  9.     private String city;  
  10.     private String state;  
  11.     @Column(name="Zip_Code")  
  12.     private String zip;  
  13. //...     
  14. }  

    我們來看代碼裡加入的标注。這裡Employee有一個Address的屬性。我們在Employee裡對這個屬性增加了@Embedded标注。而定義Address的類裡增加了@Embeddable标注。其中@Embedded表示Address元素被嵌入到Employee表中間。為了能夠嵌入到Employee表中,我們還需要将Address屬性增加@Embeddable,表示它是能夠被嵌入到其他對象裡面的。

    我們運作如下的代碼:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package main;  
  2. import javax.persistence.EntityManager;  
  3. import javax.persistence.EntityManagerFactory;  
  4. import javax.persistence.EntityTransaction;  
  5. import javax.persistence.Persistence;  
  6. import model.Address;  
  7. import model.Employee;  
  8. public class EmployeeTest {  
  9.     public static void main(String[] args) {  
  10.         EntityManagerFactory entityManagerFactory =    
  11.                 Persistence.createEntityManagerFactory("EmployeeService");  
  12.         EntityManager em = entityManagerFactory.createEntityManager();  
  13.         EntityTransaction userTransaction = em.getTransaction();  
  14.         userTransaction.begin();  
  15.         Employee employee = new Employee();  
  16.         employee.setName("frank");  
  17.         employee.setSalary(2000);  
  18.         Address address = new Address();  
  19.         address.setCity("Beijing");  
  20.         address.setState("BJ");  
  21.         address.setStreet("Shuangying");  
  22.         address.setZip("100000");  
  23.         employee.setAddress(address);  
  24.         em.persist(employee);  
  25.         userTransaction.commit();  
  26.         em.close();  
  27.         entityManagerFactory.close();  
  28.     }  
  29. }  

    我們現在來看資料庫的結果:

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料

外鍵限制

    在采用這種外鍵限制的時候我們需要首先考慮一下。根據我們定義的邏輯關系,可以認為是Employee裡有Address這麼一個字段。那麼就相當于Employee裡要引用到Address的資訊。而按照外鍵的定義,是一個表引用另外一個表的主鍵。那麼,我們就需要給Address定義一個主鍵,同時我們也要标注一下在Employee裡引用的Address字段該是什麼名字。我們定義的Employee如下:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package model;  
  2. import javax.persistence.CascadeType;  
  3. import javax.persistence.Entity;  
  4. import javax.persistence.GeneratedValue;  
  5. import javax.persistence.GenerationType;  
  6. import javax.persistence.Id;  
  7. import javax.persistence.JoinColumn;  
  8. import javax.persistence.OneToOne;  
  9. import javax.persistence.Table;  
  10. @Entity  
  11. @Table(name="Employee")  
  12. public class Employee {  
  13.     @Id  
  14.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  15.     private int id;  
  16.     private String name;  
  17.     private long salary;  
  18.     @OneToOne(cascade=CascadeType.ALL)  
  19.     @JoinColumn(name="address_id")  
  20.     private Address address;  
  21.     public Address getAddress() {  
  22.         return address;  
  23.     }  
  24.     public void setAddress(Address address) {  
  25.         this.address = address;  
  26.     }  
  27.     public Employee() {}  
  28.     public Employee(int id) { this.id = id; }  
  29. // ...  
  30. }  

    這裡,我們增加了一個标注@OneToOne(cascade=CascadeType.ALL)和@JoinColumn(name="address_id")。@OneToOne表示他們是一對一的關系,同時cascade表示他們的級聯關系。如果我們仔細觀察一下的話會發現前面應用代碼裡有一個比較有趣的地方,我們em.persist()隻是儲存了employee對象。而對應的Address對象隻是設定為employee對象的一個屬性。我們希望是employee對象被儲存到資料庫裡的時候address對象也自動儲存進去。那麼我們就需要設定這個cascade的級聯通路屬性。否則我們就需要顯式的利用em.persist()來儲存address對象。這也就是為什麼我們要用一個cascade的屬性。

  @JoinColumn裡面指定了Employee表裡引用到Address時關聯的名字是什麼。

    和前面的定義比較起來,Address的定義如要增加了一個id:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. @Id  
  2. @GeneratedValue(strategy = GenerationType.IDENTITY)  
  3. private int id;  

    在我們運作程式前需要将Address加入到persistence.xml檔案裡。因為我們将它映射到一個單獨的表裡。我們再運作前面的程式,發現生成如下的表結構:

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料

單獨的映射表

    采用這種映射方式的實作很簡單,隻需要在Employee裡面做一點如下的修改:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. @OneToOne(cascade=CascadeType.ALL)  
  2. @JoinTable(name="employee_address",joinColumns=@JoinColumn(name="address_id"),inverseJoinColumns=@JoinColumn(name="employee_id"))  
  3. private Address address;  

 其他地方都不需要改變。這裡的@JoinTable描述了關聯表的名字,他們互相關聯的時候裡面包含的字段。一個為address_id,一個為employee_id。

    運作結果如下圖:

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料

一對多

    讨論完了前面的一對一映射,我們再來看看一對多的關系。其實和前面一對一的關系很類似,我們可以猜想得到,既然前面描述一對一的關系有@OneToOne,我們這裡應該有@OneToMany的映射關系。而且比較有意思的是,在JPA裡,除了一對多的關系,也存在着一個多對一的關系描述。那麼,在哪些情況下該使用一對多來描述,哪些情況下用多對一來描述呢?我覺得這主要還是取決于我們設計的對象邏輯關系。通過對象的邏輯關系來設定不同的選擇。

    我們來看一個示例。

Many to one

     假定我們有一個Employee類,然後還有一個Department類。對于每個Employee來說,他屬于且僅屬于一個Department。這樣對于Employee和Department來說,他們就構成一個多對一的關系。而從Department的角度來說,他們則是一個一對多的關系。假定我們要求Employee對象有一個方法來取得他所在的Department。這樣從面向對象的角度來說,我們可能希望Employee有一個指向Department的引用。于是按照這種思路,我們設計Employee的代碼如下:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package model;  
  2. import javax.persistence.CascadeType;  
  3. import javax.persistence.Entity;  
  4. import javax.persistence.GeneratedValue;  
  5. import javax.persistence.GenerationType;  
  6. import javax.persistence.Id;  
  7. import javax.persistence.JoinColumn;  
  8. import javax.persistence.ManyToOne;  
  9. import javax.persistence.Table;  
  10. @Entity  
  11. @Table(name="Employee")  
  12. public class Employee {  
  13.     @Id  
  14.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  15.     private int id;  
  16.     private String name;  
  17.     private long salary;  
  18.     @ManyToOne(cascade=CascadeType.ALL)  
  19.     @JoinColumn(name="Dept_Id")  
  20.     private Department department;  
  21.     public Employee() {}  
  22.     public Employee(int id) { this.id = id; }  
  23. // ...    
  24. }  

    這裡我們省略了對所有屬性get, set方法。這裡用了一個@ManyToOne的标注。并設定了映射的名字為“Dept_Id”。

    而Department的定義則如下:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package model;  
  2. import javax.persistence.Column;  
  3. import javax.persistence.Entity;  
  4. import javax.persistence.GeneratedValue;  
  5. import javax.persistence.GenerationType;  
  6. import javax.persistence.Id;  
  7. import javax.persistence.Table;  
  8. @Entity  
  9. @Table(name="Department")  
  10. public class Department {  
  11.     @Id  
  12.     @Column(name="Dept_Id")  
  13.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  14.     private long id;  
  15.     @Column(name="name")  
  16.     private String name;  
  17. // ...  
  18. }  

    這個類的定義則沒有什麼特殊的。和前面定義的差不多。

    運作完之後的資料庫情況如下:

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料

One to many 

    還是前面那個示例,不過這次我們換一個角度來考慮一下。前面是我們希望Employee有一個取得所在Department的屬性的方式。這裡我們希望從Department對象得到裡面所有的員工。從面向對象的角度來說,我們可以在Department裡面定義一個集合類來儲存一系列的Employee對象。那麼,這裡兩個類的定義形式則如下:

Employee:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package model;  
  2. import javax.persistence.Entity;  
  3. import javax.persistence.GeneratedValue;  
  4. import javax.persistence.GenerationType;  
  5. import javax.persistence.Id;  
  6. import javax.persistence.Table;  
  7. @Entity  
  8. @Table(name="Employee")  
  9. public class Employee {  
  10.     @Id  
  11.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  12.     private int id;  
  13.     private String name;  
  14.     private long salary;  
  15.     public Employee() {}  
  16.     public Employee(int id) { this.id = id; }  
  17. // ...  
  18. }  

    Employee的定義基本上不需要做什麼特殊的關系映射定義。

Department:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package model;  
  2. import java.util.List;  
  3. import javax.persistence.CascadeType;  
  4. import javax.persistence.Column;  
  5. import javax.persistence.Entity;  
  6. import javax.persistence.FetchType;  
  7. import javax.persistence.GeneratedValue;  
  8. import javax.persistence.GenerationType;  
  9. import javax.persistence.Id;  
  10. import javax.persistence.JoinColumn;  
  11. import javax.persistence.OneToMany;  
  12. import javax.persistence.Table;  
  13. @Entity  
  14. @Table(name="Department")  
  15. public class Department {  
  16.     @Id  
  17.     @Column(name="Dept_Id")  
  18.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  19.     private long id;  
  20.     @Column(name="name")  
  21.     private String name;  
  22.     @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)  
  23.     @JoinColumn(name="Dept_Id")  
  24.     private List<Employee> employees;  
  25.     // ...  
  26. }  

    這裡一個比較有意思的地方是在定義了employees的地方加了一個@OneToMany的标注。它同時也通過@JoinColumn說明了關聯的項。

    這裡我們運作的程式稍微修改了一下,代碼如下:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package main;  
  2. import java.util.ArrayList;  
  3. import java.util.List;  
  4. import javax.persistence.EntityManager;  
  5. import javax.persistence.EntityManagerFactory;  
  6. import javax.persistence.EntityTransaction;  
  7. import javax.persistence.Persistence;  
  8. import model.Department;  
  9. import model.Employee;  
  10. public class EmployeeTest {  
  11.     public static void main(String[] args) {  
  12.         EntityManagerFactory entityManagerFactory =    
  13.                 Persistence.createEntityManagerFactory("EmployeeService");  
  14.         EntityManager em = entityManagerFactory.createEntityManager();  
  15.         EntityTransaction userTransaction = em.getTransaction();  
  16.         userTransaction.begin();  
  17.         Employee employee = new Employee();  
  18.         Department dept = new Department();  
  19.         dept.setName("Information");  
  20.         List<Employee> employees = new ArrayList<Employee>();  
  21.         employee.setName("frank");  
  22.         employee.setSalary(2000);  
  23.         employees.add(employee);  
  24.         employee = new Employee();  
  25.         employee.setName("fred");  
  26.         employee.setSalary(3000);  
  27.         employees.add(employee);  
  28.         dept.setEmployees(employees);  
  29.         em.persist(dept);  
  30.         userTransaction.commit();  
  31.         em.close();  
  32.         entityManagerFactory.close();  
  33.     }  
  34. }  

    我們寫入了一個Employee的集合。是以最後運作結果生成的表和結果如下: 

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料

    和我們前面一對一映射的關系類似。我們也可以将一對多或者多對一的關系用一個中間關系表來儲存。在這裡實作就很簡單,之需要将Department裡面的@JoinColumn去掉就可以了。因為在JPA裡,如果我們設定了兩個對象之間的一對多屬性關系,它會預設生成一個中間表,并且表名以兩個表的名字加一個下劃線拼接起來。

多對多

    對于多對多的場景,看起來它會顯得更複雜一些。實際上則未必。因為當我們典型的針對這種關系來設計資料庫表的時候,肯定會考慮将他們拆分出一個中間的映射表來。這樣的話,基本上要實作這樣關系的映射,肯定就隻有拆分映射表這麼一條路。這裡定義了另外一個示例。假定Student和Department是一個多對多的關系。

這裡,我們定義了Student:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package model;  
  2. import java.util.ArrayList;  
  3. import java.util.Collection;  
  4. import javax.persistence.Entity;  
  5. import javax.persistence.GeneratedValue;  
  6. import javax.persistence.GenerationType;  
  7. import javax.persistence.Id;  
  8. import javax.persistence.JoinColumn;  
  9. import javax.persistence.JoinTable;  
  10. import javax.persistence.ManyToMany;  
  11. @Entity  
  12. public class Student {  
  13.   @Id @GeneratedValue(strategy=GenerationType.IDENTITY)  
  14.   private int id;  
  15.   private String name;  
  16.   @ManyToMany   
  17.   @JoinTable(name="Student_Dept",   
  18.       joinColumns=@JoinColumn(name="Stut_ID"),  
  19.       inverseJoinColumns=@JoinColumn(name="DEPT_ID"))    
  20.   private Collection<Department> departments;  
  21.   public Student() {  
  22.     departments = new ArrayList<Department>();  
  23. }  
  24.   public void addDepartment(Department department) {  
  25.     if (!getDepartments().contains(department)) {  
  26.         getDepartments().add(department);  
  27.     }  
  28.     if (!department.getStudents().contains(this)) {  
  29.       department.getStudents().add(this);  
  30.     }  
  31.   }  
  32.   public String toString() {  
  33.     return "\n\nID:" + id + "\nName:" + name ;  
  34.   }  
  35. // ...  
  36. }  

    這裡省略了元素的get, set方法。為了實作對象的雙向關聯,這裡定義的addDepartment需要将自己加入到目标Department對象的清單裡。另外,我們在對多的關系部分添加了一個@ManyToMany的标注,同時也指定@JoinTable的屬性。

    而對于Department:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package model;  
  2. import java.util.ArrayList;  
  3. import java.util.Collection;  
  4. import javax.persistence.Entity;  
  5. import javax.persistence.GeneratedValue;  
  6. import javax.persistence.GenerationType;  
  7. import javax.persistence.Id;  
  8. import javax.persistence.ManyToMany;  
  9. @Entity  
  10. public class Department {  
  11.     @Id @GeneratedValue(strategy=GenerationType.IDENTITY)  
  12.     private int id;  
  13.     private String name;  
  14.     @ManyToMany(mappedBy="departments")  
  15.     private Collection<Student> students;  
  16.     public Department(){  
  17.       students = new ArrayList<Student>();  
  18.     }  
  19.     public void addStudent(Student student) {  
  20.       if (!getStudents().contains(student)) {  
  21.           getStudents().add(student);  
  22.       }  
  23.       if (!student.getDepartments().contains(this)) {  
  24.           student.getDepartments().add(this);  
  25.       }  
  26.     }  
  27.     public String toString() {  
  28.         return "Department id: " + getId() +   
  29.                ", name: " + getName();  
  30.     }  
  31. // ...  
  32. }  

    這裡引用Student集合的地方也用了一個@ManyToMany的标注,同時,裡面還設定了一個mappedBy屬性。這個屬性有什麼用呢?它是指定我們這個students的集合是映射到Student類裡面的departments清單。這樣後面生成的對象才找到對應的映射一方。還有一個就是,既然是多對多的映射,在Student裡面定義了jointable的屬性,這和我們在Department裡面定義有什麼差别呢?

    這個問題在于我們想定義這個關系的擁有方。我們把jointable的定義放在哪個類這個類就成為了擁有方。那麼這個擁有方來綁定和解除他們的關系。另外一方則不能用來綁定和解除他們的關系。

    我們調用的代碼實作如下:

Java代碼  

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料
  1. package main;  
  2. import java.util.List;  
  3. import javax.persistence.EntityManager;  
  4. import javax.persistence.EntityManagerFactory;  
  5. import javax.persistence.Persistence;  
  6. import javax.persistence.Query;  
  7. import model.Department;  
  8. import model.Student;  
  9. public class Main {  
  10.   static EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService");  
  11.   static EntityManager em = emf.createEntityManager();  
  12.   public static void main(String[] a) throws Exception {  
  13.     em.getTransaction().begin();  
  14.     Student student = new Student();  
  15.     student.setName("Joe");  
  16.     em.persist(student);  
  17.     Student student1 = new Student();  
  18.     student1.setName("Joe");  
  19.     em.persist(student1);  
  20.     Department dept = new Department();  
  21.     dept.setName("dept name");  
  22.     dept.addStudent(student);  
  23.     dept.addStudent(student1);  
  24.     em.persist(dept);  
  25.     em.flush();  
  26.     Query query = em.createQuery("SELECT e FROM Student e");  
  27.     List<Student> list = (List<Student>) query.getResultList();  
  28.     System.out.println(list);  
  29.     query = em.createQuery("SELECT d FROM Department d");  
  30.     List<Department> dList = (List<Department>) query.getResultList();  
  31.     System.out.println(dList);  
  32.     em.getTransaction().commit();  
  33.     em.close();  
  34.     emf.close();  
  35.   }  
  36. }  

    這裡,我們添加了兩個Student對象到一個Department對象中。他們運作後的資料表結構如下:

eclipselink + jpa + mysql 的用法 一個簡單的eclipselink  + jpa  + mysql 的用法示例 各種關系映射 總結 參考材料

    多對多關系對應的對象關系還有一個比較麻煩的地方在于。一旦建立了雙方的對象關系之後,就基本上形成了一個對象之間的循環引用。從面向對象的角度來說這不是一個合适的設計。另外,從記憶體管理的角度來看,這樣也容易導緻記憶體洩漏。因為我們一不小心就容易導緻一些引用的對象沒有被釋放。在将這些關系映射到不同對象的時候,一定要非常的慎重。

總結

    以前使用一些語言和開發架構的時候用到ORM。這裡針對一個最基本的JPA示例一并探讨了幾種典型的對象映射關系在JPA中的實作。有了這些對象關系的定義作為基礎我們可以定義很多面向對象的方法而不用采取直接拼接sql字元串的方式與資料庫互動。由于要針對這些關系一一讨論,本文的篇幅就會顯得長一些。

參考材料

pro jpa2

http://www.java2s.com/Tutorial/Java/0355__JPA/ManyToManyJoinTableJoinInverseJoinColumn.htm