一、 简介
Hibernate的映射类型(最常用为一对多和多对一映射):
一对多 (one-to-many)
多对一 (many-to-one)
一对一 (one-to-one)
多对多 (many-to-many)
二、单向一对多关联
1. 定义
就像班级和学生。站在班级的角度看。班级和学生就是一对多的关系,班级为一方,学生为多方且是单向的。
如何实现这种关系?
1.在数据库中,可以通过添加主外键的关联,表现一对多的关系;
2.在java中,通过在一方持有多方的集合实现,即在“一”的一端中使用<set>元素表示持有“多”的一端的对象
2. 以班级和学生关联为例
(1) 创建工程,导入hibernate和数据库驱动相关jar包
(2) 配置hibernate.cfg.xml文件
(3) 创建HibernateUtil工具类,用以获取会话工厂,会话的创建和关闭
Tips:不同的hibernate版本,会话的获取方式不一样,需要注意
package com.imooc.util;
import com.imooc.entity.Grade;
import com.imooc.entity.Student;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
Configuration config = new Configuration().configure(); // 创建配置对象
config.addClass(Grade.class);
config.addClass(Student.class);
ServiceRegistry serviceRegistery = new StandardServiceRegistryBuilder().applySettings(config.getProperties()).build();
return config.buildSessionFactory(serviceRegistery);
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Session getSession() {return sessionFactory.openSession();}
//关闭Session对象
public static void closeSession(Session session){
if(session!=null){
session.close();
sessionFactory.close();
}
}
}
(4) MySQL数据库中建表
create table grade
(
gid int primary key,
gname varchar(20) not null,
gdesc varchar(50)
);
create table student
(
sid int primary key,
sname varchar(20) not null,
gender varchar(2) ,
gid int
);
alter table student add constraint fk_student_gid foreign key (gid)
references grade(gid);//对应表主键
Tips: Intellij IDEA集成的hibernate会自行建表,这一步可省略;但是根据实体类自动建表的话,就必须在Student实体类中定义Grade grade属性,否则因为建立关联时Student数据表中找不到gid这一项,会报错
(5) 创建持久化类和映射文件
Grade.java
package com.imooc.entity;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
public class Grade implements Serializable{
private int gid;
private String gname;
private String gdesp;
private Set<Student> students = new HashSet<>(); //一方持有多方的集合
/**getter和setter方法**/
{...}
public Grade() {
}
public Grade(String gname, String gdesp) {
this.gname = gname;
this.gdesp = gdesp;
}
public Grade(int gid, String gname, String gdesp, Set<Student> students) {
this.gid = gid;
this.gname = gname;
this.gdesp = gdesp;
this.students = students;
}
}
Student.java
package com.imooc.entity;
import java.io.Serializable;
public class Student implements Serializable {
private int sid;
private String sname;
private String gender;
//如果选择hibernate自动创建数据库表,这个属性不能漏掉
private Grade grade;
public Student(int sid, String sname, String gender) {
this.sid = sid;
this.sname = sname;
this.gender = gender;
}
public Student() {
}
public Student(String sname, String gender) {
this.sname = sname;
this.gender = gender;
}
}
Grade.hbm.xml
<?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="com.imooc.entity.Grade" table="grade">
<id name="gid" type="int">
<column name="gid" />
<generator class="increment" />
</id>
<property name="gname" type="java.lang.String">
<column name="gname" />
</property>
<property name="gdesp" type="java.lang.String">
<column name="gdesp" />
</property>
<set name="students" table="student">
<key column="gid"></key>
<one-to-many class="com.imooc.entity.Student" />
</set>
</class>
</hibernate-mapping>
Tips:<set>中key指定的是grade(被参照表)的主键,one-to-many指定了Set中元素(参照元素)的类型
Student.hbm.xml
<?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="com.imooc.entity.Student" table="student">
<id name="sid" type="int">
<column name="sid" />
<generator class="increment" />
</id>
<property name="sname" type="java.lang.String">
<column name="sname" length="20" not-null="true"/>
</property>
<property name="gender" type="java.lang.String">
<column name="gender" />
</property>
</class>
</hibernate-mapping>
Tips: 最后需要在hibernate.cfg.xml中配置<mapping resource>
(6) 测试-增删改查
package com.imooc.entity;
import com.imooc.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
import sun.security.ssl.HandshakeInStream;
import java.util.Set;
/**
* 单向一对多关联关系,班级(一方) --- > 学生(多方)
* 建立关联关系后,可以方便地从一个对象导航到另一个对象
* 注意关联的方向
*/
public class Test {
public static void main(String[] args) {
//add();
//getStudentsByGrade();
//update();
delete();
}
//将学生添加到班级
public static void add() {
Grade g = new Grade("生物一班", "班主任:钱**");
Student stu1 = new Student("李明", "男");
Student stu2 = new Student("刘丹", "女");
//如果希望在学生表中添加对应的班级编号,需要在班级中添加学生,建立关联关系
g.getStudents().add(stu1);
g.getStudents().add(stu2);
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
session.save(g);
session.save(stu1);
session.save(stu2);
tx.commit();
HibernateUtil.closeSession(session);
}
//通过班级获取学生
public static void getStudentsByGrade(){
Session session = HibernateUtil.getSession();
Grade grade = (Grade) session.get(Grade.class, 1);
System.out.println(grade.getGname() + " " + grade.getGdesp());
Set<Student> students = grade.getStudents();
for (Student stu :students) {
System.out.println(stu.getSname() + " " + stu.getGender());
}
HibernateUtil.closeSession(session);
}
//修改学生信息
public static void update() {
Grade g = new Grade("生物二班", "班主任:王**");
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Student stu = session.get(Student.class, 1);
g.getStudents().add(stu);
session.save(g);
tx.commit();
HibernateUtil.closeSession(session);
}
//删除学生信息
public static void delete() {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Student stu = (Student) session.get(Student.class, 2);
session.delete(stu);
tx.commit();
HibernateUtil.closeSession(session);
}
}
执行输出日志如下:
Hibernate: alter table student add constraint FKrahcsicxucn7bhee8empkb42c foreign key (gid) references grade (gid)
Hibernate: select max(gid) from grade
Hibernate: select max(sid) from student
Hibernate: insert into grade (gname, gdesp, gid) values (?, ?, ?)
Hibernate: insert into student (sname, gender, sid) values (?, ?, ?)
Hibernate: insert into student (sname, gender, sid) values (?, ?, ?)
Hibernate: update student set gid=? where sid=?
Hibernate: update student set gid=? where sid=?
Tips:1.出现数据表插入中文为乱码的情况时,可以简单地修改数据库编码方式为utf8:LTER DATABASE hibernateTest CHARACTER SET utf8;或者修改MySQL默认字符集为utf8;同时hibernate.cfg.xml的connection.url配置中需要设置编码方式:jdbc:mysql://127.0.0.1:3306/hibernateTest?useUnicode=true&characterEncoding=utf8
2.在"一"保存"多"的集合创建一对多关系:
a.在插入"一"的数据时,首先将"一"的元素(除了建立一对多关系的集合)添加到对应表项,然后根据集合元素到"多"对应的表中更新"多"的参照数据(集合只用于更新"多"的数据,而不会作为"一"的数据保存)
b.可以直接(不用二次查表)通过"一"得到相应的"多",反之不行(导航查询要注意方向:一到多)
(7) set元素的常用属性
属性 | 含义和作用 | 是否必须 | 默认值 |
---|---|---|---|
name | 映射类属性的名称 | Y | |
table | 关联类的目标数据库 | N | |
lazy | 指定关联对象是否使用延迟加载 | N | proxy |
inverse | 表示双向关联中被动的一方 | N | false |
三、单向多对一关联
1. 简介
1.多对一的关系和关系数据库中的外键参照关系最匹配,即在己方的表中的一个外键参照另一个表的主键;以班级和学生为例,从学生的角度看,学生就是多方,班级是一方
2.通过在多方持有一方的引用实现,需要在“多”的一端(此处为学生端)使用<many-to-one>配置
2. 配置
<!-- 配置多对一关联关系-->
<many-to-one name="grade" class="com.imooc.entity.Grade" column="gid"/>
3. 测试单向多对一
package com.imooc.entity;
import com.imooc.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
/**
* 单向多对一 (学生--->班级)
*/
public class Test02 {
public static void main(String[] args) {
save();
}
public static void save() {
Grade grade = new Grade("化学一班", "班主任:林**");
Student stu1 = new Student("任盈盈", "女");
Student stu2 = new Student("令狐冲", "男");
stu1.setGrade(grade);
stu2.setGrade(grade);
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
session.save(grade);
session.save(stu1);
session.save(stu2);
tx.commit();
HibernateUtil.closeSession(session);
}
}
Hibernate: alter table student add constraint FKrahcsicxucn7bhee8empkb42c foreign key (gid) references grade (gid)
Hibernate: select max(gid) from grade
Hibernate: select max(sid) from student
Hibernate: insert into grade (gname, gdesp, gid) values (?, ?, ?)
Hibernate: insert into student (sname, gender, gid, sid) values (?, ?, ?, ?)
Hibernate: insert into student (sname, gender, gid, sid) values (?, ?, ?, ?)
四、双向多对一关联
1. 修改实体类及hbm文件配置
a. 分别在持久化类中定义另一方的引用;
一方:private Set<Student>students=new HashSet<Student>(),并setter、getter
多方:private Grade grade,并setter、getter;
b. 在一方hbm文件配置一对多关联,在多方配置多对一关联关系
一方配置:
<set name="一方实体类定义的多方的引用,即多方属性Students" table="多方的表名">
<key column="关联外键的列gid"></key>
<one-to-many class="多方实体类的全类名com.imooc.entity.Student" ></one>
</set>
多方配置:
<many-to-one name="指定对应的属性的名称" class="对应实体类的全类名com.imooc.entity.Grade" column="指定 外键的列名gid" ></many-to-one>
2. 测试
public static void save() {
Grade grade = new Grade("化学一班", "班主任:林**");
Student stu1 = new Student("任盈盈", "女");
Student stu2 = new Student("令狐冲", "男");
//设置关联关系
grade.getStudents().add(stu1);
grade.getStudents().add(stu2);
stu1.setGrade(grade);
stu2.setGrade(grade);
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
session.save(grade);
session.save(stu1);
session.save(stu2);
tx.commit();
HibernateUtil.closeSession(session);
}
Hibernate: alter table student add constraint FKrahcsicxucn7bhee8empkb42c foreign key (gid) references grade (gid)
Hibernate: select max(gid) from grade
Hibernate: select max(sid) from student
Hibernate: insert into grade (gname, gdesp, gid) values (?, ?, ?)
Hibernate: insert into student (sname, gender, gid, sid) values (?, ?, ?, ?)
Hibernate: insert into student (sname, gender, gid, sid) values (?, ?, ?, ?)
Hibernate: update student set gid=? where sid=?
Hibernate: update student set gid=? where sid=?
3. inverse属性
1.<set>节点的inverse属性指定关联关系的控制方向,默认由one方来维护;
2.关联关系中,inverse="false"(默认值)则为主动方,由主动方负责维护关联关系;
3.在一对多关联中,只能设置one方的inverse为true,这将有助于性能的改善。
4. cascade属性:
1、当设置了cascade属性不为none时,Hibernate会自动持久化所关联的对象;
2、cascade属性的设置会带来性能上的变动,需要谨慎设置;
属性值 | 含义和作用 |
---|---|
all | 对所有操作进行级联操作 |
save-update | 执行保存和更新操作时进行级联操作 |
delete | 执行删除时进行级联操作 |
none | 所有操作都不执行级联操作 |
2.在Student的映射文件中的<many-to-one>节点添加cascade属性为"save-update",那么在设置Student对象的Grade属性后,保存Student对象时,会级联操作,保存Grade对象
五、总结
实现单向一对多:
1)在one方的实体中添加保存many方的集合
2)在one方的配置文件中添加<one-to-many>配置
实现单向多对一:
1)在many方的实体中添加one方的引用
2)在many方的配置文件中添加<many-to-one>配置
常用属性:
cascade:设置级联关系
all:对所有操作进行级联操作
save-update:执行保存和更新操作时进行级联操作
delete:执行删除操作时进行级联操作
none:对所有操作不进行级联操作】
inverse:设置由哪一方维护关联关系;inverse="false"则为主动方,由主动方负责维护关联关系