Hibernate @OneToOne懶加載實現解決方案


hibernate注解(三)中,我提高過一對一(@OneToOne)懶加載失效的問題。雖然給出了解決方法,但並沒有給出完整的解決方案。今天我專門針對該問題進行討論。至於懶加載失效的原因,在之前的文章中已經我已經敘述過了,就不再重復了,不明白的可以去看看。

一、測試環境

數據庫:myqsl

代碼:主:Student,從:Card

表:


   
   
  
  
          
  1. DROP TABLE IF EXISTS `student`;
  2. CREATE TABLE `student` (
  3. `ID` int( 11) NOT NULL,
  4. `NAME` varchar( 50) NOT NULL,
  5. `CARD_ID` int( 11) DEFAULT NULL,
  6. PRIMARY KEY ( `ID`),
  7. KEY `PK_CARD_ID` ( `CARD_ID`),
  8. CONSTRAINT `PK_CARD_ID` FOREIGN KEY ( `CARD_ID`) REFERENCES `card` ( `ID`) ON DELETE NO ACTION ON UPDATE NO ACTION
  9. ) ENGINE= InnoDB DEFAULT CHARSET=utf8;
  10. DROP TABLE IF EXISTS `card`;
  11. CREATE TABLE `card` (
  12. `ID` int( 11) NOT NULL,
  13. `CODE` varchar( 32) NOT NULL,
  14. PRIMARY KEY ( `ID`)
  15. ) ENGINE= InnoDB DEFAULT CHARSET=utf8;

代碼:


   
   
  
  
          
  1. package com.po;
  2. import javax.persistence.CascadeType;
  3. import javax.persistence.Column;
  4. import javax.persistence.Entity;
  5. import javax.persistence.FetchType;
  6. import javax.persistence.GeneratedValue;
  7. import javax.persistence.GenerationType;
  8. import javax.persistence.Id;
  9. import javax.persistence.JoinColumn;
  10. import javax.persistence.OneToOne;
  11. import javax.persistence.Table;
  12. @Entity
  13. @Table(name = "Student")
  14. public class Student {
  15. private int id;
  16. private String name;
  17. private Card card;
  18. @Id
  19. @GeneratedValue(strategy = GenerationType.IDENTITY)
  20. @Column(name = "ID", unique = true, nullable = false)
  21. public int getId() {
  22. return id;
  23. }
  24. public void setId(int id) {
  25. this.id = id;
  26. }
  27. @Column(name = "NAME", nullable = false, length = 50)
  28. public String getName() {
  29. return name;
  30. }
  31. public void setName(String name) {
  32. this.name = name;
  33. }
  34. @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  35. @JoinColumn(name = "CARD_ID")
  36. public Card getCard() {
  37. return card;
  38. }
  39. public void setCard(Card card) {
  40. this.card = card;
  41. }
  42. }

   
   
  
  
          
  1. package com.po;
  2. import javax.persistence.CascadeType;
  3. import javax.persistence.Column;
  4. import javax.persistence.Entity;
  5. import javax.persistence.FetchType;
  6. import javax.persistence.GeneratedValue;
  7. import javax.persistence.GenerationType;
  8. import javax.persistence.Id;
  9. import javax.persistence.OneToOne;
  10. import javax.persistence.Table;
  11. @Entity
  12. @Table(name = "card")
  13. public class Card {
  14. private int id;
  15. private String code;
  16. private Student student;
  17. @Id
  18. @GeneratedValue(strategy = GenerationType.IDENTITY)
  19. @Column(name = "ID", unique = true, nullable = false)
  20. public int getId() {
  21. return id;
  22. }
  23. public void setId(int id) {
  24. this.id = id;
  25. }
  26. @Column(name = "CODE", length = 32, nullable = false)
  27. public String getCode() {
  28. return code;
  29. }
  30. public void setCode(String code) {
  31. this.code = code;
  32. }
  33. @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "card")
  34. public Student getStudent() {
  35. return student;
  36. }
  37. public void setStudent(Student student) {
  38. this.student = student;
  39. }
  40. }

方案一

