hibernate多表操作1(六)多对一和一对多

来源:互联网 发布:数据库外键和主键 编辑:程序博客网 时间:2024/06/18 15:13

hibernate多表操作

多对一

创建学生和班级的类以及对应的映射文件,多对一的情况多个学生属于一个班级,以此为例。类中除了基本的属性需要在学生类中添加一个班级的引用。

班级类

package com.hibernate.entity;public class Grade implements java.io.Serializable{    private static final long serialVersionUID = 1L;    private String id;    private String gradeName;

学生类

package com.hibernate.entity;public class Student implements java.io.Serializable{    private static final long serialVersionUID = 1L;    private String id;    private String stuName;    private  Grade grade;}

在映射文件中普通的属性已经通过普通的标签进行了映射,对于引用的属性,需要运用many-to-one标签进行映射。注意该标签要在多的那一方类的映射文件中添加。
name属性:引用的名称;
class属性:对应的类可以指明也可以不指明;
column属性:外键名称自己随便起;
添加完该标签之后将映射文件添加到连接文件中。

<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping><class name="com.hibernate.entity.Student" >        <id name="id">            <generator class="uuid" />        </id>           <property name="stuName" />        <many-to-one name="grade" class="com.hibernate.entity.Grade" column="fk_s_g"/>    </class></hibernate-mapping>

测试类

public static void main(String[] args) {        Configuration cfg = new Configuration().configure();        SchemaExport export = new SchemaExport(cfg);        export.create(true, true);    }

运行测试类结果中可以看到有生成两个表的语句和外键的语句。
这里写图片描述

观察数据库中生成的表,可以在学生表中看到外键已经被创建。

这里写图片描述这里写图片描述

测试添加

将多个学生保存到一个班级中,创建一个班级临时对象,将班级类持久化,创建多个学生持久对象。

    public static void saveManyToOne() {        Grade grade = new Grade();        grade.setGradeName("测试班级1");        BaseDao.saveOrUpdate(grade);        Student student1 = new Student();        student1.setStuName("娃娃");        student1.setGrade(grade);        BaseDao.saveOrUpdate(student1);        Student student2 = new Student();        student2.setStuName("张三");        student2.setGrade(grade);        BaseDao.saveOrUpdate(student2);    }

从运行结果的sql语句中可以看到外键已经insert进去。
这里写图片描述

可以看到已经将班级表的id拿来存到学生表中,是hibernate自己存储的。

这里写图片描述这里写图片描述

测试查询

查学生类就可以了。当输出的信息包括班级是就会报错。

这里写图片描述
报错原因:配置文件中少配置一个属性。lazy:值为false,将其添加即可成功运行。属性设置为false,让延迟加载,其实本质是不让hibernate使用JDK的动态代理机制。
lazy=”false”指定此关联总是被预先抓取

    <many-to-one name="grade" class="com.hibernate.entity.Grade" column="fk_s_g" lazy="false"/>

通过设置断点进行调试,在有该标签时,可以看到班级类的内容已经被获取到。

这里写图片描述

没有时添加该标签时,班级类的内容没有被获取。主要是因为他的底层用的是JDK的动态代理,当你不用的时候不查出来,用的时候才查出来。添加这个标签是屏蔽了动态代理。动态代理在查询数据时不是从数据库中查,从内容中查。就好比买火车票在代理商处买,而不是直接去火车站买更加方便。动态代理在此可以理解为不加载引用类中的属性。

这里写图片描述

从运行结果可以看出查询是先查从表(学生表)再查主表(班级表)。
这里写图片描述

一对多

一对多情况:一个班级包括多个学生。以此为例,完成一对多的操作,需要在班级类中装多个学生,在其中创建一个set集合(选择set因为选其他集合有下标最终会在数据库中生成表的字段),其中存储student,并声明该集合的get,set方法。删除掉在多对一时对学生类的操作。

private Set<Student> students=new HashSet<Student>();    public Set<Student> getStudents() {        return students;    }    public void setStudents(Set<Student> students) {        this.students = students;    }

如何在映射文件中对集合来进行映射:运用set标签,name属性:是属性的名称;key标签是外键,其中的column属性值是外键名;关系用one-to-many标签,其中class属性是集合中存储内容对应的类名称。

<set name="students">            <key column="fk_s_g" />            <one-to-many class="com.hibernate.entity.Student" />        </set>

运行后查看生成的数据库发现与之前多对一生成的表是一致的,在学生表中生成了一个外键,说明两种情况的主外键关系是一致的,只是在类的层面发生变化。

测试添加

创建一个班级对象,通过集合对应的get方法得到班级里的学生集合,在往集合中添加学生,之后在将集合设置到班级里,在学生类里创建一个构造函数(通过姓名创建对象),将班级类持久化,学生是临时对象。

public static void saveOneToMany() {        Grade grade=new Grade();        grade.setGradeName("测试一对多");        Set<Student> students=grade.getStudents();        students.add(new Student("张三"));        students.add(new Student("李斯"));        students.add(new Student("王五"));        grade.setStudents(students);        BaseDao.saveOrUpdate(grade);    }

在主函数中运行此方法,因为是一个持久对象可以调用临时对象,会报如下错。

这里写图片描述

解决办法在set标签中添加cascade属性:级联操作。指明哪些操作会从父对象级联到关联的对象。

    <set name="students" cascade="all">

再次运行,通过sql语句可以看到此时外键是通过更新完成的。

这里写图片描述

测试查询

查询比较麻烦因此一般将一对多转换为多对一,调用dao层方法查找班级,得到班级里面的学生集合,设置迭代器输出学生姓名,注意要在set标签中添加lazy属性并将值设置为false。

public static void findOneToMany(){        Grade grade=(Grade) BaseDao.getUser("from Grade where id='402881e55de38bcb015de38bd0e70001'");        System.out.println(grade.getGradeName());        Set<Student> students=grade.getStudents();        Iterator<Student> iter=students.iterator();        while(iter.hasNext()){            System.out.println(iter.next().getStuName());        }       }

在一个类中添加是单向,当然也可以在两个类中都添加是双向,但这样做会造成一些不合理的问题,例如:转成json格式是会造成死循环。

原创粉丝点击