最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

hibernate

运维笔记admin17浏览0评论

hibernate

hibernate

-

2023年4月19日发(作者:飞鸽传书2007绿色版)Hibernate使⽤详解(⼀)⼀、前⾔ 这些天都在为公司框架重构做准备,浏览了⼀下代码,挑了⼏个不熟或者没接触过的知识点进⾏攻坚,hibernate是其中之⼀。其实接触hibernate是在⼤学期间,应该是在2012年,已经2017-2012=5年时间了,当初给我的印象就是hibernate难学(特别是关联关系的配置这块内容),没学好,很多概念当时理解不了,于是我经⼿的项⽬基本都是使⽤mybatis,不再去碰这个“⿇烦”(所以,给⼈的第⼀印象很重要,平时要注意⼀下形象了)。但是呢,我发现,编程的世界就是这么⼩,兜兜转转最后还是需要照⾯,于是乎,我决定,啃下这块⾻头~我翻出了2012年的⼤学课件,也在⽹上搜索了⼀⼤堆的博⽂,算是理清了hibernate关联关系配置这块内容,果真是“会当凌绝顶,⼀览众⼭⼩”,现在回头想想,hibernate并没有第⼀印象那么难,只是有些细节需要注意~ 本篇博⽂持续更新,主要是记录⼀些hibernate使⽤细节、难点,知识点顺序不分难易,想到哪记到哪⼉,有需要⾃⾏全⽂搜索,如有错误之处,还望斧正~ 本⽂运⾏环境: jdk1.8.0_131 Eclipse Mars.2 Release (4.5.2) Mysql 5.6.14⼆、正⽂ 写这篇博⽂的起因是研究hibernate的关联关系配置过程中,发现很多细节问题,对于新⼿或者⼀知半解的⼈来说,理解起来很困难,作为“过来⼈”,我希望能⽤通俗⼀点的描述加上⾃⼰写的实例代码解决同⾏的疑惑,所以这边就先记录⼀下“关联关系”配置过程中的问题~ 数据库中表与表之间的关系分为三种:⼀对⼀,⼀对多,多对多。数据表是如何体现这种关系的呢?对于⼀对⼀和⼀对多,会在其中⼀张表增加⼀个外键字段(有可能和这张表的主键同⼀字段),关联另外⼀张表的主键,多对多则会建⽴⼀张中间表,存储了两张关系表的主键,hibernate中的关联关系是建⽴在数据库中表的这层关系之上。⾄于hibernate中单向、双向问题,完全是业务需求决定的,因为从数据库层⾯来讲,A表和B表有关联关系,那么必定可以通过连接查询,从A表查询出B表的信息,或者从B表查询出A表的信息,所以,从数据库的层⾯来说,就是双向的。⽽到了程序⾥⾯,有些时候我们只需要从A表对应(映射)的ClassA查询出B表对应(映射)的ClassB,⽽不需要从ClassB查询出ClassA,这时我们⽤单向就⾏,如果需要双向查询,这样的情况,就需要双向的关联关系。所以希望初学者不要迷惑hibernate中单双向配置问题,这个完全是业务需求决定,要单向就配置单向,要双向就配置双向。1)cascade和inverse之间的区别 cascade定义的是级联,也就是说对某个对象A做CRUD(增删改查)操作是否同样对其所关联的另外⼀个对象B做同样的操作。⽽inverse定义的是维护关联关系的责任,这个怎么理解呢?现有⼀个数据表Student如下,其中cid表⽰的是Classes表的id: Classes表: 表Student中的cid是外键,关联Classes的主键id,这两张表的关联关系就体现在cid字段上,如果某条记录cid为空,那么当条记录就与Classes中的任何记录⽆关联关系,假如整个表这个字段都为空,那么这张表就和Classes⽆关联关系。inverse定义的就是谁去维护这个cid字段的责任!就是由谁去设置这个值!这样说可能也不太确切,应该这样表述:哪个类对应的映射配置了inverse="false"(默认都是false,并且只有集合标记“set/map/list/array/bag”才有inverse属性”),那么就是对这个类进⾏CRUD的时候,触发hibernate去维护这个字段!如果还是不太清楚,那么请看下⾯代码~ 假设现在有⼀个班级类(Classes),学⽣类(Student),他们之间是“⼀对多”的关系,在学⽣类(Student)中包含⼀个队Classes类的引⽤,Classes不包含对学⽣类的引⽤,两个类以及对应的映射⽂件分别如下: Student类: 1 package ; 2 3 public class Student { 4 private int id; 5 private String name; 6 private Classes cls; 7 public int getId() { 8 return id; 9 }10 public void setId(int id) {11 this.id = id;12 }13 public String getName() {14 return name;15 }16 public void setName(String name) {17 this.name = name;18 }19 public Classes getCls() {20 return cls;21 }22 public void setCls(Classes cls) {23 this.cls = cls;24 }25 26 } : 1 <?xml version="1.0"?> 2 5 6 7 8 9 10 11 12 13 14 Classes类: 1 package ; 2 3 import t; 4 import ; 5 6 public class Classes { 7 private int id; 8 private String clsName; 9 10 public int getId() {11 return id;12 }13 public void setId(int id) {14 this.id = id;15 }16 public String getClsName() {17 return clsName;18 }19 public void setClsName(String clsName) {20 this.clsName = clsName;21 }22 23 24 } : 1 <?xml version="1.0"?> 2 5 6 7 8 9 10 11 12 13 14 再附加⼀个的配置吧~ : 1 <?xml version="1.0" ?> 2 5 6 7 8 9 10 jdbc:mysql://localhost:3306/test 11 12 root 13 root.123 14 15 5InnoDBDialect16 17 true18 19 true20 21 22 23 24 由中配置可知,配置的是“多对⼀”关系中的单向关联。注意:在关联关系中,没有inverse属性,但是默认就是由配置这端去维护关联关系(也就是设置外键字段的值),相当于默认inverse="false",在节点有个cascade属性,其取值有如下⼏个(多个cascade属性之间可以⽤英⽂逗号隔开,⽐如:cascade="save-update,delete"): :默认值,Session操作当前对象时,忽略其他关联的对象:当通过Session的delete()⽅法删除当前的对象时,会级联删除所有关联的对象-orphan:接触所有和当前对象解除关联关系的对象 例如:ers().clear(); 执⾏后,数据库中的先前与该customer相关联的order都被删除。-update:当通过Session的save()、update()及saveOrUpdate()⽅法更新或保存当前对象 时,级联保存所有关联的新建的临时对象,并且级联更新所有关联的游离对象t:当通过Session的persist()⽅法来保存当前对象时,会级联保存所关联的 新建的临时对象:当通过Session的merge()⽅法来保存当前对象时,会级联融合所有关联的游离对象:当通过Session的lock()⽅法把当前游离对象加⼊到Session()缓存中时,会把所有关联的游离对象也加⼊到 Session缓存中。ate:当通过Session的replicate()⽅法赋值当前对象时,会级联赋值所有关联的对象:当通过Session的evict()⽅法从Session缓存中清除当前对象时,会级联清除所有关联的对象h:当通过Session的refresh()⽅法刷新当前对象时,会级联刷新所有关联的对象,所为刷新是指读取数据库中相应的数据 然后根据数据库中的最新的数据去同步更新Session缓存中的数据:包含save-update、persist、merge、delete、lock、replicate、evict及refresh的⾏为-delete-orphan:包含all和delete-orphan的⾏为 这边配置了cascade="all"属性之后,如果Student中cls有值,那么在保存Student对象的时候,也会保存cls引⽤的Classess对象到表Classes中,默认cascade="none",此时保存Student对象时,就算cls有值,也不会保存到表Classes中,这就是级联的作⽤: HibernateMain类: 1 package ; 2 3 import n; 4 import nFactory; 5 import ction; 6 import uration; 7 8 import s; 9 import t;10 11 public class HibernateMain {12 13 public static void main(String[] args) {14 // TODO Auto-generated method stub15 Configuration cfg = new Configuration().configure();16 SessionFactory factory = essionFactory();17 Session session = ssion();18 Transaction ts = nsaction();19 ();20 21 Student st1 = new Student();22 e("学⽣甲");23 24 Student st2 = new Student();25 e("学⽣⼄");26 27 Classes cls = new Classes();28 Name("班级2");29 30 (cls);31 (cls);32 33 (st1);34 (st2);35 36 ();37 (0);38 }39 40 } 控制台输出的sql语句执⾏顺序(⼀对多关联关系,save时,先save“⼀”的⼀⽅,然后才是“多”的⼀⽅,删除的时候,先删除“多”的⼀⽅,然后才是“⼀”的⼀⽅):Hibernate: insert into classes (name) values (?)Hibernate: insert into Student (name, cid) values (?, ?)Hibernate: insert into Student (name, cid) values (?, ?) 数据库中的数据: classes: student: 现在将两张表数据删除,并且将⽂件中节点的cascade属性删除(默认cascade=“none”),然后再执⾏上⾯的代码,这个时候你会发现如下报错,这是什么原因呢?前⾯我有说过,节点虽然没有inverse属性,但是hibernate默认赋予配置的⼀端,在对这个类进⾏CRUD的时候,触发hibernate去维护体现关联关系的字段(也就是设置“外键”cid的值),在执⾏的代码⾥⾯,Student类实例st1和st2都设置了cls属性,这就向Hibernate表明,需要维护体现关联关系那个字段(因为默认本端维护,⽆法修改),但是cascade属性并没有设置(默认为cascade="none"),也就是在保存st1和st2的时候,并不会先保存cls引⽤的Classes对象,⽽要维护cid这个“外键”字段时,⼜必须要先保存Class对象才能获取到这个cid,这边就出现冲突(这边是个⼈理解,仅供参考,我觉得这边应该还涉及到hibernate中持久化对象状态问题,但是现象上来说可以这⼉解释)。如果我们不去设置st1和st2的cls属性,那么我们是能够保存成功的(这边就不贴执⾏结果了)⼗⽉ 11, 2017 3:50:28 下午 ionMapperStandardImpl mapManagedFlushFailureERROR: HHH000346: Error during managed flush [entObjectException: object references an unsaved transient instance - save the transient instance before flushing: s]Exception in thread "main" lStateException: entObjectException: object references an unsaved transient instance - save the transient instance before flushing: s at t(:146) at t(:157) at t(:164) at h(:1443) at dFlush(:493) at eforeTransactionCompletion(:3207) at TransactionCompletion(:2413) at TransactionCompletion(:467) at CompletionCallback(:156) at $100(:38) at sourceLocalTransactionCoordinatorImpl$(:231) at (:68) at (:36)Caused by: entObjectException: object references an unsaved transient instance - save the transient instance before flushing: s at ityIdentifierIfNotUnsaved(:279) at ntifier(:462) at y(:315) at y(:326) at rty(:325) at rty(:4218) at heck(:528) at teNecessary(:215) at hEntity(:142) at ntities(:216) at verythingToExecutions(:85) at h(:38) at h(:1437) ... 9 more 接下来,我们再测试⼀下“⼀对多”双向关联关系,Student类和都不需要改变,我们将Classes类和修改如下: Classes类: 1 package ; 2 3 import t; 4 import ; 5 6 public class Classes { 7 private int id; 8 private String clsName; 9 private Set students = new HashSet();10 11 public int getId() {12 return id;13 }14 public void setId(int id) {15 this.id = id;16 }17 public String getClsName() {18 return clsName;19 }20 public void setClsName(String clsName) {21 this.clsName = clsName;22 }23 public Set getStudents() {24 return students;25 }26 public void setStudents(Set students) {27 this.students = students;28 }29 30 31 } : 1 <?xml version="1.0"?> 2 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 然后执⾏的代码改为: 1 package ; 2 3 import n; 4 import nFactory; 5 import ction; 6 import uration; 7 8 import s; 9 import t;10 11 public class HibernateMain {12 13 public static void main(String[] args) {14 // TODO Auto-generated method stub15 Configuration cfg = new Configuration().configure();16 SessionFactory factory = essionFactory();17 Session session = ssion();18 Transaction ts = nsaction();19 ();20 21 Student st1 = new Student();22 e("学⽣甲");23 24 Student st2 = new Student();25 e("学⽣⼄");26 27 Classes cls = new Classes();28 Name("班级2");29 30 dents().add(st1);31 dents().add(st2);32 (cls);33 ();34 (0);35 }36 37 } 运⾏之后,控制台的sql语句执⾏顺序如下:Hibernate: insert into classes (name) values (?)Hibernate: insert into Student (name, cid) values (?, ?)Hibernate: insert into Student (name, cid) values (?, ?)Hibernate: update Student set cid=? where id=?Hibernate: update Student set cid=? where id=? 这个时候你会发现,本来在insert student的时候已经设置了cid,为什么,最后还会有个update操作?这是因为两边的配置默认都要维护表⽰关联关系的字段cid!之前我提过(往前翻),凡是可以设置inverse属性的地⽅(只有集合标记“set/map/list/array/bag”才有inverse属性”),如果没有设置,那么默认都是inverse="false",也就是说在操作本端对象的CRUD时,会触发维护体现关联关系字段的操作。在⽂件中有配置set节点,但是没有设置inverse属性,默认就是inverse="false",也就是本端负责维护关联关系的那个字段,⼜因为对端配置的是默认就赋予它inverse="false"的效果,所以变成两端都维护这个字段。 如果我们此时在⽂件中的set节点,配置inverse="true",也就是明确表⽰⾃⼰不参与维护体现关联关系的字段,这时候,我们再执⾏程序,控制台的sql执⾏顺序如下:Hibernate: insert into classes (name) values (?)Hibernate: insert into Student (name, cid) values (?, ?)Hibernate: insert into Student (name, cid) values (?, ?) 这时并没有update的语句!⾄此,cascade和inverse的使⽤和区别,我想我已经在上⾯讲清楚了,如果有错误或者不能理解的地⽅,请加我建⽴的群进⾏探讨~三、链接四、联系本⼈ 为⽅便没有博客园账号的读者交流,特意建⽴⼀个企鹅群(纯公益,⾮利益相关),读者如果有对博⽂不明之处,欢迎加群交流:261746360,⼩杜⽐亚-博客园

-

hibernate

发布评论

评论列表(0)

  1. 暂无评论