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實體類


   
   
  
  
          
  1. @Entity
  2. @Table(name= "partner_info")
  3. public class Partner {
  4. private Integer partner_id;
  5. private String partner_name;
  6. private Grade grade;
  7. @Id
  8. @Column(name= "partner_id")
  9. @GeneratedValue(strategy=GenerationType.IDENTITY)
  10. public Integer getPartner_id() {
  11. return partner_id;
  12. }
  13. public void setPartner_id(Integer partner_id) {
  14. this.partner_id = partner_id;
  15. }
  16. @Column(name= "partner_name")
  17. public String getPartner_name() {
  18. return partner_name;
  19. }
  20. public void setPartner_name(String partner_name) {
  21. this.partner_name = partner_name;
  22. }
  23. @JoinColumn(name= "team_id")
  24. @ManyToOne
  25. public Grade getGrade() {
  26. return grade;
  27. }
  28. public void setGrade(Grade grade) {
  29. this.grade = grade;
  30. }
  31. }

Grade實體類


   
   
  
  
          
  1. @Entity
  2. @Table(name= "grade_info")
  3. public class Grade {
  4. private Integer teamId;
  5. private String teamName;
  6. @Id
  7. @Column(name= "team_id")
  8. @GeneratedValue(strategy=GenerationType.IDENTITY)
  9. public Integer getTeamId() {
  10. return teamId;
  11. }
  12. public void setTeamId(Integer teamId) {
  13. this.teamId = teamId;
  14. }
  15. @Column(name= "team_name",columnDefinition= "varchar(50) not null ")
  16. public String getTeamName() {
  17. return teamName;
  18. }
  19. public void setTeamName(String teamName) {
  20. this.teamName = teamName;
  21. }
  22. }

測試下:


   
   
  
  
          
  1. public static void main(String[] args) {
  2. EntityManagerFactory factory = Persistence.createEntityManagerFactory( "jpa");
  3. EntityManager entityManager = factory.createEntityManager();
  4. EntityTransaction transaction = entityManager.getTransaction();
  5. transaction.begin();
  6. //執行insert操作
  7. Grade grade = new Grade();
  8. grade.setTeamName( "初中1班");
  9. Partner p1 = new Partner();
  10. p1.setPartner_name( "張三風");
  11. Partner p2 = new Partner();
  12. p2.setPartner_name( "李四關");
  13. p1.setGrade(grade);
  14. p2.setGrade(grade);
  15. entityManager.persist(grade);
  16. entityManager.persist(p1);
  17. entityManager.persist(p2);
  18. transaction.commit();
  19. entityManager.close();
  20. factory.close();
  21. }

在實體創建中,字段的定義和數據庫表的字段類型、長度、是否非空一致,不一致容易出問題。

如果出現問題,可以試着將數據庫的表刪除,交給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注解標識映射關系


   
   
  
  
          
  1. @Entity
  2. @Table(name= "employee")
  3. public class Employee {
  4. @Id
  5. @Column(name= "id")
  6. @GeneratedValue(strategy=GenerationType.IDENTITY)
  7. private Integer id;
  8. @Column(name= "name")
  9. private String name;
  10. @Column(name= "salary",columnDefinition= "DECIMAL(10,2)")
  11. private BigDecimal salary;
  12. @JoinColumn(name= "pspace_id")
  13. @OneToOne
  14. private ParkingSpace ps;
  15. public Integer getId() {
  16. return id;
  17. }
  18. public void setId(Integer id) {
  19. this.id = id;
  20. }
  21. public String getName() {
  22. return name;
  23. }
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27. public BigDecimal getSalary() {
  28. return salary;
  29. }
  30. public void setSalary(BigDecimal salary) {
  31. this.salary = salary;
  32. }
  33. public ParkingSpace getPs() {
  34. return ps;
  35. }
  36. public void setPs(ParkingSpace ps) {
  37. this.ps = ps;
  38. }
  39. }

   
   
  
  
          
  1. @Entity
  2. @Table(name= "parking_space")
  3. public class ParkingSpace{
  4. @Id
  5. @Column(name= "id")
  6. @GeneratedValue(strategy=GenerationType.IDENTITY)
  7. private Integer id;
  8. @Column(name= "lot")
  9. private Integer lot;
  10. @Column(name= "location")
  11. private String location;
  12. public Integer getId() {
  13. return id;
  14. }
  15. public void setId(Integer id) {
  16. this.id = id;
  17. }
  18. public String getLocation() {
  19. return location;
  20. }
  21. public void setLocation(String location) {
  22. this.location = location;
  23. }
  24. public Integer getLot() {
  25. return lot;
  26. }
  27. public void setLot(Integer lot) {
  28. this.lot = lot;
  29. }
  30. }

雙向一對一關系

現在員工已經指向停車位,只要停車位實體在指向員就構成雙向的關系

首先,必須清楚包含連接列的一方,決定了是關系的所有者,可是在雙向一對一關系中,兩個映射均是一對一映射,兩方

