1. 程式人生 > >Hibernate-ORM:11.Hibernate中的關聯查詢

Hibernate-ORM:11.Hibernate中的關聯查詢

雙向 create oracle 下一條 關系 配置文件 href 並且 commit

------------吾亦無他,唯手熟爾,謙卑若愚,好學若饑-------------

本篇博客將講述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中的關聯查詢