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

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格式是会造成死循环。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值