hibernate 关联关系主要有一对一,一对多,多对多
一对一关联
一对一关联包括:
- 主键关联
- 唯一外键关联
主键关联
两张表通过主键一对一映射
比如员工和工牌之间,一个员工对应一个工牌,一个工牌也对应一个员工
对应员工实体类
public class Employee implements Serializable {
private int id;
private String firstName;
private String lastName;
private int salary;
private CardEntity cardEntity;
}
hbm
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-lazy="true">
<class name="bean.Employee" table="employee">
<meta attribute="class-description">
This class contains the employee detail.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="first_name" type="string"/>
<property name="lastName" column="last_name" type="string"/>
<property name="salary" column="salary" type="int"/>
<!--声明一对一关系-->
<one-to-one name="cardEntity" cascade="all" class="bean.CardEntity" outer-join="true"></one-to-one>
</class>
</hibernate-mapping>
cascade用来设置级联关系
- none:在保存,删除或修改当前对象时,不对其附属对象(关联对象)进行级联操作。它是默认值。
- save-update:在保存,更新当前对象时,级联保存,更新附属对象(临时对象,游离对象)。
- delete:在删除当前对象时,级联删除附属对象。
- all:所有情况下均进行级联操作,即包含save-update和delete等等操作。
- delete-orphan:删除此对象的同时删除与当前对象解除关系的孤儿对象(仅仅使用于一对多关联关系中)。
工牌对应实体类
public class CardEntity {
private int id;
private String cardNo;
}
hbm
<?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="bean.CardEntity" table="t_card" schema="jpa">
<id name="id" column="id"/>
<property name="cardNo" column="card_no"/>
</class>
</hibernate-mapping>
插入数据
Session session = sessionFactory.openSession();
//获取事务
Transaction ts = session.beginTransaction();
Employee employee=new Employee();
CardEntity cardEntity=new CardEntity();
cardEntity.setCardNo("123");
//设置关联
employee.setCardEntity(cardEntity);
employee.setFirstName("张");
employee.setLastName("三");
session.save(employee);
ts.commit();
session.close();
运行后sql输出
insert
into
employee
(first_name, last_name, salary, department_id, phone)
values
(?, ?, ?, ?, ?)
insert
into
jpa.t_card
(card_no, id)
values
(?, ?)
即同时插入了员工表和工牌表
这时你运行查询方法
List<Object> employees = session.createQuery("select e.cardEntity.cardNo from Employee e").list();
是查询不了数据的,因为刚刚插入的工牌和员工主键不一致
这里引入generator属性用于保持主键的一致性
generator属性有7种主键生成策略
- identity:用于mysql,主键递增
- sequence:用于oracle数据库
- native:跨数据库时使用,由底层方言产生
- hilo:通过高低位合成id,先建表hi_value,再建列next_value。必须要有初始值
- sequencehilo:同过高低位合成id,建一个sequence序列,不用建表。
- assigned:用户自定义id
- foreign:用于一对一关系共享主健时,两id值一样
所以将工牌对应的hbm修改为
<?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="bean.CardEntity" table="t_card" schema="jpa">
<id name="id" column="id">
<generator class="foreign">
<param name="property">employee</param>
</generator>
</id>
<one-to-one name="employee" constrained="true" class="bean.Employee"></one-to-one>
<property name="cardNo" column="card_no"/>
</class>
</hibernate-mapping>
插入员工
Session session = sessionFactory.openSession();
//获取事务
Transaction ts = session.beginTransaction();
Employee employee=new Employee();
CardEntity cardEntity=new CardEntity();
cardEntity.setCardNo("123");
employee.setFirstName("张");
employee.setLastName("三");
//相互关联
cardEntity.setEmployee(employee);
employee.setCardEntity(cardEntity);
session.save(employee);
ts.commit();
session.close();
唯一外键关联
每一个员工都从属于一个部门.员工表employee包含一个department_id字段,此字段与department的id字段关联.这是一个典型的唯一外键关联
hibernate中的唯一外键关联由many-to-one定义,
因为唯一外键关联的一对一只是多对一关系的特例
员工类hbm
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping SYSTEM
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-lazy="true">
<class name="bean.Employee" table="employee">
<meta attribute="class-description">
This class contains the employee detail.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="first_name" type="string"/>
<property name="lastName" column="last_name" type="string"/>
<property name="salary" column="salary" type="int"/>
<property name="departmentId" column="department_id" type="int" insert="false" update="false"/>
<property name="phone" column="phone" type="bean.TestUseType"/>
<many-to-one name="department" class="bean.Department" column="department_id"></many-to-one>
</class>
</hibernate-mapping>
这样就形成了一个单向关系
如果要形成双向关系则需要修改Department类,为其追加one-to-one配置
<hibernate-mapping>
<class name="bean.Department" table="department">
<meta attribute="class-description">
This class contains the employee detail.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="name" column="name" type="string"/>
<one-to-one class="bean.Employee"></one-to-one>
</class>
</hibernate-mapping>
一对多关联
一对多关联分为双向和单向关联
一对多关联非常常见.例如每个员工都有多个地址,如办公室地址,家庭住址等
单项一对多
员工hbm添加
<set table="address" name="addressEntities" cascade="all">
<key column="emp_id"></key>
<one-to-many class="bean.AddressEntity"></one-to-many>
</set>
地址hbm
<hibernate-mapping>
<class name="bean.AddressEntity" table="address" schema="jpa">
<id name="id" column="id">
</id>
<property name="address" column="address"/>
<property name="type" column="type"/>
<property name="empId" column="emp_id"/>
</class>
</hibernate-mapping>
这就完成了单向一对多的配置,单向关联时,为了保持关联关系,我们只能通过主控方对被控方进行级联更新,如果被关联方的关联字段为not null的话就会出现约束违例
这个问题可以通过双向关联来解决,它避免了约束违例并且提高了性能
双向一对多
双向一对多关联,实际上是一对多和多对一关联的组合,也就是说我们必须在主控方配置单向一对多关系的基础上,在被控方配置对应的多对一
修改员工类hbm
<set table="address" name="addressEntities" cascade="all" inverse="true">
<key column="emp_id"></key>
<one-to-many class="bean.AddressEntity"></one-to-many>
</set>
添加了inverse属性,指定address为关系维护者
修改address对应hbm
<many-to-one name="employee" class="bean.Employee" column="emp_id"></many-to-one>
多对多关联
hibernate的多对多关系需要借助中间表来完成多对多映射信息的保存
多对多最常见的是在权限系统中使用,一个角色对应多个权限,一个权限对应多个角色
角色hbm
<class name="bean.RoleEntity" table="role" schema="jpa">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"/>
<set name="permissionEntities"
table="role_permission_map"
cascade="save-update"
lazy="false"
inverse="false"
>
<key column="role_id"></key>
<many-to-many class="bean.PermissionEntity" column="permission_id" ></many-to-many>
</set>
</class>
权限hbm
<class name="bean.PermissionEntity" table="permission" schema="jpa">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<set name="roleEntities"
table="role_permission_map"
cascade="save-update"
lazy="false"
inverse="true"
>
<key column="permission_id"></key>
<many-to-many column="role_id" class="bean.RoleEntity"></many-to-many>
</set>
</class>
插入角色
Session session = sessionFactory.openSession();
Transaction ts = session.beginTransaction();
RoleEntity roleEntity = new RoleEntity();
roleEntity.setName("test");
RoleEntity roleEntity2 = new RoleEntity();
roleEntity2.setName("test2");
PermissionEntity permissionEntity = new PermissionEntity();
permissionEntity.setName("新建用户");
PermissionEntity permissionEntity2 = new PermissionEntity();
permissionEntity2.setName("删除用户");
Set set = new HashSet<RoleEntity>();
set.add(roleEntity);
permissionEntity.setRoleEntities(set);
Set set2 = new HashSet<RoleEntity>();
set2.add(roleEntity2);
permissionEntity2.setRoleEntities(set2);
Set set3 = new HashSet<PermissionEntity>();
set3.add(permissionEntity);
set3.add(permissionEntity2);
roleEntity.setPermissionEntities(set3);
Set set4 = new HashSet<PermissionEntity>();
set4.add(permissionEntity2);
roleEntity2.setPermissionEntities(set4);
session.save(roleEntity);
session.save(roleEntity2);
ts.commit();
session.close();