##概述
在开发中经常会遇到多表操作,Hibernate支持多表关联的操作。
数据库表的关联关系分为三种:
- 一对一,
- 一对多,
- 多对多,
下面以“一对多”的栗子讲述Hibernate对多表的操作。
##准备工作
该博客继承上个博客,继续使用student表,再创建一个score表,student表与score是“一对多”的关系,即一个学生可以有多个分数。
创建score表的sql语句如下:
create table score(
id int primary key auto_increment,
student_id int,
score int,
type varchar(20),
foreign key(student_id) references student(id)
);
注:必须指定score表的student_id字段是student表的外键。
##开发环境
见Java操作数据库方式三Hibernate的使用之多表操作。
##正式开发
一,创建Model类
student的Model类如下:
public class Student {
private int id;
private String name;
private int age;
private Set<Score> score = new HashSet<>();//此处使用集合,一个Student对应多个Score
//set和get方法略
}
score的Model类如下:
public class Score{
private int id;
private int score;
private String type;
private Student student;
//set和get方法略
}
二,创建对象与表结构映射关系文件
对student.hbm.xml进行修改,修改之后的文件内容是:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!--
class用来描述一个类
name 类的全名
table 该持久化类对应的表名 可以不写,默认值为类名
lazy 此时设置为false,表示不使用懒加载
-->
<class name="com.honor.sql.Student" table="student" lazy="false">
<!--
Student类字段与表结构字段对应关系
name 属性的名称
column 属性的名称对应的表的字段 可以不写 默认值就是属性的名称
length 属性的名称对应的表的字段的长度 如果不写,默认是最大的长度
-->
<id name="id" column="id" length="5">
<!-- 主键的产生器 -->
<generator class="increment"></generator>
</id>
<property name="name" column="name" length="20" ></property>
<property name="age" column="age" length="50" ></property>
<set name="score" fetch="join" >
<key column="student_id"></key>
<one-to-many class="com.honor.sql.Score"/>//
</set>
</class>
</hibernate-mapping>
创建score对应的映射文件score.hbm.xml,文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!--
class用来描述一个类
name 类的全名
table 该持久化类对应的表名 可以不写,默认值为类名
lazy 此时设置为false,表示不使用懒加载
-->
<class name="com.honor.sql.Score" table="score" lazy="false">
<!--
Student类字段与表结构字段对应关系
name 属性的名称
column 属性的名称对应的表的字段 可以不写 默认值就是属性的名称
length 属性的名称对应的表的字段的长度 如果不写,默认是最大的长度
-->
<id name="id" column="id" length="5">
<!-- 主键的产生器 -->
<generator class="increment"></generator>
</id>
<property name="score" column="score" length="50" ></property>
<property name="type" column="type" length="50" ></property>
<many-to-one name="student" class="com.honor.sql.Student" column="student_id"/>
</class>
</hibernate-mapping>
三,创建hibernate框架的全局配置文件
在hibernate.cfg.xml中添加score.hbm.xml的引用
<!--加载model类与表结构映射关系文件-->
<mapping resource="student.hbm.xml" />
<mapping resource="score.hbm.xml" />
四,得到Session对象
/**
* 根据配置文件获取Session对象
*
* @return
*/
public static Session getSession() {
//加载hibernate全局配置文件
Configuration cfg = new Configuration().configure("/hibernate.cfg.xml");
SessionFactory factory = cfg.buildSessionFactory();
//得到Session对象,所有的数据库操作都是通过Session对象来完成
Session session = factory.openSession();
return session;
}
五,插入操作
public static boolean insertStudentAndScore() {
//获取Session对象
Session session = getSession();
try {
//开始事务
session.beginTransaction();
Student student = new Student();
student.setName("zhaomin");
student.setAge(18);
Score score1 = new Score();
score1.setType("yuwen");
score1.setScore(123);
Score score2 = new Score();
score2.setType("yingyu");
score2.setScore(150);
student.getScore().add(score1);
student.getScore().add(score2);
score1.setStudent(student);
score2.setStudent(student);
session.save(student);//保存student对象
session.save(score1);//保存score1
session.save(score2);//保存score2
//提交事务
session.getTransaction().commit();
return true;
} catch (Exception e) {
e.printStackTrace();
//出现异常时回滚事务
session.getTransaction().rollback();
} finally {
if (session != null) {
if (session.isOpen()) {
session.close();//关闭session
}
}
}
return false;
}
此时查看执行日志发现共执行了5次sql语句:
六,根据id查找单个对象
public static Student selectStudent(int id) {
//获取Session对象
Session session = getSession();
try {
session.beginTransaction();
//根据id查询Student对象
Student student = session.load(Student.class, id);
session.getTransaction().commit();
return student;
} catch (Exception e) {
e.printStackTrace();
//出现异常时回滚事务
session.getTransaction().rollback();
} finally {
if (session != null) {
if (session.isOpen()) {
session.close();//关闭session
}
}
}
return null;
}
注:此时代码与单表操作时相同,但结果不同,结果如下:
注:此时把student对应的core对象都查出来了。这是将lazy设置为false的坏处,如果一个学生关联了万条score将会非常占用内存。
七,根据其他条件得到对象集合
查询yingyu分数为150的学生:
public static List<Score> selectScoreList() {
//获取Session对象
Session session = getSession();
try {
session.beginTransaction();
//定义hql语句,类似于sql语句,注意:此时Student是对象,并不是表名。在student.hbm.xml文件中指定Student类对应student表
String hql = "from Score s where s.score = 150 and s.type = 'yingyu'";
//String hql = " from Student s,Score c ";
Query query = session.createQuery(hql);
List scoreList = query.list();
session.getTransaction().commit();
return scoreList;
} catch (Exception e) {
e.printStackTrace();
//出现异常时回滚事务
session.getTransaction().rollback();
} finally {
if (session != null) {
if (session.isOpen()) {
session.close();//关闭session
}
}
}
return null;
}
结果如下:
##总结
使用Hibernate操作多表自由度很低,如果对多表查询较多建议使用MyBatis框架。