Hibernate之一对多和多对一映射笔记

引入:

(借用了他人的例子)首先应该清楚多对一和一对多只是站在不同的角度看待问题,其本质是一样的。在思考这个问题的时候,不要把这两个概念混在一起,这样不容易理解,而要分开,站在不同的角度去解决同一个问题。

  • 在数据库中,可以通过添加外主键的关联,表现一对多的关系

  • 通过一方持有多方的集合实现,即在“一”的一端中使用元素表示持有“多”的一端的对象

就拿员工和部门的例子来说,我们站在不同的角度,可能会遇到如下的几种情况:

站在员工的角度看,是多对一的关系:

1、来了新员工,但是还不知道该分配到哪个部门,只有先把员工保存到员工表中,部门那一列设为空,待以后再更新其部门信息

2、来了新员工,并且确定了该员工分配到哪个部门,则将员工的完整信息保存到员工表中

站在部门的角度看,是一对多的关系:

1、成立了新部门,要向其中添加一些新员工

2、要撤销一个部门,这时,这个部门中的员工的部门信息,全部置为空

此外应注意以下几个问题:

1、若是保存员工,说明表中还没有这个员工的id,在保存的时候,设置其id属性的时候,就不要设置,用默认的就可以,这样系统会自动给这个员工分配一个id的

2、若是更新某个员工,说明员工表中已经有了这个员工的id,在更新的时候,一定要指明其id,才可以找到这个员工

3、无论是站在部门还是站在员工的角度,即无论是一对多还是多对一的关系,映射到数据库中的表的关系都是一样的,就相当于是两个表的关系的两种实现方式,但最终的结果是一样的。

下面,我们来看看如何具体的去实现上面提到的几种情况:

单向一对多关联关系

首先配置hibernate的基本环境:

1、创建hibernate.cfg.xml文件:

<?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.password">123456</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate?useUnicode=true&amp;characterEncoding=UTF-8</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

        <!--
            1、 分别为在控制台是否输出sql语句
            2、控制台是否格式化输出sql语句
         -->
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        <property name="hbm2ddl.auto">update</property>
    </session-factory>
</hibernate-configuration>

2、创建HibernateUtil工具类

package com.util;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

    private static Session session;
    private static SessionFactory sessionFactory;

    static{
        //创建Configuration对象,读取Hibernate.cfg.xml文件,完成初始化
        Configuration configuration=new Configuration().configure();
        StandardServiceRegistryBuilder ssrd=new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
        StandardServiceRegistry ssr=ssrd.build();
        sessionFactory=configuration.buildSessionFactory(ssr);
    }

    //获取SessionFactory
    public static SessionFactory GetSessionFactory(){
        return sessionFactory;
    }

    //获取Session
    public static Session GetSession(){
        session=sessionFactory.openSession();
        return session;
    }

    //关闭Session
    public static void CloseSession(){
        if(session!=null){
            session.close();
        }
    }
}

建立两实体个类:Department和Employee

Employee类:

package com.entity;

public class Employee {

    private int Eid; //员工的id
    private String name;//员工的姓名
    private String sex;//员工的性别
    /**
     * 1、属性一定要是私有的
     * 2、public属性的无参构造方法
     * 3、Get和Set方法
     * 4、有参的构造方法
     */
    public Employee(){

    }

    public Employee(String name, String sex) {
        super();
        this.name = name;
        this.sex = sex;
    }

    public Employee(int eid, String name, String sex) {
        super();
        Eid = eid;
        this.name = name;
        this.sex = sex;
    }

    public int getEid() {
        return Eid;
    }

