一对多测试案例
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.onetomany.entity; import java.io.Serializable; import java.util.Set; public class Clazz implements Serializable { private static final long serialVersionUID = 235617966763001653L; private Integer id; private String name; // 当前的班级都有那些学生 private Set<Student> students; // 维护从 班级( one ) 到 学生( many ) 的 一对多 关联关系 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 Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } }
班级类中使用students来使班级和学生相关联
学生类:
package ecut.onetomany.entity; import java.io.Serializable; public class Student implements Serializable { private static final long serialVersionUID = 399214402506858645L; private Integer id; private String name; public Student() { super(); } public Student(String name) { super(); this.name = 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; } }
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.onetomany.entity.Clazz" table="t_class"> <id name="id" type="integer" column="id" > <generator class="increment" /> </id> <property name="name" type="string" column="name" /> <!-- 使用 set 标记 映射 Set 集合 , name 属性指定 实体类中的 Set 集合的名称,order-by 所对应的表中的列名 --> <set name="students" order-by="id ASC" cascade="all" > <!-- 使用 key 的 column 属性指定 对方 ( many 一方 ) 表中 参照 当前表 ( t_class ) 的主键的那个外键的名称 --> <key column="class_id" /> <!-- 指定 对方 ( many 一方 ) 的类型 ( Set 集合中存放的元素的类型 ) --> <one-to-many class="ecut.onetomany.entity.Student" /> </set> </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.onetomany.entity.Student" table="t_student"> <id name="id" type="integer" column="id" > <generator class="increment" /> </id> <property name="name" type="string" column="name" /> </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&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/onetomany/entity/Clazz.hbm.xml"/> <mapping resource="ecut/onetomany/entity/Student.hbm.xml"/> </session-factory> </hibernate-configuration>
6、测试类
从数据库中获取一个班级对象,之后获得所属学生的集合:
package ecut.onetomany.test; import java.util.HashSet; import java.util.Set; 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.onetomany.entity.Clazz; import ecut.onetomany.entity.Student; public class TestOneToMany { private SessionFactory factory ; private Session session ; public @Before void init() { // 创建一个 Configuration 对象,用来读取配置文件 ( 默认位置、默认名称 ) Configuration config = new Configuration(); // 读取配置文件 config.configure("ecut/onetomany/hibernate.cfg.xml"); //config.configure(); // 读取 默认位置 ( 当前 classpath ) 的 默认名称 ( hibernate.cfg.xml ) 的配置文件 // 使用 Configuration 创建 SessionFactory factory = config.buildSessionFactory(); // 创建 Session 对象 ( 这里的 Session 是 Java 程序 跟 数据库 之间的 会话 ) session = factory.openSession(); } public @Test void loadClazz(){ Clazz c = session.find( Clazz.class , 2 ); if( c == null ){ System.out.println( "没有找到对应的班级" ); } else { System.out.println( c.getId() + " : " + c.getName() ); Set<Student> students = c.getStudents(); // <set name="students" order-by="id DESC" > System.out.println( students.getClass() ); if( students == null || students.isEmpty() ) { System.out.println( c.getName() + "还没有学生"); } else { for( Student s : students ){ System.out.println( " " + s.getId() + " : " + s.getName() ); } } } }public @After void destory(){ session.close(); factory.close(); } }
班级持久化类中的set是PersistentSet类其中可以排序,可以在班级映射文件中的set标签中指定order-by属性来给学生排序。
<!-- 使用 set 标记 映射 Set 集合 , name 属性指定 实体类中的 Set 集合的名称,order-by 所对应的表中的列名 --> <set name="students" order-by="id ASC" cascade="all" >
保存一个新创建的班级对象到数据库,并为其添加学生,将学生一起保存:
package ecut.onetomany.test; import java.util.HashSet; import java.util.Set; 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.onetomany.entity.Clazz; import ecut.onetomany.entity.Student; public class TestOneToMany { private SessionFactory factory ; private Session session ; public @Before void init() { // 创建一个 Configuration 对象,用来读取配置文件 ( 默认位置、默认名称 ) Configuration config = new Configuration(); // 读取配置文件 config.configure("ecut/onetomany/hibernate.cfg.xml"); //config.configure(); // 读取 默认位置 ( 当前 classpath ) 的 默认名称 ( hibernate.cfg.xml ) 的配置文件 // 使用 Configuration 创建 SessionFactory factory = config.buildSessionFactory(); // 创建 Session 对象 ( 这里的 Session 是 Java 程序 跟 数据库 之间的 会话 ) session = factory.openSession(); } public @Test void saveClazz(){ Clazz c = new Clazz(); c.setName( "华山派" ); Set<Student> students = new HashSet<>(); Student s1 = new Student( "令狐冲" ) ; students.add( s1 ); Student s2 = new Student( "岳灵珊" ) ; students.add( s2 ); Student s3 = new Student( "陆大有" ) ; students.add( s3 ); Student s4 = new Student( "林平之" ) ; students.add( s4 ); c.setStudents( students ); // 确定 班级中都有哪些 学生 session.getTransaction().begin(); session.save( c ); // <set name="students" cascade="all" > session.getTransaction().commit(); } public @After void destory(){ session.close(); factory.close(); } }
运行结果:
Hibernate: select max(id) from t_class
Hibernate: select max(id) from t_student
Hibernate: insert into t_class (name, id) values (?, ?)
Hibernate: insert into t_student (name, id) values (?, ?)
Hibernate: insert into t_student (name, id) values (?, ?)
Hibernate: insert into t_student (name, id) values (?, ?)
Hibernate: insert into t_student (name, id) values (?, ?)
Hibernate: update t_student set class_id=? where id=?
Hibernate: update t_student set class_id=? where id=?
Hibernate: update t_student set class_id=? where id=?
Hibernate: update t_sent set class_id=? where id=?
是先保存class然后再保存student,并通过update语句来指定班级号。
若没有在映射文件中的set标签指定cascade属性则会抛出java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing:的异常,为了解决这个异常,可以在保存class前先保存一下student,或者是通过指定cascade属性来完成级联保存class的对象同时会把关联的瞬时状态的对象student也保存。
<!-- 使用 set 标记 映射 Set 集合 , name 属性指定 实体类中的 Set 集合的名称,order-by 所对应的表中的列名 --> <set name="students" order-by="id ASC" cascade="all" >
转载请于明显处标明出处: