Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 346|回复: 0

Hibernate @OneToOne懒加载实现解决方案

[复制链接]
  • TA的每日心情
    奋斗
    昨天 12:49
  • 签到天数: 789 天

    [LV.10]以坛为家III

    2049

    主题

    2107

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    722638
    发表于 2021-6-30 13:46:18 | 显示全部楼层 |阅读模式

    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
    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-9-7 03:19 , Processed in 2.057743 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表