在card表增加一個student表的外鍵字段STUDENT_ID,並在Card類的@OneToOne下增加@JoinColumn(name = "STUDENT_ID"),去掉mappedBy = "card",即


   
   
  
  
          
  1. DROP TABLE IF EXISTS `card`;
  2. CREATE TABLE `card` (
  3. `ID` int( 11) NOT NULL,
  4. `CODE` varchar( 32) NOT NULL,
  5. `STUDENT_ID` int( 11) DEFAULT NULL,
  6. PRIMARY KEY ( `ID`),
  7. KEY `PK_STUDENT_ID` ( `STUDENT_ID`),
  8. CONSTRAINT `PK_STUDENT_ID` FOREIGN KEY ( `STUDENT_ID`) REFERENCES `student` ( `ID`) ON DELETE NO ACTION ON UPDATE NO ACTION
  9. ) ENGINE= InnoDB DEFAULT CHARSET=utf8;

   
   
  
  
          
  1. public class Card {
  2. // ... 略
  3. @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  4. @JoinColumn(name = "STUDENT_ID")
  5. public Student getStudent() {
  6. return student;
  7. }
  8. // ... 略
  9. }

優點:不改變Student與Card在代碼中的對應關系(一對一)

缺點:需要同時維護Student和Card的兩個外鍵。

方案二

改為主鍵關聯。


   
   
  
  
          
  1. DROP TABLE IF EXISTS `student`;
  2. CREATE TABLE `student` (
  3. `ID` int( 11) NOT NULL,
  4. `NAME` varchar( 50) NOT NULL,
  5. PRIMARY KEY ( `ID`),
  6. CONSTRAINT `PK_CARD_ID` FOREIGN KEY ( `ID`) REFERENCES `card` ( `ID`) ON DELETE NO ACTION ON UPDATE NO ACTION
  7. ) ENGINE= InnoDB DEFAULT CHARSET=utf8;

   
   
  
  
          
  1. public class Student {
  2. // ... 略
  3. @Id
  4. @GenericGenerator(name = "PK_Card", strategy = "foreign", parameters = @Parameter(name = "property", value = "card"))
  5. @GeneratedValue(generator = "PK_Card")
  6. @Column(name = "ID", unique = true, nullable = false)
  7. public int getId() {
  8. return id;
  9. }
  10. // ... 略
  11. @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, optional = false)
  12. @PrimaryKeyJoinColumn
  13. public Card getCard() {
  14. return card;
  15. }
  16. // ... 略
  17. }

   
   
  
  
          
  1. public class Card {
  2. // ... 略
  3. @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "card", optional = false)
  4. public Student getStudent() {
  5. return student;
  6. }
  7. // ... 略
  8. }

除了改變student表的主鍵、外鍵結構外,Student類和Card類也要做相應修改,尤其注意“optional”,要設置為false,否則無法實現懶加載。

優點:不改變Student與Card在代碼中的對應關系(一對一)

缺點:改動較大,且使用主鍵關聯具有局限性。

 

PS:主鍵關聯的局限性

使用主鍵關聯會影響數據存儲結構,主鍵關聯是一種強耦合,以上述為例:Card存在時,Student才能存在,Card消亡時,Student也隨之消失。這是因為Student的主鍵依賴於Card主鍵,Student無法獨立存在(就是說必須先有學生卡,才能有學生)。

方案三

將Card類中的OneToOne改為OneToMany(一對多)。


   
   
  
  
          
  1. public class Card {
  2. private Set students;
  3. // ... 略
  4. @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "card")
  5. public Set<Student> getStudents() {
  6. return students;
  7. }
  8. public void setStudents(Set students) {
  9. this.students = students;
  10. }
  11. // ... 略
  12. }

優點:數據庫不用修改

缺點:需要修改Student與Card在代碼中的對應關系

方案四

放棄用注解的方式,改為Xml方式來實現hibernate模型設計,並在Card Xml的OneToOne標簽中添加constrained屬性,靠注解解決的辦法已經沒有了(instrument增強就算了吧,很麻煩)。

 

最后,我們來評估下以上方案的可行性。

方案一:從可讀性來講,是最容易理解的,但需要維護兩個外鍵,如果程序控制不好的話,容易出問題,即關聯錯誤。

方案二:主鍵關聯雖然有些約束,但也取決於業務需求,比如訂單和訂單詳情,采用主鍵關聯也挺合適的,只是不適合相對靈活的對象關系。

方案三:改動在我看來是最小的了,犧牲了一定的可讀性(關系從Card角度看變為了一對多),我個人比較喜歡該種方案,因此推薦。

方案四:如果不采用注解,而采用Xml的話,我是很推薦這種方案的,注解雖然優點多,也趨於主流,但最傳統的Xml,功能還是最強大的。但如果你僅為了解決該問題,而將注解和Xml混合使用的話,我建議你還是放棄吧。

原文地址:https://blog.csdn.net/wangpeng047/article/details/19624795


免責聲明!

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



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