Hibernate-ORM:11.Hibernate中的关联查询
————吾亦无他,唯手熟尔,谦卑若愚,好学若饥————-
本篇博客将讲述Hibernate中的关联查询,及其级联(cascade)操作,以及指定哪一方维护关联关系的(inverse)
一,讲述目录如下:
1.单向一对多:(增加一个区县及其它以下的对应街道)
2.单项一对多:(查询一个区县,以及它下面所有的对应街道)
3.单项多对一:(查询一个指定的街道,并同时展示出其对应的区县)
4.双向一对多(多对一):(值得注意:toString()套路不对容易引发错误Error——StackOverflowError)
5.inverse的使用和详解:(指定哪一方来维护关联关系)
6.级联删除:(删除主表中的数据,同时将会干掉外键是它的数据,慎用!!!!!!)
注:其实还想讲一下多对多,但是没有准备,下篇博客会详细总结;
二,单向一对多:(增加一个区县及其它以下的对应街道)
1.简介:
我会在此案例下做好全部准备工作,其他的案例会在此案例上做略改操作:
2.实体类的准备:
2.1:District区县实体类:
package cn.dawn.day03.entity; import java.util.ArrayList; import java.util.List; /** * Created by Dawn on 2018/5/30. */ /*区县*/ public class District { private Integer id;//区县id private String name;//区县名称 /*一个区县对应多个街道*/ private List<Street> streets=new ArrayList<Street>(); @Override public String toString() { return "District{" + "id=" + id + ", name=\'" + name + \'\\'\' + ", streets=" + streets + \'}\'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Street> getStreets() { return streets; } public void setStreets(List<Street> streets) { this.streets = streets; } public District() { } public District(Integer id, String name, List<Street> streets) { this.id = id; this.name = name; this.streets = streets; } public District(Integer id, String name) { this.id = id; this.name = name; } }
2.2:Street街道类:
package cn.dawn.day03.entity; /** * Created by Dawn on 2018/5/30. */ /*街道类*/ public class Street { private Integer id;//街道id private String name;//街道名称 private District district; @Override public String toString() { return "Street{" + "id=" + id + ", name=\'" + name + \'\\'\' + ", district=" + district + \'}\'; } public Street() { } public Street(Integer id, String name) { this.id = id; this.name = name; } public District getDistrict() { return district; } public void setDistrict(District district) { this.district = district; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
3.hbm文件的准备:
3.1:District.hbm.xml文件的配置:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.dawn.day03.entity"> <!--如果上面指定package的话,class的name就不必写全类名--> <!--lazy:是否懒加载(延迟加载) 默认值是true,延迟加载--> <!--<class name="Teacher">--> <!--直接加载--> <class name="District" lazy="false"> <!--主键--> <id name="id" column="id"> <!--主键生成策咯 assigned程序员自己创建--> <!--identity是mysql里的自增,一会做增加操作不必再给主键赋值--> <!--increment是先查最大的主键列,在下一条给主键加一--> <!--sequence是oracle的主键生成策咯,他一会需要指定序列名字<param name="sequence">序列名</param>--> <generator class="assigned"></generator> </id> <property name="name" column="name"></property> <!-- 配置一对多的关联管理 name:关联关系属性名 column:数据库中对应的外键 class:关联关系的类型 cascade:对当前对象操作的时候,是否影响关联对象 inverse="true": 放弃与数据库的交互 --> <bag name="streets" cascade="save-update" > <key column="districtid"></key> <one-to-many class="Street"></one-to-many> </bag> </class> </hibernate-mapping>
3.2:Street.hbm.xml文件配置:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.dawn.day03.entity"> <!--如果上面指定package的话,class的name就不必写全类名--> <!--lazy:是否懒加载(延迟加载) 默认值是true,延迟加载--> <!--<class name="Teacher">--> <!--直接加载--> <class name="Street" lazy="false"> <!--主键--> <id name="id" column="id"> <!--主键生成策咯 assigned程序员自己创建--> <!--identity是mysql里的自增,一会做增加操作不必再给主键赋值--> <!--increment是先查最大的主键列,在下一条给主键加一--> <!--sequence是oracle的主键生成策咯,他一会需要指定序列名字<param name="sequence">序列名</param>--> <generator class="assigned"></generator> </id> <property name="name" column="name"></property> <!--配置多对一的关联关系 name: 本类中 关联关系的属性名 class:关联关系的类型 column:在数据库中两个表的外键 --> <!--<many-to-one name="district" class="District" column="districtId"/>--> </class> </hibernate-mapping>
4.单测方法,增加一个区县及其它以下的对应街道:
@Test /** * 单项一对多,增加一个区县及其他下面的对应街道 * 千万注意,在hbm中要加:级联 * cascade="save-update" */ public void t1OneToMany(){ /*创建三个街道*/ Street street1=new Street(1,"海淀1街"); Street street2=new Street(2,"海淀2街"); Street street3=new Street(3,"海淀3街"); List<Street> streets=new ArrayList<Street>(); streets.add(street1); streets.add(street2); streets.add(street3); /*添加一个区到数据库*/ District district=new District(1,"测试海淀区"); /*也可以从数据库中获取一个区,推荐第二种*/ /*District district = session.get(District.class, 1);*/ district.setStreets(streets); /*新增街道*/ session.save(district); tr.commit(); /*执行结果 Hibernate: create table District (id integer not null, name varchar(255), primary key (id)) engine=MyISAM Hibernate: create table Street (id integer not null, name varchar(255), districtid integer, primary key (id)) engine=MyISAM Hibernate: alter table Street add constraint FKjuba2team1j6124qeurjyblk1 foreign key (districtid) references District (id) Hibernate: select street_.id, street_.name as name2_1_ from Street street_ where street_.id=? Hibernate: select street_.id, street_.name as name2_1_ from Street street_ where street_.id=? Hibernate: select street_.id, street_.name as name2_1_ from Street street_ where street_.id=? Hibernate: insert into District (name, id) values (?, ?) Hibernate: insert into Street (name, id) values (?, ?) Hibernate: insert into Street (name, id) values (?, ?) Hibernate: insert into Street (name, id) values (?, ?) Hibernate: update Street set districtid=? where id=? Hibernate: update Street set districtid=? where id=? Hibernate: update Street set districtid=? where id=? */ }
三,单项一对多:(查询一个区县,以及它下面所有的对应街道)
1.在原有代码不改变的情况下,做如下操作:
2.查询一个区县,以及它下面所有的对应街道:
@Test /*查询指定区县下所有的街道*/ public void t2selectStreetByDistrict(){ /*获取id为1的区县*/ District district=session.get(District.class,1); /*获取区县下所有的街道*/ List<Street> streets = district.getStreets(); for (Street street:streets) { System.out.println(street); } /*运行结果 Hibernate: alter table Street add constraint FKjuba2team1j6124qeurjyblk1 foreign key (districtid) references District (id) Hibernate: select district0_.id as id1_0_0_, district0_.name as name2_0_0_ from District district0_ where district0_.id=? Hibernate: select streets0_.districtid as district3_1_0_, streets0_.id as id1_1_0_, streets0_.id as id1_1_1_, streets0_.name as name2_1_1_ from Street streets0_ where streets0_.districtid=? Street{id=1, name=\'海淀1街\'} Street{id=2, name=\'海淀2街\'} Street{id=3, name=\'海淀3街\'} * */ }
四,单项多对一:(查询一个指定的街道,并同时展示出其对应的区县)
1.先把二.3.3.1中District.hbm.xml把 <bag节点注释掉,或者直接干掉>
<!--<bag name="streets" cascade="save-update" > <key column="districtid"></key> <one-to-many class="Street"></one-to-many> </bag>-->
2.其实应该再多的一方,植入一个少的一方的对象,但是我在准备工作中就已经做过了
/*街道类*/ public class Street { private Integer id;//街道id private String name;//街道名称 /*重点:::::::::*/ private District district;
3.在Street.hbm.xml配置文件中,把我注释掉的那个<many-to-one>的节点放开:
<!--配置多对一的关联关系 name: 本类中 关联关系的属性名 class:关联关系的类型 column:在数据库中两个表的外键 --> <many-to-one name="district" class="District" column="districtId"/>
4.书写测试方法:查询一个指定的街道,并同时展示出其对应的区县:
@Test /*查询指定街道所对应的县区*/ /*多对一*/ /*先把之前配置的那个干掉*/ public void t3ManyToOne(){ /*需要在多的一方的实体类加入少的一方对象,并且在hbm配置manytoone*/ Street street = session.get(Street.class, 1); System.out.println(street); /* Hibernate: alter table Street add constraint FKfch89j84iailpfqs5bpkj2rs0 foreign key (districtId) references District (id) Hibernate: select street0_.id as id1_1_0_, street0_.name as name2_1_0_, street0_.districtId as district3_1_0_ from Street street0_ where street0_.id=? Hibernate: select district0_.id as id1_0_0_, district0_.name as name2_0_0_ from District district0_ where district0_.id=? Street{id=1, name=\'海淀1街\', district=District{id=1, name=\'测试海淀区\', streets=[]}} */ }
五,双向一对多(多对一):(值得注意:toString()套路不对容易引发错误Error——StackOverflowError)
1.解释:
他们的的关系是相互的,都要参与维护关联关系
2.讲四中注释掉District.hbm.xml中的<bag>标签再放开
<bag name="streets" cascade="save-update" > <key column="districtid"></key> <one-to-many class="Street"></one-to-many> </bag>
3.值得注意的是,需要修改toString()方法,否者他会互相调用,出现堆栈溢出的错误
4.先不改toString()的时候,书写测试方法:(查询一个街道及其对应的区县)
@Test /*双向的一对多,就是关系是相互的!!!!!!!!*/ /*将t3干掉的配置再加回去*/ public void t4twoway(){ Street street = session.get(Street.class, 1); System.out.println(street); /*注:toString需要做个手段,否则互相调用的话就死循环,堆栈溢出java.lang.StackOverflowError*/ /* Hibernate: alter table Street add constraint FKfch89j84iailpfqs5bpkj2rs0 foreign key (districtId) references District (id) Hibernate: select street0_.id as id1_1_0_, street0_.name as name2_1_0_, street0_.districtId as district3_1_0_ from Street street0_ where street0_.id=? Hibernate: select district0_.id as id1_0_0_, district0_.name as name2_0_0_ from District district0_ where district0_.id=? Hibernate: select streets0_.districtid as district3_1_0_, streets0_.id as id1_1_0_, streets0_.id as id1_1_1_, streets0_.name as name2_1_1_, streets0_.districtId as district3_1_1_ from Street streets0_ where streets0_.districtid=? java.lang.StackOverflowError ...... * */ }
5.修改任意一toString()方法,使其不再互相无节制的调用:
/*区县*/ public class District { private Integer id;//区县id private String name;//区县名称 /*一个区县对应多个街道*/ private List<Street> streets=new ArrayList<Street>(); @Override public String toString() { return "District{" + "id=" + id + ", name=\'" + name + \'\\'\' + ", streets=" + streets.size() + \'}\'; }
6.重新执行4中的代码,发现不会再出现错误,成功查询
六,inverse的使用和详解:(指定哪一方来维护关联关系)
1.讲解:
(1).Hibernate中的规则是,多的一方必须维护关联关系,所以,我们只能修改一的一方(one-to-many)
(2).inverse默认值为false,就是与数据库交互,会维护关联关系,如果为了性能的提升,我们可以放弃一的一方与数据库交互,即放弃维护关联关系,将值改为true即可;
2.District.hbm.xml中的配置修改的如下:
<!-- 配置一对多的关联管理 name:关联关系属性名 column:数据库中对应的外键 class:关联关系的类型 cascade:对当前对象操作的时候,是否影响关联对象 inverse="true": 放弃与数据库的交互 --> <bag name="streets" cascade="save-update" inverse="false"> <key column="districtid"></key> <one-to-many class="Street"></one-to-many> </bag>
3.单测方法(先测试一的一方维护关联关系):
@Test public void t5inverse(){ //创建区县 District district=new District(2,"大兴区"); //创建街道 Street street1=new Street(4,"大兴3街道"); Street street2=new Street(5,"大兴4街道"); //把街道放进区县的街道集合中 district.getStreets().add(street1); district.getStreets().add(street2); //新增区县 session.save(district); tr.commit(); /** * 产生了8条sql语句! 发现 后面两条update 是无用功!!!! Hibernate: alter table Street add constraint FKfch89j84iailpfqs5bpkj2rs0 foreign key (districtId) references District (id) Hibernate: select street_.id, street_.name as name2_1_, street_.districtId as district3_1_ from Street street_ where street_.id=? Hibernate: select street_.id, street_.name as name2_1_, street_.districtId as district3_1_ from Street street_ where street_.id=? Hibernate: insert into District (name, id) values (?, ?) Hibernate: insert into Street (name, districtId, id) values (?, ?, ?) Hibernate: insert into Street (name, districtId, id) values (?, ?, ?) Hibernate: update Street set districtid=? where id=? Hibernate: update Street set districtid=? where id=? 3条insert是 District产生的! 为什么会产生两条update?? Street! hibernate中 规定: 01.多的一方 many-to-one,必须去维护双方的关系! 因为many-to-one压根就没有inverse这个属性! 02.inverse默认为false! 不反转! 我来维护! 03.必须在一的一方 设置 inverse="true" 放弃维护的权力! 维护===》是否与数据库产生交互! */ }
4.将inverse改为true,放弃维护的权力,再执行刚才的代码,发现:
/*修改inverse=true,删除刚才添加的数据,再次执行单测*/ /* Hibernate: alter table Street add constraint FKfch89j84iailpfqs5bpkj2rs0 foreign key (districtId) references District (id) Hibernate: select street_.id, street_.name as name2_1_, street_.districtId as district3_1_ from Street street_ where street_.id=? Hibernate: select street_.id, street_.name as name2_1_, street_.districtId as district3_1_ from Street street_ where street_.id=? Hibernate: insert into District (name, id) values (?, ?) Hibernate: insert into Street (name, districtId, id) values (?, ?, ?) Hibernate: insert into Street (name, districtId, id) values (?, ?, ?) 发现后面的update没有了 */
七,级联删除(慎用慎用慎用!!!!!!!!!!!!!!!!!!!!!!!)
1.讲述:
如果设置完毕后,当干掉主键所在的那张表中的一台记录,它所关联的外键的那一行记录也会被干掉,所以说很危险,不要使用!!
2.设置District.hbm.xml中的cascade=”delete”和inverse=”false”(或者inverse不配置)
<bag name="streets" cascade="delete" inverse="false"> <key column="districtid"></key> <one-to-many class="Street"></one-to-many> </bag>
3.书写测试代码,比如删一个区县:
@Test /*级联删除*/ /*cascade="delete" inverse="false"*/ public void t6deleteMapping(){ /*cascade="delete" inverse="false"记得改这个参数*/ /*获取id为2的区县*/ District district=session.get(District.class,2); /*删除*/ session.delete(district); tr.commit(); /* Hibernate: alter table Street add constraint FKfch89j84iailpfqs5bpkj2rs0 foreign key (districtId) references District (id) Hibernate: select district0_.id as id1_0_0_, district0_.name as name2_0_0_ from District district0_ where district0_.id=? Hibernate: select streets0_.districtid as district3_1_0_, streets0_.id as id1_1_0_, streets0_.id as id1_1_1_, streets0_.name as name2_1_1_, streets0_.districtId as district3_1_1_ from Street streets0_ where streets0_.districtid=? Hibernate: update Street set districtid=null where districtid=? Hibernate: delete from Street where id=? Hibernate: delete from Street where id=? Hibernate: delete from District where id=? * */ /*他会把俩张表的都干掉了,所以,慎用!!!!!!!!!!!!!*/ }
作者:晨曦Dawn
转载请注明出处:https://www.cnblogs.com/DawnCHENXI/p/9123184.html
如果有错误请您指出,感激不尽!!!!!!!!!!!!!!!!