分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 教程案例

Hibernate-ORM:11.Hibernate中的关联查询

发布时间:2023-09-06 01:57责任编辑:熊小新关键词: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

如果有错误请您指出,感激不尽!!!!!!!!!!!!!!!!

Hibernate-ORM:11.Hibernate中的关联查询

原文地址:https://www.cnblogs.com/DawnCHENXI/p/9123184.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved