[学习笔记] Hibernate之Hibernate关系映射

# 学习 # · 2021-03-15

单向多对一

1、关系剖析:

(1)关系:多个员工对应一个部门的多对一的关系。

(2)意味:多个Emp对象只会引用一个Dept对象。

(3)方法:在Emp类中定义一个Dept类型属性,来引用所有关联的Dept对象。

2、实现单项多对一:

(1)建立两个实体类(Emp和Dept):

/**
 * Dept实体类
 */
public class Dept implements Serializable {
    private Integer deptNo;
    private String deptName;
    private String loc;
}

/**
 * Emp实体类
 */
public class Emp implements Serializable {
    private Integer empNo;
    private String ename;
    private String job;
    private Date hireDate;
    private Integer sal;
    // 定义Dept类型属性,引用关联的Dept对象
    private Dept dept;
}

(2)建立Emp.hbm.xml映射文件:

<hibernate-mapping>
    <class name="com.aduo.pojo.Emp" table="`EMP`" dynamic-update="true">
        <id name="empNo" column="`EMPNO`" type="java.lang.Integer">
            <generator class="native"/>
        </id>
        <property name="ename" type="java.lang.String" column="`ENAME`"/>
        <property name="job" type="java.lang.String" column="`JOB`"/>
        <property name="hireDate" type="java.util.Date" column="`HIREDATE`"/>
        <property name="sal" type="java.lang.Integer" column="`SAL`"/>

        <!--
        多对一的实现:建立Emp表的外键DEPTNO和dept属性之间的映射
        ①name:设定持久化类的属性名
        ②column:name对应表的外键,即Emp表的外键
        ③class:设定持久化类的属性的类型
        -->
        <many-to-one name="dept" column="`DEPTNO`" class="com.aduo.pojo.Dept"/>
    </class>
</hibernate-mapping>

(3)建立Dept.hbm.xml映射文件:

<hibernate-mapping>
    <class name="com.aduo.pojo.Dept" table="`DEPT`" dynamic-update="true">
        <id name="deptNo" column="`DEPTNO`" type="java.lang.Integer">
            <generator class="native"/>
        </id>
        <property name="deptName" type="java.lang.String" column="`DNAME`"/>
        <property name="loc" type="java.lang.String" column="`LOC`"/>
    </class>
</hibernate-mapping>

(4)实现持久化操作:新增一个员工,部门ID为10。

@Test
public void AddObjectToDept(){
    // 创建SESSION、开启事务
    Session session = HibernateUtil.currentSession();
    Transaction tx = session.beginTransaction();

    // 定义要添加的员工所属的部门信息deptNo
    Dept dept = new Dept();
    dept.setDeptNo(10);

    // 定义要添加的员工信息(员工姓名、关联的部门信息)
    Emp emp = new Emp();
    emp.setEname("小王");
    emp.setDept(dept);

    // 执行插入数据操纵
    session.save(emp);
    tx.commit();
    session.close();
}

(5)实现持久化操作:查找部门ID为10的员工信息。

@Test
public void GetObjectFromEmp() {
    // 创建SESSION、开启事务
    Session session = HibernateUtil.currentSession();
    Transaction tx = session.beginTransaction();

    // 指定查询的员工所在的部门ID为10
    Dept dept = new Dept();
    dept.setDeptNo(10);

    // 定义HQL查询语句
    String hqlStr = "from Emp where dept=?";
    Query query = session.createQuery(hqlStr);
    query.setParameter(0,dept);

    // 执行查询
    List<Emp> list = query.list();
    for (Emp emp : list) {
        System.out.println(emp);
    }
    tx.commit();
    session.close();
}

单项一对多

1、关系剖析:

(1)关系:一个部门里有多个员工的一对多的关系。

(2)意味:每一个Dept对象会引用一组Emp对象。

(3)方法:Dept类中应该定义一个集合类型属性,来引用所有关联的Emp对象。

2、实现单项一对多:

(1)建立两个实体类(Emp和Dept):

/**
 * Dept实体类
 */
public class Dept implements Serializable {
    private Integer deptNo;
    private String deptName;
    private String loc;
    // 定义集合类型属性,引用当前部门下的员工对象
    private Set<Emp> emps = new HashSet<Emp>();
}

/**
 * Emp实体类
 */
public class Emp implements Serializable {
    private Integer empNo;
    private String ename;
    private String job;
    private Date hireDate;
    private Integer sal;
}

(2)建立Dept.hbm.xml映射文件:

<hibernate-mapping>
    <class name="com.aduo.pojo.Dept" table="`DEPT`" dynamic-update="true">
        <id name="deptNo" column="`DEPTNO`" type="java.lang.Integer">
            <generator class="native"/>
        </id>
        <property name="deptName" type="java.lang.String" column="`DNAME`"/>
        <property name="loc" type="java.lang.String" column="`LOC`"/>
        <!--
        set元素:映射emps属性
        ①name属性:设定持久化类的属性名
        ②key元素中的column属性:设定与所关联的持久化类相对应的表的外键
        ③one-to-many元素中的class属性:设定所关联的持久化类型
        -->
        <set name="emps">
            <key column="`DEPTNO`"></key>
            <one-to-many class="com.aduo.pojo.Emp"></one-to-many>
        </set>
    </class>
</hibernate-mapping>

(4)实现持久化操作:查询部门ID为10的员工信息。

@Test
public void GetObjectFromDept() {
    // 创建SESSION、开启事务
    Session session = HibernateUtil.currentSession();
    Transaction tx = session.beginTransaction();

    // 定义HQL查询语句
    String hqlStr = "from Dept where deptNo=?";
    Query query = session.createQuery(hqlStr);
    query.setParameter(0,10);

    // 执行查询
    List<Dept> list = query.list();
    for (Dept dept : list) {
        System.out.println(dept);
    }
    tx.commit();
    session.close();
}

双向一对多

1、关系剖析:满足多对一和一对多的关联就是双向关联。

2、配置双向一对多关联:

(1)建立两个实体类(Emp和Dept):

/**
 * Dept实体类
 */
public class Dept implements Serializable {
    private Integer deptNo;
    private String deptName;
    private String loc;
    // 定义集合类型属性,引用当前部门下的员工对象
    private Set<Emp> emps = new HashSet<Emp>();
}

/**
 * Emp实体类
 */
public class Emp implements Serializable {
    private Integer empNo;
    private String ename;
    private String job;
    private Date hireDate;
    private Integer sal;
    // 定义Dept类型属性,引用关联的Dept对象
    private Dept dept;
}

(2)Dept.hbm.xml映射文件的内容和单向一对多中使用的映射文件内容一致,Emp.hbm.xml映射文件的内容和单向多对一中使用的映射文件内容一致。

3、set的cascade属性:用于指定如何操纵与当前对象关联的其他对象。

(1)none:当Sesssion操纵当前对象时,忽略其他关联的对象。

(2)save-update:当Session保存或更新当前对象时,级联保存所有关联的瞬时状态的对象,级联更新所有关联的游离状态的对象。

(3)delete:当Session的delete方法删除当前对象时,会级联删除所有关联的对象。

(4)all:包含save-update、delete的行为。

4、set的inverse属性:译为反转,其指定了关联关系中的方向。

(1)inverse=false:在关联关系中为主动方,主动方会负责维护关联关系。

4、双向关联关系下的增操作:

(1)修改Dept.hbm.xml配置文件,在set元素中设置cascade属性:

<set name="emps" cascade="all">
    <key column="`DEPTNO`"></key>
    <one-to-many class="com.aduo.pojo.Emp"></one-to-many>
</set>

(2)在测试类中实现:保存Dept对象的同时级联保存与Dept对象关联的Emp对象。

@Test
public void AddObjectToDept() {
    // 创建SESSION、开启事务
    Session session = HibernateUtil.currentSession();
    Transaction tx = session.beginTransaction();

    // 创建一个新的部门对象
    Dept dept = new Dept();
    dept.setDeptName("新财务部");

    // 创建两个新的员工
    Emp emp1 = new Emp();
    emp1.setEname("NewUA");
    Emp emp2 = new Emp();
    emp2.setEname("NewUB");

    // 将员工添加到部门里
    dept.getEmps().add(emp1);
    dept.getEmps().add(emp2);

    // 将部门绑定到员工对象的属性上
    emp1.setDept(dept);
    emp2.setDept(dept);

    // 执行新增操作
    session.save(dept);
    tx.commit();
    session.close();
}

4、双向关联关系下的删操作:

(1)在测试类中实现:删除Dept对象的同时级联删除与Dept对象关联的Emp对象。

@Test
public void DeleteObjectFromDept() {
    // 创建SESSION、开启事务
    Session session = HibernateUtil.currentSession();
    Transaction tx = session.beginTransaction();

    // 创建一个要删除的部门对象
    Dept dept = new Dept();
    dept.setDeptNo(13);

    // 执行删除操作
    session.delete(dept);
    tx.commit();
    session.close();
}

5、双向关联关系下的改操作:

(1)修改Dept.hbm.xml配置文件,在set元素中设置inverse属性:

<set name="emps" inverse="false">
    <key column="`DEPTNO`"></key>
    <one-to-many class="com.aduo.pojo.Emp"></one-to-many>
</set>

(2)在测试类中实现:将员工7369的部门ID调整为20。

@Test
public void update() {
    // 创建SESSION、开启事务
    Session session = HibernateUtil.currentSession();
    Transaction tx = session.beginTransaction();

    // 加载Emp和Dept的持久化对象
    Emp emp = (Emp) session.load(Emp.class, 7369);
    Dept dept = (Dept) session.load(Dept.class9,10);

    // 建立Emp对Dept的关联关系
    emp.setDept(dept);

    // 建立Dept对Emp的关联关系
    dept.getEmps().add(emp);

    // 执行修改操作
    session.update(emp);
    tx.commit();
}

单向多对多

1、关系剖析:

(1)关系:一个项目需要多位员工参与,一位员工可能参与多个项目,项目和员工之间构成了多对多关系。

(2)在关系数据模型中,无法直接表达PROJECT和EMPLOYEE表之间的多对多关系,需要创建一个连接表PROEMP。它同时参照两表。

2、配置单项多对多关联:

(1)建立两个实体类(Project和Employee):

/**
 * Project实体类
 */
public class Project implements Serializable {
    private Integer proid;
    private String proname;
    Set<Employee> employees = new HashSet<Employee>();
}

/**
 * Employee实体类
 */
public class Employee {
    private Integer empid;
    private String empname;
}

(2)建立Project.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>
    <class name="com.aduo.pojo.Project" table="`PROJECT`" dynamic-update="true">
        <id name="proid" column="`PROID`" type="java.lang.Integer">
            <generator class="native"/>
        </id>
        <property name="proname" type="java.lang.String" column="`PRONAME`"/>
        <!--
        set元素:映射employees属性
        ①table:指定关系表的名称
        ②save-update:表明保存或更新Project对象时,会级联保存或更新与它关联的Employee对象
        ③key:指定PROEMP的外键RPROID,用来参照PROJECT表
        ④many-to-many中的class:指定集合存放Employee对象
        ⑤many-to-many中的column:指定PROJECT表的外键REMPID,用来参照EMPLOYEE表
        -->
        <set name="employees" table="`PROEMP`" cascade="save-update">
            <key column="`RPROID`"></key>
            <many-to-many class="com.aduo.pojo.Employee" column="`REMPID`"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

(3)建立Employee.hbm.xml映射文件:

<?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="com.aduo.pojo.Employee" table="`EMPLOYEE`">
        <id name="empid" type="java.lang.Integer">
            <column name="`EMPID`" precision="6" scale="0" />
            <generator class="native" />
        </id>
        <property name="empname" type="java.lang.String">
            <column name="`EMPNAME`" length="100" not-null="true" />
        </property>
    </class>
</hibernate-mapping>

(4)实现持久化操作:创建两个Project对象和两个Employee对象,建立它们的关联关系,保存Project的同时保存Employee对象。

@Test
public void addNewObject() {
    // 创建SESSION、开启事务
    Session session = HibernateUtil.currentSession();
    Transaction tx = session.beginTransaction();

    // 创建两个员工对象
    Employee emp1 = new Employee();
    emp1.setEmpname("张三");
    Employee emp2 = new Employee();
    emp2.setEmpname("李四");

    // 创建两个项目对象
    Project proj1 = new Project();
    proj1.setProname("A项目");
    Project proj2 = new Project();
    proj2.setProname("B项目");

    // 将员工绑定到项目上,建立关联
    proj1.getEmployees().add(emp1);
    proj2.getEmployees().add(emp2);

    // 执行插入项目和员工
    session.save(proj1);
    session.save(proj2);
    tx.commit();
}


双向多对多

1、关系剖析:

2、配置双向多对多关联:

(1)建立两个实体类(Project和Employee):

/**
 * Project实体类
 */
public class Project implements Serializable {
    private Integer proid;
    private String proname;
    Set<Employee> employees = new HashSet<Employee>();
}

/**
 * Employee实体类
 */
public class Employee {
    private Integer empid;
    private String empname;
    private Set<Project> projects = new HashSet<Project>();
}

(2)建立Project.hbm.xml映射文件:

<set name="employees" table="`PROEMP`" cascade="save-update">
    <key column="`RPROID`"></key>
    <many-to-many class="com.aduo.pojo.Employee" column="`REMPID`"></many-to-many>
</set>

(3)建立Employee.hbm.xml映射文件:对于双向多对多关联的两端,需要把其中一端的set元素的inverse属性设置为true。

<set name="projects" table="`PROEMP`" inverse="true">
    <key column="`REMPID`"/>
    <many-to-many class="com.aduo.pojo.Project" column="`RPROID`"/>
</set>

(4)实现持久化操作:

@Test
public void addNewProject() {
    // 创建SESSION、开启事务
    Session session = HibernateUtil.currentSession();
    Transaction tx = session.beginTransaction();

    // 创建两个员工对象
    Employee emp1 = new Employee();
    emp1.setEmpname("王五");
    Employee emp2 = new Employee();
    emp2.setEmpname("赵六");

    // 创建两个项目对象
    Project proj1 = new Project();
    proj1.setProname("C项目");
    Project proj2 = new Project();
    proj2.setProname("D项目");

    // 将员工绑定到项目上,建立关联
    proj1.getEmployees().add(emp1);
    proj2.getEmployees().add(emp2);

    // 将项目绑定到员工上,建立关联
    emp1.getProjects().add(proj1);
    emp2.getProjects().add(proj2);

    // 执行插入项目和员工
    session.save(proj1);
    session.save(proj2);
    tx.commit();
}

使用IDEA反向生成工具映射关联关系

1、在IDEA中连接Oracle数据库。

2、配置hibernate反向生成工具:如果建项目的时候没有选择hibernate选项,则右键项目选择Add Frameworks Support,添加hibernate即可。

3、打开Presistence视图,右键选择Generate Presistence Mapping>By Database Schema。


延迟加载

1、延迟加载策略:能避免加载应用程序不需要访问的关联对象。

2、Hibernate允许在对象关系映射文件中使用lazy属性配置加载策略。

(1)类级别:class元素中lazy属性可选值为true(延迟加载)和false(立即加载),默认值为true。

(2)一对多关联级别:set元素中lazy属性可选值为true(延迟加载)、extra(增强延迟加载)和false(立即加载),默认值为true。

(3)多对关联级别:many-to-one元素中lazy属性可选值为proxy(延迟加载)、no-proxy(无代理延迟加载)、false(立即加载),默认值为proxy。

3、Open Session In View模式:在用户的每一次请求过程始终保持一个Session对象打开着。

4、Open Session In View模式的实现:

(1)把Session绑定到当前线程上,保证在一次请求中只有一个Session对象(getCurrentSession())。

(2)用Filter过滤器在请求到达时打开Session,在页面生成完毕时关闭Session。

①OpenSessionInViewFilter.java:

public class OpenSessionInViewFilter implements Filter {
    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1,
            FilterChain arg2) throws IOException, ServletException {
        Transaction tx = null;
        try {
            // 请求到达时,打开Session并启动事务
            tx = HibernateUtil.currentSession().beginTransaction();
            // 执行请求处理链
            arg2.doFilter(arg0, arg1);
            // 返回响应时,提交事务
            tx.commit();
        } catch (HibernateException e) {
            e.printStackTrace();
            if (tx != null)
                tx.rollback(); // 回滚事务
        }
    }

    @Override
    public void destroy() {
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }
}

②在web.xml中配置Filter:

<filter>
    <filter-name>openSessionInView</filter-name>
    <filter-class>cn.hibernatedemo.web.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>openSessionInView</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

(3)调整业务逻辑,删除和会话及事务管理的相关代码,仅保留业务逻辑代码。

如无特殊说明,本博所有文章均为博主原创。

如若转载,请注明出处:一木林多 - https://www.l5v.cn/archives/269/

评论