Hibernate的关联映射:
多对一关联映射:
第一步: 创建实体模型
Emp类:
package com.rl.hiber.model;
import java.util.Date;
public class Emp {
private Integer empNo;
private String ename;
private Date birthday;
private Integer gender;
private String address;
/**
* 引入一的一端作为属性, 建立多对一的关系
*/
private Team team;
public Integer getEmpNo() {
return empNo;
}
public void setEmpNo(Integer empNo) {
this.empNo = empNo;
}
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
this.team = team;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Team类:
package com.rl.hiber.model;
public class Team {
private Integer tId;
private String tName;
private String loc;
public Integer gettId() {
return tId;
}
public void settId(Integer tId) {
this.tId = tId;
}
public String gettName() {
return tName;
}
public void settName(String tName) {
this.tName = tName;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
第二步: 配置实体映射文件
Emp.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Emp" table="t_emp">
<id name="empNo" column="emp_no">
<generator class="native"></generator>
</id>
<property name="ename"></property>
<property name="birthday"></property>
<property name="gender"></property>
<property name="address"></property>
<!-- 指定多对一的关联映射
name: 多的一端中定义的一的一端的属性, 也就是team
column: 多的一端中的外键, 也就是一的一端中的主键, t_id
-->
<many-to-one name="team" column="t_id">
</many-to-one>
</class>
</hibernate-mapping>
Team.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Team" table="t_team">
<id name="tId" column="t_id">
<generator class="native"></generator>
</id>
<property name="tName" column="t_name"></property>
<property name="loc"></property>
</class>
</hibernate-mapping>
hibernate.cfg.xml:
生成建表脚本:
数据库建表结果:
第三步: 测试多对一关联映射
测试代码:
package com.rl.hiber.test;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.rl.hiber.model.Emp;
import com.rl.hiber.model.Team;
import com.rl.hiber.utils.HibernateUtil;
public class TestHibernate {
@Test
public void test1() {
Session session = HibernateUtil.getSessoion();
Transaction tx = session.beginTransaction();
try {
Team team = new Team();
team.settName("公牛队");
team.setLoc("芝加哥");
//保存球队(要先保存球队, 否则是瞬时对象)
session.save(team);
Emp emp1 = new Emp();
Emp emp2 = new Emp();
emp1.setEname("乔丹");
emp1.setAddress("芝加哥");
emp1.setGender(1);
emp1.setBirthday(new Date());
//设置队员与球队之间的关系
emp1.setTeam(team);
//保存队员
session.save(emp1);
emp2.setAddress("芝加哥");
emp2.setBirthday(new Date());
emp2.setEname("菲尔");
emp2.setGender(1);
emp2.setTeam(team);
session.save(emp2);
//提交事务
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtil.closeResource(session);
}
}
}
hibernate发出sql语句:
数据库结果:
下面讲解一个知识点:
级联: 在"多的一端"设置cascade属性
直接贴代码吧:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Emp" table="t_emp">
<id name="empNo" column="emp_no">
<generator class="native"></generator>
</id>
<property name="ename"></property>
<property name="birthday"></property>
<property name="gender"></property>
<property name="address"></property>
<!-- 指定多对一的关联映射
name: 多的一端中定义的一的一端的属性, 也就是team
column: 多的一端中的外键, 也就是一的一端中的主键, t_id
cascade: delete(删除多的一端会直接上一的一端也删除, 即 删除队员也会把球队给删掉), 在多的一端不建议使用
save-update(保存或者更新多的一端时, 一的一端也会自动先做保存或更新, 即 不需要先保存球队), 建议使用
all:包含以上两种
none: 没有级联(默认状态)
-->
<many-to-one name="team" column="t_id" cascade="save-update">
</many-to-one>
</class>
</hibernate-mapping>
测试级联状态为save-update代码:
package com.rl.hiber.test;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.rl.hiber.model.Emp;
import com.rl.hiber.model.Team;
import com.rl.hiber.utils.HibernateUtil;
public class TestHibernate {
/**
* 将级联状态设置为save-update时不需要先保存球队(一的一端)
*/
@Test
public void test2() {
Session session = HibernateUtil.getSessoion();
Transaction tx = session.beginTransaction();
try {
Team team = new Team();
team.settName("小牛队");
team.setLoc("达拉斯");
Emp emp1 = new Emp();
Emp emp2 = new Emp();
emp1.setEname("库班");
emp1.setAddress("达拉斯");
emp1.setGender(1);
emp1.setBirthday(new Date());
//设置队员与球队之间的关系
emp1.setTeam(team);
//保存队员
session.save(emp1);
emp2.setAddress("达拉斯");
emp2.setBirthday(new Date());
emp2.setEname("诺维斯基");
emp2.setGender(1);
emp2.setTeam(team);
session.save(emp2);
//提交事务
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtil.closeResource(session);
}
}
}
hibernate发出sql语句:
数据库结果:
一对一单端关联映射:
映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="IDCard" table="t_id_card">
<!--
指定idcard的主键名称, 建议跟emp端的主键名称相同
-->
<id name="cardId" column="emp_no">
<!--
使用外键的方式来生成主键
-->
<generator class="foreign">
<!--
指定idcard这一端的emp属性
-->
<param name="property">emp</param>
</generator>
</id>
<property name="cardNo" column="card_no"></property>
<!-- 一对一关联映射
name: 指定idcard这一端中定义的另一个一的一端的属性(即emp)
constrained: true(即建表外键约束), 当要删除队员信息时必须先删除idcard
-->
<one-to-one name="emp" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
测试代码:
package com.rl.hiber.test;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.rl.hiber.model.Emp;
import com.rl.hiber.model.IDCard;
import com.rl.hiber.utils.HibernateUtil;
public class TestHibernate {
@Test
public void testAdd() {
Session session = HibernateUtil.getSessoion();
Transaction tx = session.beginTransaction();
try {
Emp emp = new Emp();
emp.setAddress("广州");
emp.setBirthday(new Date());
emp.setEname("张三");
emp.setGender(1);
IDCard card = new IDCard();
card.setCardNo("111");
card.setEmp(emp);
//此时并没有设置级联, 但hibernate会自动在保存idcard时先保存emp
//因为如果不保存emp则无法返回主键, 它们俩是共用主键的以保证一对一关系
session.save(card);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtil.closeResource(session);
}
}
}
数据库结果:
一对一双向映射
Emp表的配置文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Emp" table="t_emp">
<id name="empNo" column="emp_no">
<generator class="native"></generator>
</id>
<property name="ename"></property>
<property name="birthday"></property>
<property name="gender"></property>
<property name="address"></property>
<!-- 指定一对一关联映射关系 -->
<one-to-one name="card"></one-to-one>
</class>
</hibernate-mapping>
测试代码:
package com.rl.hiber.test;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.rl.hiber.model.Emp;
import com.rl.hiber.model.IDCard;
import com.rl.hiber.utils.HibernateUtil;
public class TestHibernate {
@Test
public void testAdd() {
Session session = HibernateUtil.getSessoion();
Transaction tx = session.beginTransaction();
try {
Emp emp = new Emp();
emp.setAddress("北京");
emp.setBirthday(new Date());
emp.setEname("李四");
emp.setGender(1);
IDCard card = new IDCard();
card.setCardNo("123");
emp.setCard(card);
card.setEmp(emp);
session.save(emp);
session.save(card);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtil.closeResource(session);
}
}
}
数据库结果同上的一对一单端映射
一对多单向关联映射:
在多对一的基础上修改:
Emp配置文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Emp" table="t_emp">
<id name="empNo" column="emp_no">
<generator class="native"></generator>
</id>
<property name="ename"></property>
<property name="birthday"></property>
<property name="gender"></property>
<property name="address"></property>
</class>
</hibernate-mapping>
Team配置文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Team" table="t_team">
<id name="tId" column="t_id">
<generator class="native"></generator>
</id>
<property name="tName" column="t_name"></property>
<property name="loc"></property>
<!-- 指定一对多关联映射
name: 在一的一端指定一个多的一端的集合属性emps
key:->column: 多的一端里面有一个一的一端的外键t_id
one-to-many:->class: 多的一端所在的位置(默认已经指定了com.rl.hiber.model)
-->
<set name="emps">
<key column="t_id"></key>
<one-to-many class="Emp"/>
</set>
</class>
</hibernate-mapping>
测试代码:
package com.rl.hiber.test;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.rl.hiber.model.Emp;
import com.rl.hiber.model.Team;
import com.rl.hiber.utils.HibernateUtil;
public class TestHibernate {
@Test
public void testAdd() {
Session session = HibernateUtil.getSessoion();
Transaction tx = session.beginTransaction();
try {
Emp emp = new Emp();
emp.setAddress("芝加哥");
emp.setBirthday(new Date());
emp.setEname("乔丹");
emp.setGender(1);
//保存队员
session.save(emp);
Emp emp1 = new Emp();
emp1.setAddress("芝加哥");
emp1.setBirthday(new Date());
emp1.setEname("菲尔");
emp1.setGender(1);
//
session.save(emp1);
Set<Emp> emps = new HashSet<Emp>();
emps.add(emp);
emps.add(emp1);
Team team = new Team();
team.setLoc("芝加哥");
team.settName("公牛队");
//指定一对多的关系
team.setEmps(emps);
//保存球队
session.save(team);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtil.closeResource(session);
}
}
}
数据库结果:
设置级联:
测试代码:
package com.rl.hiber.test;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.rl.hiber.model.Emp;
import com.rl.hiber.model.Team;
import com.rl.hiber.utils.HibernateUtil;
public class TestHibernate {
@Test
public void testAdd() {
Session session = HibernateUtil.getSessoion();
Transaction tx = session.beginTransaction();
try {
Emp emp = new Emp();
emp.setAddress("达拉斯");
emp.setBirthday(new Date());
emp.setEname("库班");
emp.setGender(1);
Emp emp1 = new Emp();
emp1.setAddress("达拉斯");
emp1.setBirthday(new Date());
emp1.setEname("诺维斯基");
emp1.setGender(1);
Set<Emp> emps = new HashSet<Emp>();
emps.add(emp);
emps.add(emp1);
Team team = new Team();
team.setLoc("达拉斯");
team.settName("小牛队");
team.setEmps(emps);
//保存球队时就会自动保存队员
session.save(team);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtil.closeResource(session);
}
}
}
数据库结果:
一对多双向映射:
一对多是有缺陷的, 保存的时候如果多的一端的外键是非空字段, 那么保存就会出错.
跟多对一类似, 有点小区别:
需要在一对多的基础上再在多的一端指定多对一的关系, 就不贴代码了
Emp.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Emp" table="t_emp">
<id name="empNo" column="emp_no">
<generator class="native"></generator>
</id>
<property name="ename"></property>
<property name="birthday"></property>
<property name="gender"></property>
<property name="address"></property>
<!-- 指定多对一的关联映射
name: 多的一端中定义的一的一端的属性, 也就是team
column: 多的一端中的外键, 也就是一的一端中的主键, t_id
这个外键必须跟team.hbm.xml指定的外键一样, 如果两个column的名称不一样则相当于有两个外键就冲突了
-->
<many-to-one name="team" column="t_id" />
</class>
</hibernate-mapping>
Team.hbm.xml同上
测试代码同多对一.
真正的解决非空字段的方法, 如果真的有需求需要在一的一端进行保存的话, 可以在Team.hbm.xml设置反转:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Team" table="t_team">
<id name="tId" column="t_id">
<generator class="native"></generator>
</id>
<property name="tName" column="t_name"></property>
<property name="loc"></property>
<!-- 指定一对多关联映射
name: 在一的一端指定一个多的一端的集合属性emps
key:->column: 多的一端里面有一个一的一端的外键t_id
one-to-many:->class: 多的一端所在的位置(默认已经指定了com.rl.hiber.model)
inverser: 设置反转, 表面上用一的一端进行保存, 实际上反转给多的一端进行保存
-->
<set name="emps" cascade="save-update" inverse="true">
<key column="t_id"></key>
<one-to-many class="Emp"/>
</set>
</class>
</hibernate-mapping>
测试代码:
package com.rl.hiber.test;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.rl.hiber.model.Emp;
import com.rl.hiber.model.Team;
import com.rl.hiber.utils.HibernateUtil;
public class TestHibernate {
@Test
public void testAdd2() {
Session session = HibernateUtil.getSessoion();
Transaction tx = session.beginTransaction();
try {
Team team = new Team();
team.setLoc("达拉斯");
team.settName("小牛队");
Emp emp = new Emp();
emp.setAddress("达拉斯");
emp.setBirthday(new Date());
emp.setEname("库班");
emp.setGender(1);
//利用队员指定多对一
emp.setTeam(team);
Emp emp1 = new Emp();
emp1.setAddress("达拉斯");
emp1.setBirthday(new Date());
emp1.setEname("诺维斯基");
emp1.setGender(1);
emp1.setTeam(team);
Set<Emp> emps = new HashSet<Emp>();
emps.add(emp);
emps.add(emp1);
team.setEmps(emps);
//只保存球队, 实际上会反转给多的一端进行保存
session.save(team);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtil.closeResource(session);
}
}
}
结果相同
单端多对多关联映射:
Emp类需要加上另一端的属性, 并提供set跟get方法, 就不贴代码了
配置文件:
Emp.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Emp" table="t_emp">
<id name="empNo" column="emp_no">
<generator class="native"></generator>
</id>
<property name="ename"></property>
<property name="birthday"></property>
<property name="gender"></property>
<property name="address"></property>
<!-- 指定多对多关系
table: 中间表
-->
<set name="roles" table="emp_role">
<!-- emp端的外键, 也就是emp_no主键 -->
<key column="emp_no"></key>
<!--
class: 另一端的路径
column: 另一端的外键, 也就是role_id主键
-->
<many-to-many class="Role" column="role_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
Role.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Role" table="t_role">
<id name="roleId" column="role_id">
<generator class="native"></generator>
</id>
<property name="roleName" column="role_name"></property>
<property name="roleDesc" column="role_desc"></property>
</class>
</hibernate-mapping>
生成数据库表:
测试代码:
package com.rl.hiber.test;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.rl.hiber.model.Emp;
import com.rl.hiber.model.Role;
import com.rl.hiber.utils.HibernateUtil;
public class TestHibernate {
@Test
public void testAdd() {
Session session = HibernateUtil.getSessoion();
Transaction tx = session.beginTransaction();
try {
Emp emp = new Emp();
emp.setAddress("上海");
emp.setBirthday(new Date());
emp.setEname("田七");
emp.setGender(1);
Role role1 = new Role();
role1.setRoleDesc("讲课");
role1.setRoleName("老师");
session.save(role1);
Role role2 = new Role();
role2.setRoleDesc("管理");
role2.setRoleName("总监");
session.save(role2);
Set<Role> roles = new HashSet<Role>();
roles.add(role1);
roles.add(role2);
emp.setRoles(roles);
session.save(emp);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtil.closeResource(session);
}
}
}
数据库结果:
同样也可以设置级联, 就不举例了.
多对多的双向关联:
Role类需要加上另一端的属性, 并提供set跟get方法
在原来的基础上修改Role.hbm.xml,
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Role" table="t_role">
<id name="roleId" column="role_id">
<generator class="native"></generator>
</id>
<property name="roleName" column="role_name"></property>
<property name="roleDesc" column="role_desc"></property>
<!-- 指定多对多关系(需中间表) -->
<set name="emps" table="emp_role" cascade="save-update">
<!-- key:->column: role端的外键, role_id -->
<key column="role_id"></key>
<!-- class: 另一端的路径 column: 另一端的外键emp_no -->
<many-to-many class="Emp" column="emp_no"></many-to-many>
</set>
</class>
</hibernate-mapping>
测试代码:
package com.rl.hiber.test;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.rl.hiber.model.Emp;
import com.rl.hiber.model.Role;
import com.rl.hiber.utils.HibernateUtil;
public class TestHibernate {
/**
* 采用级联的方式
*/
@Test
public void testAdd2() {
Session session = HibernateUtil.getSessoion();
Transaction tx = session.beginTransaction();
try {
Emp emp = new Emp();
emp.setAddress("上海");
emp.setBirthday(new Date());
emp.setEname("田七");
emp.setGender(1);
Emp emp2 = new Emp();
emp2.setAddress("天津");
emp2.setBirthday(new Date());
emp2.setEname("周八");
emp2.setGender(2);
Set<Emp> emps = new HashSet<Emp>();
emps.add(emp);
emps.add(emp2);
Role role1 = new Role();
role1.setRoleDesc("讲课");
role1.setRoleName("老师");
role1.setEmps(emps);
session.save(role1);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtil.closeResource(session);
}
}
}
数据库结果: