Hibernate一对多,多对多操做

表与表之间的关系

一对多:java

  • 分类和商品关系,一个分类里面有多个商品,一个商品只能属于一个分类。

客户和联系人是一对多关系:mysql

联系人在人际交流中担任着重要的角色,在销售过程当中,咱们一般不是在最开始就联系到有决策权的高管,而有多是接电话的前台A、营销人员B、客服C、技术人员D等等。这些人都是咱们与企业保持交流的联系人。他们对产品的支持态度决定了产品是否可以顺利推送到高管面前。web

一般销售人员在跟进一个客户时,会有一个或者多个联系人,这些联系人所处的职位决定了他们的角色。算法

  • 客户:与公司有业务往来,好比淘宝商家与淘宝就有业务往来,淘宝商家和淘宝谈判想要让本身的商品在淘宝上架,其中淘宝就是客户。
  • 联系人:为了和淘宝联系,淘宝公司里面的员工等就是联系人。

也就是说客户和联系人的关系是:
公司和公司员工的关系。sql

  • 一个公司能够有多个员工,即一个客户能够有多个联系人。
  • 一个联系人只能有一个客户,即一个员工只能有一个公司。

这里写图片描述

多对多:网络

  • 订单和商品关系,一个订单里面有多个商品,一个商品属于多个订单。
  • 老师和学生,一个老师能够教多个学生,一个学生能够被多个老师教。

这里写图片描述

一对一:session

Hibernate中的一对多操做(重点)

一对多映射配置:架构

以客户和联系人为例:客户是一,联系人是多。app

第一步:建立两个实体类,客户和联系人:dom

第二步:让两个实体类之间互相表示。

  • 让客户实体类里面表示多个联系人。

一个客户能够对应多个联系人。

这里装载联系人,装载的容器用的是Set集合,而不是LinkedList,由于Set集合的特色是,值不可以重复。

  • 在联系人实体类里面表示所属客户。

一个联系人只能属于一个客户。

//客户的实体类:
public class Customer {

    private Integer cid;
    private String custName;
    private String custLevel;
    private String custSource;
    private String custPhone;
    private String custMobile;
    private Set<LinkMan> linkMans=new HashSet<LinkMan>();


    public Set<LinkMan> getLinkMans() {
        return linkMans;
    }
    public void setLinkMans(Set<LinkMan> linkMans) {
        this.linkMans = linkMans;
    }
    public Integer getCid() {
        return cid;
    }
    public void setCid(Integer cid) {
        this.cid = cid;
    }
    public String getCustName() {
        return custName;
    }
    public void setCustName(String custName) {
        this.custName = custName;
    }
    public String getCustLevel() {
        return custLevel;
    }
    public void setCustLevel(String custLevel) {
        this.custLevel = custLevel;
    }
    public String getCustSource() {
        return custSource;
    }
    public void setCustSource(String custSource) {
        this.custSource = custSource;
    }
    public String getCustPhone() {
        return custPhone;
    }
    public void setCustPhone(String custPhone) {
        this.custPhone = custPhone;
    }
    public String getCustMobile() {
        return custMobile;
    }
    public void setCustMobile(String custMobile) {
        this.custMobile = custMobile;
    }
    public Customer(String custName, String custLevel, String custSource, String custPhone,
            String custMobile) {
        super();
        this.custName = custName;
        this.custLevel = custLevel;
        this.custSource = custSource;
        this.custPhone = custPhone;
        this.custMobile = custMobile;
    }
    public Customer() {
        super();
    }
    @Override
    public String toString() {
        return "Customer [cid=" + cid + ", custName=" + custName + ", custLevel=" + custLevel + ", custSource="
                + custSource + ", custPhone=" + custPhone + ", custMobile=" + custMobile + "]";
    }
}
//联系人实体类
public class LinkMan {
    private Integer lid;
    private String lname;
    private String lgender;
    private String lphone;
    private Customer customer;


    public Customer getCustomer() {
        return customer;
    }
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }
    public Integer getLid() {
        return lid;
    }
    public void setLid(Integer lid) {
        this.lid = lid;
    }
    public String getLname() {
        return lname;
    }
    public void setLname(String lname) {
        this.lname = lname;
    }
    public String getLgender() {
        return lgender;
    }
    public void setLgender(String lgender) {
        this.lgender = lgender;
    }
    public String getLphone() {
        return lphone;
    }
    public void setLphone(String lphone) {
        this.lphone = lphone;
    }
    public LinkMan() {
        super();
        // TODO Auto-generated constructor stub
    }
    public LinkMan(String lname, String lgender, String lphone) {
        super();
        this.lname = lname;
        this.lgender = lgender;
        this.lphone = lphone;
    }
    @Override
    public String toString() {
        return "LinkMan [lid=" + lid + ", lname=" + lname + ", lgender=" + lgender + ", lphone=" + lphone + "]";
    }
}

第三步:配置映射关系

  • 通常一个实体类对应一个映射文件
  • 把映射最基本配置完成
  • 在映射文件中,配置一对多关系(重点)

在客户的映射文件中,表示全部的联系人

<class name="类的全路径" table="表名">

    <id name="id" column="cid">//实体类属性名以及表字段名
        <generator class="native"></generator>
    </id>
    <property name="custName"></property>
    <property name="custLevel"></property>
    <property name="custSource"></property>
    <property name="custPhone"></property>
    <property name="custMobile"></property>


    <!-- 在客户映射文件中,表示全部联系人 使用set标签表示全部联系人 set标签里面有name属性:属性值写,在客户实体类里面表示联系人的set集合名称 -->

    <set name="setLinkMan">
        <!-- 一对多建表,有外键 hibernate机制:双向维护外键,在一和多两方都配置外键。 column属性值:外键名称。这里的外键名称能够随便写。 -->
        <key column="clid"></key>

        <!-- 客户全部的联系人,class里面写联系人实体类全路径 -->
        <one-to-many class="联系人实体类全路径" />
    </set>
</class>

在联系人映射文件中,表示所属客户

<class name="实体类的全路径" table="表名">
    <id name="lid"><!-- 当column不写时,名称和name的名称同样 -->
        <generator class="native"></generator>
    </id>
    <property name="lname"></property>
    <property name="lgender"></property>
    <property name="lphone"></property>

    <!-- 表示联系人所属客户 name属性:联系人实体类被装载到Customer中,这里写customer名称 class属性:customer全路径 column属性:外键名称 -->
    <many-to-one name="customer" class="customer全路径" column="clid"></many-to-one>
</class>

第四步:建立核心配置文件,把映射文件引入到核心配置文件中。

<mapping resource="cn/domarvel/entity/Customer.hbm.xml"/>
<mapping resource="cn/domarvel/entity/LinkMan.hbm.xml"/>

第五步:写测试类:

最后:运行项目,让表被建立。

能够看到结果,客户表和联系人表进行了关联,联系人引入客户表的id做为外键。

一对多级联操做

这些操做Hibernate都帮你封装好了。

级联操做

级联保存:

  • 添加一个客户,为这个客户添加了多个联系人。
//在没有进行简化前,须要将联系人和客户的关系在代码行中进行说明。
@Test
    public void showCreate(){
        Transaction transaction=null;
        try {
            Session session=HibernateUtils.getCurrentSession();
            transaction=session.beginTransaction();

            Customer customer=new Customer("百度", "vip", "网络", "110", "120");
            LinkMan linkMan=new LinkMan("侯松", "男", "123");

            customer.getLinkMans().add(linkMan);
            linkMan.setCustomer(customer);//注意在没有进行配置简化配置的时候还须要让联系人实体类和客户实体类关联。
            session.save(customer);
            session.save(linkMan);

            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
        }
}

简化操做:

在映射文件的客户方,也就是一对多的一这方进行配置:

<set name="linkMans" cascade="save-update"><!-- 这里配置了一个属性cascade,值为save-update,表示对保存和更新操做,作了简化,不用对多的那方 设置和一的那方的关系。 -->
    <key column="clid"></key>
    <one-to-many class="cn.domarvel.entity.LinkMan"/>
</set>
//简化后代码

    @Test
    public void showCreate02(){
        Transaction transaction=null;
        try {
            Session session=HibernateUtils.getCurrentSession();
            transaction=session.beginTransaction();

            Customer customer=new Customer("腾讯", "svip", "网络", "110", "120");
            LinkMan linkMan=new LinkMan("FireLang", "女", "123");

            customer.getLinkMans().add(linkMan);//注意在这里就只须要把联系人(多的那方)放入到客户方(一的那方),就能够了。
            session.save(customer);//最后保存的时候就只须要保存客户方(一的那方)

            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
        }
    }

级联删除:

  • 删除某一个客户,这个客户里面的全部的联系人也删除。

这里就直接上简化后的代码了。

<!-- 配置映射文件 -->

<!-- 在一的那方配置删除的简化,当要想直接删除一的那方,Hibernate自动帮你删除相关联的多的那方 -->

<set name="linkMans" cascade="save-update,delete"><!-- 在cascade里面配置delete,多个值用","分割 -->
    <key column="clid"></key>
    <one-to-many class="cn.domarvel.entity.LinkMan"/>
</set>
//测试代码
    @Test
    public void showDelete(){
        //在之前咱们要删除一的那方,是先根据一的那方删除多的那方,再删除一的那方。
        //如今Hibernate已经帮咱们封装好了,咱们只须要配置好,再直接删除一的那方就好了。

        Transaction transaction=null;
        try {
            Session session=HibernateUtils.getCurrentSession();
            transaction=session.beginTransaction();

            Customer customer=session.get(Customer.class, 2);
            session.delete(customer);

            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
        }

    }

级联修改:

操做:如今是让一个联系人的客户变成另一个。咱们须要修改的就是某一个员工的外键值。

咱们的普通更新操做:

@Test
    public void showUpdate(){
        //下面经过一个例子引入Hibernate中做更新操做时可以使用的一个关键字,目的是可以简化更新操做

        Transaction transaction=null;
        try {
            Session session=HibernateUtils.getCurrentSession();
            transaction=session.beginTransaction();

            Customer customer=session.get(Customer.class, 1);
            LinkMan linkMan=session.get(LinkMan.class, 3);

            linkMan.setCustomer(customer);//由于这里的数据是持久态的因此咱们直接设置值后,Hibernate会帮咱们自动更新数据。
            customer.getLinkMans().add(linkMan);

            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
        }
    }
//经过输出底层sql代码能够知道

Hibernate: 
    select
        customer0_.cid as cid1_1_0_,
        customer0_.custName as custName2_1_0_,
        customer0_.custLevel as custLeve3_1_0_,
        customer0_.custSource as custSour4_1_0_,
        customer0_.custPhone as custPhon5_1_0_,
        customer0_.custMobile as custMobi6_1_0_ 
    from
        customer customer0_ 
    where
        customer0_.cid=?
Hibernate: 
    select
        linkman0_.lid as lid1_2_0_,
        linkman0_.lname as lname2_2_0_,
        linkman0_.lgender as lgender3_2_0_,
        linkman0_.lphone as lphone4_2_0_,
        linkman0_.clid as clid5_2_0_ 
    from
        linkman linkman0_ 
    where
        linkman0_.lid=?
Hibernate: 
    select
        linkmans0_.clid as clid5_2_0_,
        linkmans0_.lid as lid1_2_0_,
        linkmans0_.lid as lid1_2_1_,
        linkmans0_.lname as lname2_2_1_,
        linkmans0_.lgender as lgender3_2_1_,
        linkmans0_.lphone as lphone4_2_1_,
        linkmans0_.clid as clid5_2_1_ 
    from
        linkman linkmans0_ 
    where
        linkmans0_.clid=?
Hibernate: 
    update
        linkman 
    set
        lname=?,
        lgender=?,
        lphone=?,
        clid=? 
    where
        lid=?
Hibernate: 
    update
        linkman 
    set
        clid=? 
    where
        lid=?

//在这里进行了重复的修改外键的操做,修改两次的缘由是Hibernate双向维护外键。

inverse属性

  • 在一对多里面,当咱们更新一对多的关系的时候,更新联系人所属的客户的时候。也就是上面的代码。咱们设置linkMan.setCustomer(customer);。达到了更新联系人所属关系。可是从上面的sql语句输出的状况能够看出。有两次的update操做。这两次是怎么来的呢???一次是咱们本身写的。linkMan.setCustomer(customer);另一次是Hibernate帮咱们修改外键关系的。因此这里就重复了update语句。使得修改关系时效率下降了。(注意:当咱们修改多的那方的时候,不是修改的所属关系,而是其它的数据,那么就不会有两次更新。)那么咱们该怎么解决更新两次的问题呢???那这里就得靠inverse属性了。

解决方式:让其中的一方不维护外键。

  • 一对多里面,让其中一方放弃外键维护。
  • 一个国家有总统,国家有不少人,总统不可能认识国家全部人,国家全部人能够认识总统。
  • 即让一的一方不维护外键。

具体实现:

在放弃关系维护映射文件中,进行配置,在set标签上使用inverse属性。(inverse:反向)

inverse:

  • 默认值为false;
  • true表示放弃关系维护
  • false表示不放弃关系维护
  • 目的:提升性能问题。

//在这里不须要对java代码进行修改,只须要进行配置就能够了。这里仍是用的是上面的问题代码。

//在一的一方进行配置,目的,不让一的一方反向维护
<set name="linkMans" cascade="save-update,delete" inverse="true">
    <key column="clid"></key>
    <one-to-many class="cn.domarvel.entity.LinkMan"/>
</set>

再次做关系更新操做,看一下运行结果:

Hibernate: 
    select customer0_.cid as cid1_1_0_, customer0_.custName as custName2_1_0_, customer0_.custLevel as custLeve3_1_0_, customer0_.custSource as custSour4_1_0_, customer0_.custPhone as custPhon5_1_0_, customer0_.custMobile as custMobi6_1_0_ from customer customer0_ where customer0_.cid=? Hibernate: select linkman0_.lid as lid1_2_0_, linkman0_.lname as lname2_2_0_, linkman0_.lgender as lgender3_2_0_, linkman0_.lphone as lphone4_2_0_, linkman0_.clid as clid5_2_0_ from linkman linkman0_ where linkman0_.lid=? Hibernate: select linkmans0_.clid as clid5_2_0_, linkmans0_.lid as lid1_2_0_, linkmans0_.lid as lid1_2_1_, linkmans0_.lname as lname2_2_1_, linkmans0_.lgender as lgender3_2_1_, linkmans0_.lphone as lphone4_2_1_, linkmans0_.clid as clid5_2_1_ from linkman linkmans0_ where linkmans0_.clid=? Hibernate: update linkman set lname=?, lgender=?, lphone=?, clid=? where lid=? //能够看到这里update只更新了一次 //这里要注意:咱们设置inverse=true,只是解决对更新所属关系时带来的效率问题。

这里有篇文章你们必定要看看(inverse和cascade的区别,以及一对多添加操做时外键为Null的问题)

Hibernate多对多操做

多对多映射配置

  • 以用户和角色为例

第一步:建立实体类,用户和角色。
第二步:让两个实体类之间互相表示。

  • 一个用户里面表示全部角色,使用set集合
public class User {
    private Integer uid;
    private String uname;
    private String pword;
    private Set<Role> roleSet=new HashSet<Role>();

    public Integer getUid() {
        return uid;
    }
    public void setUid(Integer uid) {
        this.uid = uid;
    }
    public String getUname() {
        return uname;
    }
    public void setUname(String uname) {
        this.uname = uname;
    }
    public String getPword() {
        return pword;
    }
    public void setPword(String pword) {
        this.pword = pword;
    }
    public Set<Role> getRoleSet() {
        return roleSet;
    }
    public void setRoleSet(Set<Role> roleSet) {
        this.roleSet = roleSet;
    }
    @Override
    public String toString() {
        return "User [uid=" + uid + ", uname=" + uname + ", pword=" + pword + ", roleSet=" + roleSet + "]";
    }
    public User() {
        super();
        // TODO Auto-generated constructor stub
    }
    public User( String uname, String pword, Role role) {
        super();
        this.uname = uname;
        this.pword = pword;
        roleSet.add(role);
    }
}
  • 一个角色有多个用户,使用set集合
public class Role {
    private Integer rid;
    private String rname;
    private String rmemo;
    private Set<Role> userSet=new HashSet<Role>();


    public Set<Role> getUserSet() {
        return userSet;
    }
    public void setUserSet(Set<Role> userSet) {
        this.userSet = userSet;
    }
    public Integer getRid() {
        return rid;
    }
    public void setRid(Integer rid) {
        this.rid = rid;
    }
    public String getRname() {
        return rname;
    }
    public void setRname(String rname) {
        this.rname = rname;
    }
    public String getRmemo() {
        return rmemo;
    }
    public void setRmemo(String rmemo) {
        this.rmemo = rmemo;
    }
    @Override
    public String toString() {
        return "Role [rid=" + rid + ", rname=" + rname + ", rmemo=" + rmemo + "]";
    }
    public Role(String rname, String rmemo) {
        super();
        this.rname = rname;
        this.rmemo = rmemo;
    }
    public Role() {
        super();
        // TODO Auto-generated constructor stub
    }
}

第三步:配置映射关系

  • 基本配置
  • 配置多对多关系

  • 在用户里面表示全部角色,使用set标签

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.domarvel.manytomany.Userb" table="userb">
        <!-- table是要生成的表名 -->
        <id name="uid">
            <generator class="native"></generator>
        </id>
        <property name="uname"/>
        <property name="pword"/>
        <!-- property里面的column属性能够不用写,不写就默认和property里面的name属性值相同 -->

        <set name="roleSet" table="user_role" cascade="save-update">
         <!-- 在用户里面表示角色,使用set标签 name属性:角色set集合名称 table属性:第三张表名称 -->
         <!-- 注意若是尚未在其它映射文件中配置表名,那么第一次配置就能够乱写, 可是若是有一张表已经声明说表名叫什么了,那就不可以再乱写 -->
            <key column="fuid"/>
            <!-- key标签里面配置: 配置当前映射文件在第三张表外键名称 -->

            <many-to-many class="cn.domarvel.manytomany.Role" column="frid"></many-to-many>
            <!-- class:角色实体类全路径 column:角色在第三张表外键名称 -->
        </set>
    </class>
</hibernate-mapping>
  • 在角色里面表示全部用户,使用set标签
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.domarvel.entity.Role" table="role">
        <id name="rid">
            <generator class="native"></generator>
        </id>
        <property name="rname"/>
        <property name="rmemo"/>

        <set name="userSet" table="user_role">
            <key column="frid"></key>
            <many-to-many class="cn.domarvel.entity.User" column="fuid"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

这里写图片描述

第四步:在核心配置文件中引入映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///hibernate</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password"></property>

        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>

        <property name="hibernate.hbm2ddl.auto">update</property>


        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.current_session_context_class">thread</property>

        <mapping resource="cn/domarvel/entity/Role.hbm.xml"/>
        <mapping resource="cn/domarvel/entity/User.hbm.xml"/><!-- 引入映射配置文件到核心配置文件 -->
    </session-factory>
</hibernate-configuration>

多对多级联保存

根据用户保存角色:

第一步:在用户配置文件中set标签进行配置,cascade值save-update,意思是对级联操做进行了简化,好比说:当我一对多的时候,咱们之前是先删除多的那方再删除一的那方,或者先保存多的那方再保存一的那方。可是如今不用了若是咱们设置了save-update或者delete,那咱们只须要删除一的那方或者保存一的那方就好了,但前提是必需要先把多的那方保存到一的那方里面才可使用。这也就是简化操做。

<set name="roleSet" table="user_role" cascade="save-update"><!-- 注意cascade是配置到哪儿里后,在保存的时候就保存哪儿一个被配置的实体。而且 另一个实体就保存到被配置的实体里面。这里配置的是User因此就是根据User保存角色 -->

第二步:写代码实现

  • 建立用户和角色对象,把角色放到用户里面,最终保存用户就能够了。
@Test
    public void showAddManyToM(){
        Transaction transaction=null;
        try {
            Session session=HibernateUtils.getCurrentSession();
            transaction=session.beginTransaction();

            User user=new User("LangSheng", "123456",new Role("开发工程师", "专一于网页先后台开发,和图像算法!!"));
            user.getRoleSet().add(new Role("架构师", "进行程序开发架构!"));
            User user2=new User("FireLang", "456123", new Role("狼神总部副总裁", "国际知名企业!!"));
            user2.getRoleSet().add(new Role("图像算法师", "经过代码对程序的算法处理!以及识别!!"));   

            session.save(user);
            session.save(user2);
            transaction.commit();
        } catch (Exception e) {
            e.printStackTrace();
            transaction.rollback();
        }
    }

多对多级联删除

对于级联删除

在完成上面级联删除的操做后,对于级联删除就变得很简单了。

第一步:在set标签中配置cascade属性,值为delete;若是有多个值记得用”,”号隔开,好比cascade=”save-update,delete”;

第二步:直接进行删除。

对于级联删除,在咱们一对多的时候还能够用,也挺方便的。可是在咱们的多对多的里面就显得有些鸡肋了,由于,假如咱们删除用户表里面的某个用户,那么就会把该用户和角色表里面属于该用户角色属性的角色以及第三张表涉及到该用户的都会删掉。而咱们通常角色是不可以删除的,由于有可能其它用户还有该角色,若是你把别人的角色删掉了,那么其它用户和该角色在第三张表中的关系也会被删掉的。

因此咱们不推荐使用级联删除来操做多对多关系。

咱们应该作的是维护第三张表,也就是用户和角色的主键组成的一个外建表。

维护第三张表关系

用户和角色多对多关系,维护关系经过第三张表维护

让某个用户有某个角色

第一步:根据id查询用户和角色

第二步:把角色放到用户里面

  • 把角色对象放到用户set集合
@Test
    public void showCreateRelation(){
        Transaction transaction=null;
        try {
            Session session=HibernateUtils.getCurrentSession();
            transaction=session.beginTransaction();

            User user=session.get(User.class, 4);
            Role role=session.get(Role.class, 6);
            user.getRoleSet().add(role);

            transaction.commit();
        } catch (Exception e) {
            e.printStackTrace();
            transaction.rollback();
        }
    }

让某个用户没有某个角色

@Test
    public void showRemoveReation(){
        Transaction transaction=null;
        try {
            Session session=HibernateUtils.getCurrentSession();
            transaction=session.beginTransaction();

            User user=session.get(User.class, 4);
            Role role=session.get(Role.class, 6);
            user.getRoleSet().remove(role);

            transaction.commit();
        } catch (Exception e) {
            e.printStackTrace();
            transaction.rollback();
        }
    }