    public void setEid(int eid) {
        Eid = eid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

Department 类:

package com.entity;

import java.util.HashSet;
import java.util.Set;

public class Department {

    private int Did;//部门id
    private String Dname;//部门姓名
    private Set<Employee> employees=new HashSet<Employee>();//员工集合

    public Department() {
        super();
    }

    public Department(int did, String dname, Set<Employee> employees) {
        super();
        Did = did;
        Dname = dname;
        this.employees = employees;
    }

    public int getDid() {
        return Did;
    }


    public void setDid(int did) {
        Did = did;
    }


    public String getDname() {
        return Dname;
    }

    public void setDname(String dname) {
        Dname = dname;
    }


    public Set<Employee> getEmployees() {
        return employees;
    }


    public void setEmployees(Set<Employee> employees) {
        this.employees = employees;
    }
}

生成两个实体类的映射文件:

Department对应的hbm.xml映射文件:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-8-3 13:27:41 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
    <class name="com.entity.Department" table="DEPARTMENT">
        <id name="Did" type="java.lang.Integer">
            <column name="DID" />
            <generator class="increment" />
        </id>
        <property name="Dname" type="java.lang.String">
            <column name="DNAME" />
        </property>
        <!-- 配置单向的一对多的关联关系 -->
        <set name="employees" table="EMPLOYEE" >
            <key>
                <column name="DID" />
            </key>
            <one-to-many class="com.entity.Employee" />
        </set>
    </class>
</hibernate-mapping>

Set元素常用的属性:

1、name:映射类属性的名称
2、table:关联类的目标数据库
3、lazy:指关联对象是否使用延迟加载 默认:proxy
4、inverse:表示双向关联中被动的一方 默认:false

Employee实体类对象的hbm.xml映射文件内容:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-8-3 14:17:01 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
    <class name="com.entity.Employee" table="EMPLOYEE">
        <id name="Eid" type="int">
            <column name="EID" />
            <!-- increment 类型可以先查询数据库中对应的最大的,然后再在原来的基础上加一  -->
            <generator class="increment" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="sex" type="java.lang.String">
            <column name="SEX" />
        </property>
    </class>
</hibernate-mapping>

最后在配置完两个hbm文件以后,在hibernate核心配置文件当中指定映射文件的路径:

SessionFactory-mapping 下面:
        <!-- 需要填写对应的物理地址 -->
        <mapping resource="com/entity/Employee.hbm.xml"/>
        <mapping resource="com/entity/Department.hbm.xml"/>

【最后】完成以上步骤就可以在test.java中进行测试:

一、单向的一对多关联关系

1、将员工信息存入数据库

    //将员工添加到部门中
    public static void save(){

        Session session=HibernateUtil.GetSession();
        Transaction transaction=session.beginTransaction();
        Department department=new Department("技术部");

        Employee employee=new Employee("张三","男");
        Employee employee2=new Employee("李四","女");

        department.getEmployees().add(employee);
        department.getEmployees().add(employee2);

        System.out.println(department.toString());
        session.save(department);
        session.save(employee);
        session.save(employee2);

        transaction.commit();
        HibernateUtil.CloseSession(session);

    }

数据库显示的结果:

这里写图片描述

2、根据单向的一对多的关系查询部门中包含的员工

//查询部门中中包含的员工信息
    public static void FindEmployee(){

        Session session=HibernateUtil.GetSession();
        Department department=(Department) session.get(Department.class, 1);
        System.out.println("Department:"+department.getDname());

        Set<Employee> employees=department.getEmployees();
        for (Employee employee : employees) {
            System.out.println("EmployeeName:"+employee.getName()+" EmployeeSex:"+employee.getSex());
        }
    }

控制台输出结果:

这里写图片描述

PS:建立关联关系以后,可以方便的从一个对象导航到另外一个对象

3、修改学生信息

//修改学生信息
    public static void update(){
        Department department=new Department("公关部");

        Session session=HibernateUtil.GetSession();
        Transaction tx=session.beginTransaction();//由于需要进行更新的操作,所以需要进行的事务的开启和提交
        Employee employee=(Employee) session.get(Employee.class, 1);//获取ID为1的学生对象
        department.getEmployees().add(employee);
        session.save(department);
        tx.commit();
        HibernateUtil.CloseSession(session);
    }

控制台输出:

这里写图片描述

4、删除学生信息(session.delete( )方法)

    //删除学生信息
    public static void delete(){
        Session session=HibernateUtil.GetSession();
        Transaction tx=session.beginTransaction();
        Employee employee=(Employee) session.get(Employee.class, 2);//获取员工id为2的员工
        session.delete(employee);
        tx.commit();
        HibernateUtil.CloseSession(session);
    }

工程源码地址以及所需要的jar包:工程源码

单向多对一关联关系

  • 多对一的关系和关系数据库中的外键参照关系最匹配,即在已方的表中的一个外键参照另外一个表的主键
  • 通过在多方持有一方的引用实现,需要在“多”的一端使用 配置

1、在多方的定义一个一方的引用:
在原有代码的基础之上修改Employee实体类添加一个多方定义一个一方的引用。

package com.entity;

public class Employee {

    private int Eid; //员工的id
    private String name;//员工的姓名
    private String sex;//员工的性别
    //在多方定义一个一方的引用
    private Department department;
    /**
     * 1、属性一定要是私有的
     * 2、public属性的无参构造方法
     * 3、Get和Set方法
     * 4、有参的构造方法
     */
    public Employee(){

    }

    public Employee(String name, String sex) {
        super();
        this.name = name;
        this.sex = sex;
    }

    public Employee(int eid, String name, String sex) {
        super();
        Eid = eid;
        this.name = name;
        this.sex = sex;
    }

    public int getEid() {
        return Eid;
    }

    public void setEid(int eid) {
        Eid = eid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }
}

对应的hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-8-3 14:17:01 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
    <class name="com.entity.Employee" table="EMPLOYEE">
        <id name="Eid" type="int">
            <column name="EID" />
            <!-- increment 类型可以先查询数据库中对应的最大的,然后再在原来的基础上加一  -->
            <generator class="increment" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="sex" type="java.lang.String">
            <column name="SEX" />
        </property>
        <!-- 配置多对一关联关系 -->
       <many-to-one name="Department" class="com.entity.Department" column="DID"></many-to-one>
    </class>
</hibernate-mapping>

在test下进行测试:

public static void save(){
        Department department =new Department("技术部");
        Employee employee=new Employee("张三","男");
        Employee employee2=new Employee("李四","女");

        //设置关联关系(多对一)
        employee.setDepartment(department);
        employee.setDepartment(department);

        Session session=HibernateUtil.GetSession();
        Transaction tx=session.beginTransaction();

        session.save(department);
        session.save(employee);
        session.save(employee2);

        tx.commit();
        HibernateUtil.CloseSession(session);

    }

控制台输出结果:

这里写图片描述

可以通过对一对多以及多对一的了解,在此基础上建立一个双向多对一关联关系。
双向多对一在测试类中增加关联关系。

    public static void save(){
        Department department =new Department("技术部");

        Employee employee=new Employee("张三","男");
        Employee employee2=new Employee("李四","女");

        //设置关联关系(多对一及一对多)
        department.getEmployees().add(employee);
        department.getEmployees().add(employee2);
        employee.setDepartment(department);
        employee.setDepartment(department);

        Session session=HibernateUtil.GetSession();
        Transaction tx=session.beginTransaction();

        session.save(department);
        session.save(employee);
        session.save(employee2);

        tx.commit();
        HibernateUtil.CloseSession(session);
    }

控制台输出的结果如下图:

这里写图片描述

可以发现其中第四、第五指令为多余的,因为学生知道自己所处的班级,多余的代码对程序的性能有一定的影响,故:我们可以通过inverse属性进行设置。

Inverse属性:

  • inerse表示反转,这个属性可以指定关联关系的方向。
  • set节点的inverse属性指定关联关系的控制方向,默认由one一方来维护

  • 关联关系中,inverse=“false”则为主动方,由主动方负责维护关联关系。

  • 在一对多关联中,只能设置one方的inverse为true,这将有助于性能的改善。

具体实施:
找到one方,在one方配置inverse属性,我们将属性该为true

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-8-3 13:27:41 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
    <class name="com.entity.Department" table="DEPARTMENT">
        <id name="Did" type="java.lang.Integer">
            <column name="DID" />
            <generator class="increment" />
        </id>
        <property name="Dname" type="java.lang.String">
            <column name="DNAME" />
        </property>
        <!-- 配置单向的一对多的关联关系 设置inverse属性为true,由多方维护关联关系 -->
        <set name="employees" table="EMPLOYEE" inverse="true">
            <key>
                <column name="DID" />
            </key>
            <one-to-many class="com.entity.Employee" />
        </set>
    </class>
</hibernate-mapping>

再次测试test,控制台输出:

这里写图片描述

原来的update语句已经没有了,这样子提高了系统的性能。

级联操作:

这里写图片描述

如果设置了级联操作,我们就可以不需要现示的保存多方,只需要保存one方就可以存入数据库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值