Hibernate学习(三)

多对一测试案例

1、类图

 2、创建表

drop table if exists t_student ;

drop table if exists t_class ;

create table t_class (
    id int(5) primary key ,
    name varchar(150)
) ;

create table t_student (
    id int(10) primary key ,
    name varchar(150) ,
    class_id int(5) ,
    foreign key ( class_id ) references t_class( id )
) ;

insert into t_class ( id , name ) values ( 1 , '计算机一班' );
insert into t_class ( id , name ) values ( 2 , '计算机二班' );
insert into t_class ( id , name ) values ( 3 , '网络一班' );
insert into t_class ( id , name ) values ( 4 , '网络二班' );

insert into t_student ( id , name , class_id ) values ( 1 , '张翠山' , 2);
insert into t_student ( id , name , class_id ) values ( 2 , '曾阿牛' , 2 );
insert into t_student ( id , name , class_id ) values ( 3 , '赵敏' , 1 );
insert into t_student ( id , name , class_id ) values ( 4 , '小昭'  , 3 );

3、持久化类

班级类:

package ecut.manytoone.entity;

import java.io.Serializable;

public class Clazz implements Serializable {
    
    private static final long serialVersionUID = 235617966763001653L;
    
    private Integer id;
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

}

学生类:

package ecut.manytoone.entity;

import java.io.Serializable;

public class Student  implements Serializable {

    private static final long serialVersionUID = 399214402506858645L;
    
    private Integer id;
    private String name;
    
    // 反映当前的学生属于哪个班级
    private Clazz clazz ; /** 维护 从 学生 到 班级 的 多对一 关联 */

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Clazz getClazz() {
        return clazz;
    }

    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }

}

学生类中增加clazz属性来使学生和班级相关联

4、映射文件

班级映射文件:

<?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="ecut.manytoone.entity.Clazz" table="t_class">
    
        <id name="id" type="integer" column="id" >
            <generator class="increment" /> 
        </id>
    
        <property name="name" type="string" column="name" />
    
    </class>
    
</hibernate-mapping>

学生映射文件:

<?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="ecut.manytoone.entity.Student" table="t_student">
    
        <id name="id" type="integer" column="id" >
            <generator class="increment" /> 
        </id>
    
        <property name="name" type="string" column="name" />
        
        <!-- 维护从 学生 ( many ) 到 班级 ( one ) 的 多对一 关联 -->
        <!-- many-to-one 中的 lazy 可以是 false(不延迟) 、proxy(产生一个代理对象,用的是时候产生真实对象) 、no-proxy(直接产生真实对象) -->
        <many-to-one name="clazz" class="ecut.manytoone.entity.Clazz" column="class_id"  lazy="proxy" cascade="all" />

    </class>
    
</hibernate-mapping>

通过指定学生类中的属性名所对应的数据库中的列名完成类与数据库的关联

5、hibernate配置文件

<?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.url">jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=UTF8</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">123456</property>
        
        <!-- 设置事务隔离级别 , 取值可以是 1、2、4、8 -->
        <property name="hibernate.connection.isolation">1</property>
        <!-- 设置事务是否自动提交 , 取值可以是 true 、false -->
        <property name="hibernate.connection.autocommit">false</property>
        
        <!-- 指定数据方言类 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        
        <!-- 指示是否显示 执行过的 SQL 语句 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 指示是否对 SQL 语句进行格式化输出 -->
        <property name="hibernate.format_sql">false</property>
        
        <mapping resource="ecut/manytoone/entity/Clazz.hbm.xml"/>
        <mapping resource="ecut/manytoone/entity/Student.hbm.xml"/>
        
    </session-factory>

</hibernate-configuration>

6、测试类

从数据库中获取一个学生对象,之后获得所属的班级:

package ecut.manytoone.test;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ecut.manytoone.entity.Clazz;
import ecut.manytoone.entity.Student;

public class TestManyToOne {
    
    private SessionFactory factory ;
    private Session session ; 
    
    public @Before void init() {
        // 创建一个 Configuration 对象,用来读取配置文件 ( 默认位置、默认名称 )
        Configuration config = new Configuration();
        // 读取配置文件
        config.configure("ecut/manytoone/hibernate.cfg.xml");
        //config.configure(); // 读取 默认位置 ( 当前 classpath ) 的 默认名称 ( hibernate.cfg.xml ) 的配置文件

        // 使用 Configuration 创建 SessionFactory
        factory = config.buildSessionFactory();
        
        // 创建 Session 对象 ( 这里的 Session 是 Java 程序 跟 数据库 之间的 会话 )
        session = factory.openSession();
    }
    
    public @Test void loadStudent(){
        
        Student s = session.find( Student.class  , 2 );
        
        if( s == null ) {
            System.out.println( "查无此人" );
        } else {
            System.out.println( s.getId() + " : " + s.getName() );
            
            Clazz c = s.getClazz();
            if( c == null ){
                System.out.println( s.getName() + " 同学还没有指定班级" );
            } else {
                System.out.println( c.getId() );//c对象只有id,默认只有获取name的时候才会去查询
                System.out.println( "~~~~~~~~~~~~" );
                System.out.println( c.getName() );
            }
            
        }
        
    }
   public @After void destory(){
        session.close();
        factory.close();
    }

}

运行结果:

Hibernate: select student0_.id as id1_1_0_, student0_.name as name2_1_0_, student0_.class_id as class_id3_1_0_ from t_student student0_ where student0_.id=?
2 : 张无忌
2
~~~~~~~~~~~~
Hibernate: select clazz0_.id as id1_0_0_, clazz0_.name as name2_0_0_ from t_class clazz0_ where clazz0_.id=?
计算机二班

在获取班级号时并没有去查询班级表,而获取班级名称时候才会执行查询的sql,类似于load方法中的懒加载,可以通过在映射文件中的many-to-one标签指定lazy属性的值来改变, many-to-one 中的 lazy 可以是 false(不延迟) 、proxy(产生一个代理对象,用的是时候产生真实对象) 、no-proxy(直接产生真实对象)。

        <many-to-one name="clazz" class="ecut.manytoone.entity.Clazz" column="class_id"  lazy="proxy" cascade="all" />

保存一个新创建的学生对象到数据,并指定所属的班级:

package ecut.manytoone.test;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ecut.manytoone.entity.Clazz;
import ecut.manytoone.entity.Student;

public class TestManyToOne {
    
    private SessionFactory factory ;
    private Session session ; 
    
    public @Before void init() {
        // 创建一个 Configuration 对象,用来读取配置文件 ( 默认位置、默认名称 )
        Configuration config = new Configuration();
        // 读取配置文件
        config.configure("ecut/manytoone/hibernate.cfg.xml");
        //config.configure(); // 读取 默认位置 ( 当前 classpath ) 的 默认名称 ( hibernate.cfg.xml ) 的配置文件

        // 使用 Configuration 创建 SessionFactory
        factory = config.buildSessionFactory();
        
        // 创建 Session 对象 ( 这里的 Session 是 Java 程序 跟 数据库 之间的 会话 )
        session = factory.openSession();
    }
   public @Test void saveStudent(){
        
        Clazz clazz = session.find( Clazz.class ,  3 );
        
        Student s = new Student();
        s.setName( "殷离" );
        
        s.setClazz( clazz ); // 告诉 hibernate 新创建的学生 对应的 班级
        
        session.getTransaction().begin();
        session.save( s );
        session.getTransaction().commit();
        
    }public @After void destory(){
        session.close();
        factory.close();
    }

}

保存一个新创建的学生,并为其创建一个新的班级,一起保存:

package ecut.manytoone.test;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ecut.manytoone.entity.Clazz;
import ecut.manytoone.entity.Student;

public class TestManyToOne {
    
    private SessionFactory factory ;
    private Session session ; 
    
    public @Before void init() {
        // 创建一个 Configuration 对象,用来读取配置文件 ( 默认位置、默认名称 )
        Configuration config = new Configuration();
        // 读取配置文件
        config.configure("ecut/manytoone/hibernate.cfg.xml");
        //config.configure(); // 读取 默认位置 ( 当前 classpath ) 的 默认名称 ( hibernate.cfg.xml ) 的配置文件

        // 使用 Configuration 创建 SessionFactory
        factory = config.buildSessionFactory();
        
        // 创建 Session 对象 ( 这里的 Session 是 Java 程序 跟 数据库 之间的 会话 )
        session = factory.openSession();
    }
public @Test void saveStudentAndClazz(){
        
        Clazz clazz = new Clazz();
        clazz.setName( "挖煤工程1班" );
        
        Student s = new Student();
        s.setName( "丽丽" );
        
        s.setClazz( clazz );
        
        session.getTransaction().begin();
        session.save( s ); //  cascade="all" 
        session.getTransaction().commit();
        
    }
    
    public @After void destory(){
        session.close();
        factory.close();
    }

}

若没有在映射文件中的many-to-one标签指定cascade属性则会抛出java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: ecut.manytoone.entity.Clazz的异常,为了解决这个异常,可以在保存student前先保存一下class,或者是通过指定cascade属性来完成级联保存student的对象同时会把关联的瞬时状态的对象class也保存。

        <many-to-one name="clazz" class="ecut.manytoone.entity.Clazz" column="class_id"  lazy="proxy" cascade="all" />

转载请于明显处标明出处:

https://www.cnblogs.com/AmyZheng/p/9314462.html

posted @ 2018-07-15 18:36  AmyZheng  阅读(101)  评论(0编辑  收藏  举报