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

Posted 晨曦Dawn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

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

以上是关于Hibernate-ORM:11.Hibernate中的关联查询的主要内容,如果未能解决你的问题,请参考以下文章