都可以是所有者,最后,關系的擁有者只能有一個,連接列只能在一方,另一方只能指向關系的擁有者

ParkingSpace實體,添加@OneToOne表示映射關系,並且添加mappBy元素表示關系的所有方是Employee。

ParkingSpace修改如下;


   
   
  
  
          
  1. @Entity
  2. @Table(name= "parking_space")
  3. public class ParkingSpace{
  4. @Id
  5. @Column(name= "id")
  6. @GeneratedValue(strategy=GenerationType.IDENTITY)
  7. private Integer id;
  8. @Column(name= "lot")
  9. private Integer lot;
  10. @Column(name= "location")
  11. private String location;
  12. @OneToOne(mappedBy= "ps")
  13. private Employee el;
  14. public Employee getEl() {
  15. return el;
  16. }
  17. public void setEl(Employee el) {
  18. this.el = el;
  19. }
  20. ...........

雙向一對一關聯的規則;

1. @JoinColumn注解只能放置在映射到包含連接列的表的實體上。

2. mappedBy元素應該在沒有定義連接列的實體的@OneToOne注解中指定,就是沒有持有關系的一方使用

3. 雙向一對一映射關系中只能有一方使用mappedBy屬性

集合值關聯

當源實體引用一個或者多個目標實體實例時候,將使用一個多值關聯(many-value association)或者關聯集合。體現在一對多和多對多關聯關系中

1. 一對多映射

員工Employee和部門Department的關系,如果使用一對多映射表示,本質上是雙向的

一個關系是雙向的,意味着存在兩個映射關系。雙向的一對多映射意味着一個回源的多對一映射。

當一個源實體中有任意數量的目標實體存儲在它的集合屬性中,沒有可以擴展的方式可以用於在數據庫表中存儲這些

它所映射到的引用。如何在單行中存儲任意數量的外鍵?這是行不通的,因此,必須讓集合中的實體表能夠指向會

源實體表的外鍵。因而一對多是雙向的。


   
   
  
  
          
  1. @Entity
  2. @Table(name= "department_info")
  3. public class DepartmentInfo {
  4. @Id
  5. @Column(name= "id")
  6. @GeneratedValue(strategy=GenerationType.AUTO)
  7. private Integer id;
  8. @Column(name= "name")
  9. private String name;
  10. @OneToMany(mappedBy= "department")
  11. private List<Employee> list = new ArrayList<Employee>();
  12. public Integer getId() {
  13. return id;
  14. }
  15. public void setId(Integer id) {
  16. this.id = id;
  17. }
  18. public String getName() {
  19. return name;
  20. }
  21. public void setName(String name) {
  22. this.name = name;
  23. }
  24. public List<Employee> getList() {
  25. return list;
  26. }
  27. public void setList(List<Employee> list) {
  28. this.list = list;
  29. }
  30. }

   
   
  
  
          
  1. @Entity
  2. @Table(name= "employee")
  3. public class Employee {
  4. @Id
  5. @Column(name= "id")
  6. @GeneratedValue(strategy=GenerationType.IDENTITY)
  7. private Integer id;
  8. @Column(name= "name")
  9. private String name;
  10. @Column(name= "salary",columnDefinition= "DECIMAL(10,2)")
  11. private BigDecimal salary;
  12. @JoinColumn(name= "department_id")
  13. @ManyToOne
  14. private DepartmentInfo department;
  15. public Integer getId() {
  16. return id;
  17. }
  18. public void setId(Integer id) {
  19. this.id = id;
  20. }
  21. public String getName() {
  22. return name;
  23. }
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27. public BigDecimal getSalary() {
  28. return salary;
  29. }
  30. public void setSalary(BigDecimal salary) {
  31. this.salary = salary;
  32. }
  33. public DepartmentInfo getDepartment() {
  34. return department;
  35. }
  36. public void setDepartment(DepartmentInfo department) {
  37. this.department = department;
  38. }
  39. }

持久化先持久化一的一方,並且從持有關系的一方設值。


   
   
  
  
          
  1. DepartmentInfo di = new DepartmentInfo();
  2. di.setName( "java開發部2");
  3. Employee e1 = new Employee();
  4. e1.setName( "張三1");
  5. e1.setSalary( new BigDecimal( 110));
  6. Employee e2 = new Employee();
  7. e2.setName( "李四1");
  8. e2.setSalary( new BigDecimal( 200));
  9. e1.setDepartment(di);
  10. e2.setDepartment(di);
  11. //di.getList().add(e1); 持久化,只能從擁有關系的一方設值
  12. //di.getList().add(e2);
  13. entityManager.persist(di);
  14. entityManager.persist(e1);
  15. entityManager.persist(e2);

關於部門實體,有幾點需要注意

1. 使用明確類型的集合類來存儲,如果並不指定存儲的類型,就必須使用targetEntity指示指向的類的信息。

2. 定義雙向一對多關系時候,必須記住多的一方是關系的所有方,必須在那一方定義連接列,一對多的映射是反方,必須使用mappedBy元素

多對多映射

多對多是雙向的關系映射,關系雙方都是多對多關系,並且雙方均沒有連接列,需要借助中間表。因為每一個雙向關系都必須具有所有方和反方,必須兩個實體中挑選一個作為關系的持有者,反方使用mappedBy屬性指向持有關系的一方。

使用@ManyToMany在實體的集合屬性上表示映射關系。


   
   
  
  
          
  1. @Entity
  2. @Table(name= "employee")
  3. public class Employee {
  4. @Id
  5. @Column(name= "id")
  6. @GeneratedValue(strategy=GenerationType.IDENTITY)
  7. private Integer id;
  8. @Column(name= "name")
  9. private String name;
  10. @Column(name= "salary",columnDefinition= "DECIMAL(10,2)")
  11. private BigDecimal salary;
  12. @JoinTable(name= "employee_project_inner",
  13. //中間表product_id字段
  14. joinColumns={ @JoinColumn(name= "employee_id",referencedColumnName= "id")},
  15. inverseJoinColumns={ @JoinColumn(name= "project_id",referencedColumnName= "id")}
  16. )
  17. @ManyToMany
  18. private List<ProjectInfo> projects = new ArrayList<ProjectInfo>();
  19. public Integer getId() {
  20. return id;
  21. }
  22. public void setId(Integer 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 BigDecimal getSalary() {
  32. return salary;
  33. }
  34. public void setSalary(BigDecimal salary) {
  35. this.salary = salary;
  36. }
  37. public List<ProjectInfo> getProjects() {
  38. return projects;
  39. }
  40. public void setProjects(List<ProjectInfo> projects) {
  41. this.projects = projects;
  42. }
  43. }

@JoinTable說明;

joinColumns元素描述關系所有方在中間表的連接列,inverseJoinColumns元素指定了反方在中間表的連接列。

反方,使用mappedBy屬性指向關系擁有的一方。


   
   
  
  
          
  1. @Entity
  2. @Table(name= "project_info")
  3. public class ProjectInfo {
  4. @Id
  5. @Column(name= "id")
  6. @GeneratedValue(strategy=GenerationType.IDENTITY)
  7. private Integer id;
  8. @Column(name= "name")
  9. private String name;
  10. @ManyToMany(mappedBy= "projects")
  11. private List<Employee> employees = new ArrayList<Employee>();
  12. public Integer getId() {
  13. return id;
  14. }
  15. public void setId(Integer id) {
  16. this.id = id;
  17. }
  18. public String getName() {
  19. return name;
  20. }
  21. public void setName(String name) {
  22. this.name = name;
  23. }
  24. }

單向集合映射

 

       當一個實體到目標實體存在一對多映射,但是@OneToMany注解不包括mappedBy元素時,就認為它存在目標實體的單向關系,此時,兩個實體都不存在連接列,是單向的映射關系

 

       需要借助中間表,存儲映射關系

,同樣的當多對多關系中,一方沒有映射到另一方時候,就是一個單向的關系,也要借助連接表。

唯一的區別是,只有兩個實體類型中的一個會使用該表來加載器相關的實體,或者更新它已存儲新增的實體關聯

創建實體:

目標實體中不會存在集合特性,源實體的@OneToMany注解中不存在mappedBy屬性,

就是phone中沒有集合屬性,employee中@OneToMany注解不用mappedBy屬性


   
   
  
  
          
  1. @Entity
  2. @Table(name= "employee")
  3. public class Employee {
  4. @Id
  5. @Column(name= "id")
  6. @GeneratedValue(strategy=GenerationType.IDENTITY)
  7. private Integer id;
  8. @Column(name= "name")
  9. private String name;
  10. @Column(name= "salary",columnDefinition= "DECIMAL(10,2)")
  11. private BigDecimal salary;
  12. @JoinTable(name= "employee_phone_inner",
  13. //中間表product_id字段
  14. joinColumns={ @JoinColumn(name= "employee_id",referencedColumnName= "id")},
  15. inverseJoinColumns={ @JoinColumn(name= "phone_id",referencedColumnName= "id")}
  16. )
  17. @OneToMany
  18. private List<Phone> phones = new ArrayList<Phone>(); //.. 省略get/set方法

   
   
  
  
          
  1. @Entity
  2. @Table(name= "phone")
  3. public class Phone {
  4. @Id
  5. @Column(name= "id")
  6. @GeneratedValue(strategy=GenerationType.IDENTITY)
  7. private Integer id;
  8. @Column(name= "name")
  9. private String num;
  10. public Integer getId() {
  11. return id;
  12. }
  13. public void setId(Integer id) {
  14. this.id = id;
  15. }
  16. public String getNum() {
  17. return num;
  18. }
  19. public void setNum(String num) {
  20. this.num = num;
  21. }
  22. }

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM