Hibernate学习笔记(3)

nibiru 2019年04月30日 44次浏览

前言

这几天主要学习了表与表之间的关系,目测五一假期期间就能把Hibernate框架学完啦!

表关系的分析

首先,我们都知道Hibernate框架实现了ORM思想,将关系数据库中标的数据映射成对象,这样我们对数据库的操作就转化成了对对象的操作

数据库中表与表之间存在三种关系,也就是系统设计中的三种实体关系。
hibernate3-1
在数据库中,实体表之间的关系映射是用外键来描述的。

表与表之间的三种关系

一对多

理解:一个学生只属于一个班,但一个班里面有多名学生

建表原则:在多的一方创建外键指向一的一方的主键。(通过外键建立关系)

多对多

理解:一个学生可以选择多门课,一门课也有多名学生。

建表原则:创建一个中间表,中间表中至少两个字段作为外键分别指向多对多双方的主键

一对一

理解:一个学生对应一个学生档案材料,或者每个人都有唯一的身份证编号。

建表原则:主键对应,一方的主键作为另一个的主键

利用Java对象关系来描述数据表之间的关系

hibernate3-2

分析

一对一:就是在本类中定义对方类型的对象,在A中定义B类类型的属性,B类中定义A类类型的属性。

一对多:图中描述的是一个A对应多个B类类型的情况,需要在A类以Set集合的方式引入B类型的对象,在B类中定义A类类型的属性a

多对多:在A类中定义B类类型的Set集合,在B类中定义A类类型的Set集合,这里用Set集合的目的是为了避免数据的重复

这里简单复习一下集合框架
hibernate3-3

代码分析>>>>一对多

创建实体

客户的实体类:

public class Customer {
    private int   cust_id;
    private String cust_name;
    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_phone;
    private String cust_mobile;
    //一个客户有多个联系人,客户中应该放有联系人的集合
    private Set<Linkman> linkMans= new HashSet<Linkman>();

    public Set<Linkman> getLinkMans() {
        return linkMans;
    }

    public void setLinkMans(Set<Linkman> linkMans) {
        this.linkMans = linkMans;
    }
    

    public int getCust_id() {
        return cust_id;
    }

    public void setCust_id(int cust_id) {
        this.cust_id = cust_id;
    }

    public String getCust_name() {
        return cust_name;
    }

    public void setCust_name(String cust_name) {
        this.cust_name = cust_name;
    }

    public String getCust_source() {
        return cust_source;
    }

    public void setCust_source(String cust_source) {
        this.cust_source = cust_source;
    }

    public String getCust_industry() {
        return cust_industry;
    }

    public void setCust_industry(String cust_industry) {
        this.cust_industry = cust_industry;
    }

    public String getCust_level() {
        return cust_level;
    }

    public void setCust_level(String cust_level) {
        this.cust_level = cust_level;
    }

    public String getCust_phone() {
        return cust_phone;
    }


    public void setCust_phone(String cust_phone) {
        this.cust_phone = cust_phone;
    }

    public String getCust_mobile() {
        return cust_mobile;
    }

    public void setCust_mobile(String cust_mobile) {
        this.cust_mobile = cust_mobile;
    }

联系人的实体类:

public class Linkman {

    private int lkm_id;
    private String lkm_name;
    private String lkm_gender;
    private String lkm_phone;
    private String lkm_mobile;
    private String lkm_email;
    private String lkm_qq;
    private String lkm_position;
    private String lkm_memo;

    private Customer customer;

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    public int getLkm_id() {
        return lkm_id;
    }

    public void setLkm_id(int lkm_id) {
        this.lkm_id = lkm_id;
    }

    public String getLkm_name() {
        return lkm_name;
    }

    public void setLkm_name(String lkm_name) {
        this.lkm_name = lkm_name;
    }

    public String getLkm_gender() {
        return lkm_gender;
    }

    public void setLkm_gender(String lkm_gender) {
        this.lkm_gender = lkm_gender;
    }

    public String getLkm_phone() {
        return lkm_phone;
    }

    public void setLkm_phone(String lkm_phone) {
        this.lkm_phone = lkm_phone;
    }

    public String getLkm_mobile() {
        return lkm_mobile;
    }

    public void setLkm_mobile(String lkm_mobile) {
        this.lkm_mobile = lkm_mobile;
    }

    public String getLkm_email() {
        return lkm_email;
    }

    public void setLkm_email(String lkm_email) {
        this.lkm_email = lkm_email;
    }

    public String getLkm_qq() {
        return lkm_qq;
    }

    public void setLkm_qq(String lkm_qq) {
        this.lkm_qq = lkm_qq;
    }

    public String getLkm_position() {
        return lkm_position;
    }

    public void setLkm_position(String lkm_position) {
        this.lkm_position = lkm_position;
    }

    public String getLkm_memo() {
        return lkm_memo;
    }

    public void setLkm_memo(String lkm_memo) {
        this.lkm_memo = lkm_memo;
    }
}

创建映射

客户:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!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.lcx.domain.Customer" table="cst_customer">
        <id name="cust_id" column="cust_id">
            <generator class="native"/>
        </id>
        <property name="cust_name" column="cust_name"/>
        <property name="cust_source" column="cust_source"/>
        <property name="cust_industry" column="cust_industry"/>
        <property name="cust_level" column="cust_level"/>
        <property name="cust_phone" column="cust_phone"/>
        <property name="cust_mobile" column="cust_mobile"/>  
        <set name="linkMans">
            <key column="lkm_cust_id"></key>
            <one-to-many class="cn.lcx.domain.Linkman"></one-to-many>
        </set>    
    </class>
</hibernate-mapping>

分析:使用set集合来描述Customer.java类中的属性linkMans。在Hibernate的映射文件中,使用< set >标签用来描述被映射类中的Set集合,< key >标签的column属性值对应多的一方的外键名称,由于在客户类中,客户与联系人是一对多的关系,在映射文件中,使用 < one-to-many >标签来描述持久化类的一对多关联,class属性用来描述映射的关联类。


联系人:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!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.lcx.domain.Linkman" table="cst_linkman">
        <id name="lkm_id" column="lkm_id">
            <generator class="native"/>
        </id>
        <property name="lkm_name" column="lkm_name"/>
        <property name="lkm_gender" column="lkm_gender"/>
        <property name="lkm_phone" column="lkm_phone"/>
        <property name="lkm_mobile" column="lkm_mobile"/>
        <property name="lkm_email" column="lkm_email"/>
        <property name="lkm_qq" column="lkm_qq"/>
        <property name="lkm_position" column="lkm_position"/>
        <property name="lkm_memo" column="lkm_memo"/>
        <many-to-one name="customer" class="cn.lcx.domain.Customer" column="lkm_cust_id"></many-to-one>
    </class>
</hibernate-mapping>

分析:< many-to-one >标签定义两个实体类的关联,从名字可以看出来,这个是多对一的关联,联系人与客户就是多对一的关系,所以用这个标签来描述,name属性用来描述customer在Linkan.java类中的属性的名称,clss用来指定映射的类,column对应表中的外键名,这个名要与上述配置文件中的 key 标签中column的属性的值对应。

将映射添加到配置文件中

<!--加载映射关系-->
        <mapping resource="cn/lcx/domain/Customer.hbm.xml"></mapping>
        <mapping resource="cn/lcx/domain/Linkman.hbm.xml"></mapping>

创建测试代码

 //创建一个客户
            Customer customer = new Customer();
            customer.setCust_name("小溪");
            // 创建两个联系人:
            Linkman linkman1=new Linkman();
            linkman1.setLkm_name("lucy");
            Linkman linkman2=new Linkman();
            linkman2.setLkm_name("mary");
            //建立关系,联系人放到customer的set集合里面
            customer.getLinkMans().add(linkman1);
            customer.getLinkMans().add(linkman2);
            //custome放到联系人里面
            linkman1.setCustomer(customer);
            linkman2.setCustomer(customer);
            session.save(customer);
            session.save(linkman1);
            session.save(linkman2);

控制台输出:
hibernate3-4
成功输出了三条insert语句和两条update语句
数据库情况:
hibernate3-5

hibernate3-6
这样基本的一对多的关系映射已经配置好了,从上面我们可以看出我们建立的关系是双向的,客户关联了联系人,同时联系人也关联了客户。


以上这种方法略显复杂,我们能否只保存客户或者只保存联系人呢?

 //创建一个客户
            Customer customer = new Customer();
            customer.setCust_name("小溪");
            // 创建两个联系人:
            Linkman linkman1=new Linkman();
            linkman1.setLkm_name("lucy");
            Linkman linkman2=new Linkman();
            linkman2.setLkm_name("mary");
            //建立关系,联系人放到customer的set集合里面
            customer.getLinkMans().add(linkman1);
            customer.getLinkMans().add(linkman2);
            //custome放到联系人里面
            linkman1.setCustomer(customer);
            linkman2.setCustomer(customer);
            session.save(customer);

hibernate3-7
运行这个代码,我们发现出现了错误。瞬时对象异常,持久态的对象关联了一个瞬时态对象的异常,那么怎么解决呢?

Hibernate的级联操作

什么是级联?

级联是指当主控方执行保存、更新或者删除操作时,关联对象也执行相同的操作。

在映射文件中通过对cascade属性的设置来控制是否执行级联操作。

级联保存或更新

首先我们要确定哪一方是主控方,哪一方是被控方,在我的这个例子中,很明显,customer是主控方。在映射文件中添加cascade属性

 <set name="linkMans" cascade="save-update">
            <key column="lkm_cust_id"></key>
            <one-to-many class="cn.lcx.domain.Linkman"></one-to-many>
        </set>

然后我们运行这个代码:

//创建一个客户
            Customer customer = new Customer();
            customer.setCust_name("小溪");
            // 创建两个联系人:
            Linkman linkman1=new Linkman();
            linkman1.setLkm_name("lucy");
            Linkman linkman2=new Linkman();
            linkman2.setLkm_name("mary");
            //建立关系,联系人放到customer的set集合里面
            customer.getLinkMans().add(linkman1);
            customer.getLinkMans().add(linkman2);
            //custome放到联系人里面
            linkman1.setCustomer(customer);
            linkman2.setCustomer(customer);
            session.save(customer);

我们发现,这回可以成功运行了,这样只需要把联系人放到客户里面就可以了,最终只需要保存客户就可以了

级联删除

理解了级联的保存和更新,其实删除也不能理解了,级联的删除也是有方向性的。

在JDBC中,两个表中如果存在外键关系是不可以删除的,那么在Hibernate中是如何处理的呢?我们来看代码:

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

hibernate3-8
我们可以看到,代码执行的时候,Hibernate会将外键设为null,然后再去删除,在数据库表中,只是把客户删除了,联系人还没有删除,在大多数情况下,我们在删除客户的时候,会将客户所关联的联系人一并删除。

这个时候我们就需要级联删除操作了。

<set name="linkMans" cascade="save-update,delete">
            <key column="lkm_cust_id"></key>
            <one-to-many class="cn.lcx.domain.Linkman"></one-to-many>
        </set>

在cascade中增加delete。


然后我们重新运行测试代码。

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

这样在删除客户的同时也删除了相关联的联系人
hibernate3-9

代码分析>>>>多对多

通过之前的分析,我们在操作多对多的时候,需要创建第三个表来建立关系。在这里,我们假设客户包含多个联系人,同时联系人也包括多个客户。

创建实体类

客户实体类

public class Customer {
    private int   cust_id;
    private String cust_name;
    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_phone;
    private String cust_mobile;
    private Set<Linkman> linkMans= new HashSet<Linkman>();

    public Set<Linkman> getLinkMans() {
        return linkMans;
    }

    public void setLinkMans(Set<Linkman> linkMans) {
        this.linkMans = linkMans;
    }


    public int getCust_id() {
        return cust_id;
    }

    public void setCust_id(int cust_id) {
        this.cust_id = cust_id;
    }

    public String getCust_name() {
        return cust_name;
    }

    public void setCust_name(String cust_name) {
        this.cust_name = cust_name;
    }

    public String getCust_source() {
        return cust_source;
    }

    public void setCust_source(String cust_source) {
        this.cust_source = cust_source;
    }

    public String getCust_industry() {
        return cust_industry;
    }

    public void setCust_industry(String cust_industry) {
        this.cust_industry = cust_industry;
    }

    public String getCust_level() {
        return cust_level;
    }

    public void setCust_level(String cust_level) {
        this.cust_level = cust_level;
    }

    public String getCust_phone() {
        return cust_phone;
    }


    public void setCust_phone(String cust_phone) {
        this.cust_phone = cust_phone;
    }

    public String getCust_mobile() {
        return cust_mobile;
    }

    public void setCust_mobile(String cust_mobile) {
        this.cust_mobile = cust_mobile;
    }

联系人的实体类

public class Linkman {

    private int lkm_id;
    private String lkm_name;
    private String lkm_gender;
    private String lkm_phone;
    private String lkm_mobile;
    private String lkm_email;
    private String lkm_qq;
    private String lkm_position;
    private String lkm_memo;

    public Set<Customer> getCustomers() {
        return customers;
    }

    public void setCustomers(Set<Customer> customers) {
        this.customers = customers;
    }

    private Set<Customer> customers=new HashSet<Customer>();

    public int getLkm_id() {
        return lkm_id;
    }

    public void setLkm_id(int lkm_id) {
        this.lkm_id = lkm_id;
    }

    public String getLkm_name() {
        return lkm_name;
    }

    public void setLkm_name(String lkm_name) {
        this.lkm_name = lkm_name;
    }

    public String getLkm_gender() {
        return lkm_gender;
    }

    public void setLkm_gender(String lkm_gender) {
        this.lkm_gender = lkm_gender;
    }

    public String getLkm_phone() {
        return lkm_phone;
    }

    public void setLkm_phone(String lkm_phone) {
        this.lkm_phone = lkm_phone;
    }

    public String getLkm_mobile() {
        return lkm_mobile;
    }

    public void setLkm_mobile(String lkm_mobile) {
        this.lkm_mobile = lkm_mobile;
    }

    public String getLkm_email() {
        return lkm_email;
    }

    public void setLkm_email(String lkm_email) {
        this.lkm_email = lkm_email;
    }

    public String getLkm_qq() {
        return lkm_qq;
    }

    public void setLkm_qq(String lkm_qq) {
        this.lkm_qq = lkm_qq;
    }

    public String getLkm_position() {
        return lkm_position;
    }

    public void setLkm_position(String lkm_position) {
        this.lkm_position = lkm_position;
    }

    public String getLkm_memo() {
        return lkm_memo;
    }

    public void setLkm_memo(String lkm_memo) {
        this.lkm_memo = lkm_memo;
    }
}

与一对多不同的是,由于我们这次测试的是多对多,所以在联系人这里面也要用set集合来包含customer的数据。

映射文件的设置

客户:

<set name="linkMans" table="tab_cust_link">
            <key column="cust_id"></key>
            <many-to-many class="cn.lcx.domain.Linkman" column="link_id"></many-to-many>
        </set>                                                                                                                        

联系人:

<set name="customers" table="tab_cust_link">
            <key column="link_id"></key>
            <many-to-many class="cn.lcx.domain.Customer" column="cust_id"></many-to-many>
        </set>

分析:set标签中,name属性为关联的另一方的集合的属性名称,table为中间表的名称


key标签,column中当前对象在中间表的外键的名称


many-to-many标签,class为关联另一方的类的全路径,colum为关联的另一方在中间表的外键的名称

编写测试代码

            Customer customer1=new Customer();
            customer1.setCust_name("小溪");
            Customer customer2=new Customer();
            customer2.setCust_name("大溪");
            Linkman linkman1=new Linkman();
            linkman1.setLkm_name("李四1");
            Linkman linkman2=new Linkman();
            linkman2.setLkm_name("张三2");
            Linkman linkman3=new Linkman();
            linkman3.setLkm_name("王五3");

            System.out.println("123");
            customer1.getLinkMans().add(linkman1);
            customer1.getLinkMans().add(linkman2);
            customer2.getLinkMans().add(linkman2);
            customer2.getLinkMans().add(linkman3);

            session.save(customer1);
            session.save(customer2);
            session.save(linkman1);
            session.save(linkman2);
            session.save(linkman3);

hibernate3-10
创建成功

多对多>>>>级联添加

多对多的级联操作和一对多的差不多,我们来看配置文件

<set name="linkMans" table="tab_cust_link" cascade="save-update">
            <key column="cust_id"></key>
            <many-to-many class="cn.lcx.domain.Linkman" column="link_id"></many-to-many>
        </set>

同样的,我们只要加入 cascade="save-update"就可以操作多对多的级联

测试代码如下:

            Customer customer1=new Customer();
            customer1.setCust_name("小溪");
            Customer customer2=new Customer();
            customer2.setCust_name("大溪");
            Linkman linkman1=new Linkman();
            linkman1.setLkm_name("李四1");
            Linkman linkman2=new Linkman();
            linkman2.setLkm_name("张三2");
            Linkman linkman3=new Linkman();
            linkman3.setLkm_name("王五3");

            System.out.println("123");
            customer1.getLinkMans().add(linkman1);
            customer1.getLinkMans().add(linkman2);
            customer2.getLinkMans().add(linkman2);
            customer2.getLinkMans().add(linkman3);

            session.save(customer1);
            session.save(customer2);

级联删除

删除操作其实不常用,了解就好,在配置文件中加入delete参数

<set name="linkMans" table="tab_cust_link" cascade="delete">
            <key column="cust_id"></key>
            <many-to-many class="cn.lcx.domain.Linkman" column="link_id"></many-to-many>
        </set>

其他操作

由于多对多的关系主要靠中间表来维护,那么一些操作我们只需要操作中间表就可以了

删除

删除客户1中的联系人2

            Customer customer=session.get(Customer.class,1);
            Linkman linkman=session.get(Linkman.class,2);
            //操作集合,相当于操作中间表
            customer.getLinkMans().remove(linkman);

添加

给客户1添加联系人2

Customer customer=session.get(Customer.class,1);
            Linkman linkman=session.get(Linkman.class,2);
            customer.getLinkMans().add(linkman);

变更

客户1中的联系人2变更为联系人3

 Customer customer=session.get(Customer.class,1);
            Linkman linkman1=session.get(Linkman.class,2);
            Linkman linkman2=session.get(Linkman.class,3);
            customer.getLinkMans().remove(linkman1);
            customer.getLinkMans().add(linkman2);

总结

以上内容就是一对多,多对多之间的级联操作,只要理解了一对多,多对多的操作也就不难了。