本文来是从 java web轻量级开发面试教程从摘录的。
Inverse的英文含义是反转,在Hibernate中用来决定是由哪方来维护两个业务实体类之间的关联关系,具体而言,就是由哪方去设置这个被外键约束的字段值。
它的默认值是false,也就是说,本端(比如inverse=false写在学生端,那么本端是学生,另外一方是课程)不“反转控制权”,这句别扭的话的另外一种说法是,本端维护关联关系。如果两边都不写,那么两端都维护。这样会造成问题,即新时因为两端都控制关系,因此可能会导致重复更新。
注意,inverse仅仅是指定由谁来设置外键值,而不是用来设置级联操作,级联操作的方式由cascade来负责,很多人会混淆它们的含义和用法。
这个例子所用到的数据表是Person表和Card表,其中Person表里包含ID、Name和Phone字段,而Card表里包含了CardID、PersonID、Bank和balance四个字段。
在hibernate.cfg.xml文件里,通过mapping resource来指定对应的映射文件,其中关键的代码如下:
1 2 3 | 1 <!-- 添加实体类的映射文件--> 2 <mapping resource= "Model/Card.hbm.xml" /> 3 <mapping resource= "Model/Person.hbm.xml" /> |
在Person.hbm.xml里,用如下代码来指定人(一方)和卡(多方)的关系,其中在人这一端,inverse是true。
1 2 3 4 | 1 <set name= "cards" cascade= "save-update" inverse= "true" > 2 <key column= "PersonID" /> 3 <one-to-many class = "Card" /> 4 </set> |
在HibernateMain.java里,通过如下关键代码插入了一个人的信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 1 //创建一张卡 2 Card card1 = new Card(); 3 card1.setCardID( "Card123" ); 4 card1.setPersonID( "Person123" ); 5 card1.setBank( "Citi" ); 6 card1.setBalance(100f); 7 //初始化一个人 8 Person person = new Person(); 9 person.setID( "Person123" ); 10 person.setName( "Peter" ); 11 person.setPhone( "123456" ); 12 //在cards这个set里加入card1 13 Set<Card> cards = new HashSet<Card>(); 14 cards.add(card1); 15 person.setCards(cards); 16 //保存人的信息 17 session.save(person); 18 session.flush(); |
运行后,在输出信息里能看到如下两条相关的插入语句:一条是插入Person信息,另一条是插入card信息。
1 Hibernate: insert into Person (Name, Phone, ID) values (?, ?, ?)
2 Hibernate: insert into Card (PersonID, Bank, Balance, Person, CardID) values (?, ?, ?, ?, ?)
当把person.hbm.xml里的inverse设置成false时,能看到相关的语句里会多出一句update语句。
1 Hibernate: insert into Person (Name, Phone, ID) values (?, ?, ?)
2 Hibernate: insert into Card (PersonID, Bank, Balance, Person, CardID) values (?, ?, ?, ?, ?)
3 Hibernate: update Card set PersonID=? where CardID=?
原因是,当设置inverse为true时,Person这一端反转外键控制权,也就是由Card这端来管理外键,而在代码里我们仅仅是插入了Person,没有插入Card,所以就没有更新两个外键(PersonID和CardID)的操作。相反,当inverse为false时,管理外键控制权是在Person端,那么当插入Person时,Hibernate就需要额外的一句update语句来管理外键了。
在一对多的例子里,inverse不论取什么值,对结果都没有影响,所以很容易让人忽视它的作用。
在一对多的例子里,一般是让多方来管理外键控制权,比如一个人有100张开,那么如果由Person方来管理的话,无形中可能会多出100个update操作,效率上就不大好了。
如果在一对多案例中,inverse只是影响效率的话,那么在多对多的例子中,inverse的设置就可能影响到数据。
我们再来看个多对多的学生选课案例,其中一个学生可以选多门课,而一门课里可以有多个学生。在Student.hbm.xml里,描述多对多关系的语句里可加上inverse=“true”的语句。
1 2 3 4 | 1 <set name= "courses" table= "students_courses" inverse= "true" cascade= "save-update" > 2 <key column= "student_id" ></key> 3 <many-to-many class = "Model.Course" column= "course_id" ></many-to-many> 4 </set> |
在Course.hbm.xml里,不加任何关于inverse的语句,也就是说,在Student端反转外键控制权,把控制权交到Course端。
在HibernateMain这个类里,通过如下代码让s1学生选修计算机课程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 1 //设置一个学生,学号是1 2 Student s1 = new Student(); 3 s1.setStudentID( "1" ); 4 s1.setStudentName( "Peter" ); 5 //设置多个课程 6 Course c1 = new Course(); 7 c1.setCourseID( "c1" ); 8 c1.setCourseName( "Math" ); 9 Course c2 = new Course(); 10 c2.setCourseID( "c2" ); 11 c2.setCourseName( "Java" ); 12 Course c3 = new Course(); 13 c3.setCourseID( "c3" ); 14 c3.setCourseName( "C#" ); 15 //设置计算机课程这个Set 16 Set<Course> computerCourses = new HashSet<Course>(); 17 computerCourses.add(c2); 18 computerCourses.add(c3); 19 //让s1这个学生选修计算机课程(也就是c2和c3课程) 20 s1.setCourses(computerCourses); 21 //保存s1 22 session.save(s1); 23 session.flush(); |
执行结果是,虽然能在Student和Course表里看到相关的学生和课程的记录,但在关键的描述学生选课关联表(students_courses)里,看不到任何关联记录。原因是已经通过设置inverse把外键管理权交给Course方了,这里仅仅是保存学生,并没有保存课程,所以没有插入外键的动作。如果要在students_courses表里插入外键关联,就需要在person.hbm.xml里设置inverse的值为false。
所以,在多对多关联里,设置错了inverse值会导致结果出错,请大家根据具体项目的情况适当设值。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?