一、 一对多的ORM关系映射
ORM : Object Relational Mapping 。 对象 关系 映射。
- 首先完成Relational数据库表的一对多的关系
- 完成Object实体对象的一对多关系
- 完成Mapping映射文件中一对多关系的映射配置
一对多的实例:客户-联系人
一个客户(公司)---对应---多个联系人
步骤:
- 创建两个数据库表customer,linkman,在多的一方添加外键建立关系
- 创建两个实体类
Customer和Linkman
,在实体类中分别建立与对方的关系 - 创建两个实体类对应的配置文件,分别配置完成一对多的关系映射
1.导入jar包(省略)
导入Hibernate所需要的一些jar包。
2. 创建数据库表customer与linkman,并建立联系。R
创建customer表与linkman表,在多的一方linkman的表中添加外键,指向一的一方的主键
create database hibernate_day03;
use hibernate_day03;
客户表 :
CREATE TABLE `cst_customer` (
`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
`cust_user_id` bigint(32) DEFAULT NULL COMMENT '负责人id',
`cust_create_id` bigint(32) DEFAULT NULL COMMENT '创建人id',
`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
`cust_linkman` varchar(64) DEFAULT NULL COMMENT '联系人',
`cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',
`cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
联系人数据库表 :
CREATE TABLE `cst_linkman` (
`lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
`lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
`lkm_cust_id` bigint(32) NOT NULL COMMENT '客户id',
`lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
`lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
`lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
`lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
`lkm_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
`lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
`lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
PRIMARY KEY (`lkm_id`),KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
)
3. 创建Customer与Linkman实体类,并建立联系。O
分别在两个实体类中添加与另一个实体类的联系。
Customer的实体类:
img17.pngLinkman的实体类:
img18.png4. 在对应的映射文件中添加一对多的映射关系
Customer.hbm.xml映射文件:
<!-- 集合,一对多关系,在映射文件中配置 -->
<!--
name属性:集合属性名
column属性: 外键列名(通过这个外键找到Linkman放入集合)
class属性: 与我关联的对象完整类名(从哪里找)
-->
<set name="linkemans">
<key column="lkm_cust_id"></key>
<one-to-many class="com.itdream.domain.Linkeman"/>
</set>
Linkman.hbm.xml映射文件:
<!-- 多对一配置 -->
<!--
name属性:引用的属性名
column属性: 外键列名(通过外键找到这个引用)
class属性: 与我关联的对象完整类名(从哪找)
-->
<many-to-one name="customer" column="lkm_cust_id" class="com.itdream.domain.Customer"></many-to-one>
5.在核心配置文件中加载映射文件
<?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的核心配置文件 -->
<hibernate-configuration>
<session-factory>
<!-- 1.Hibernate连接数据库的基本配置 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_day03</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!-- 2.配置与当前线程绑定的Session对象 -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 3.Hibernate的普通配置 -->
<!-- Hibernate的方言配置,根据配置生成对应SQL语句.这实现了Hibernate跨数据库.更换数据库只需要修改这里的配置即可-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 显示sql语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化sql语句 -->
<property name="hibernate.format_sql">true</property>
<!-- Hibernate自动创建数据库表的方式 hbm2ddl-->
<property name="hibernate.hbm2ddl.auto">none</property>
<!-- 设置隔离级别:4:Repeatable read可重复读 -->
<property name="hibernate.isolation">4</property>
<!-- c3p0连接池配置-->
<property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
<!-- 最小连接 -->
<property name="hibernate.c3p0.min_size">5</property>
<!-- 最大连接数 -->
<property name="hibernate.c3p0.max_size">20</property>
<!-- 连接超时时长 -->
<property name="hibernate.c3p0.timeout">120</property>
<!-- 每120秒检查空闲连接 -->
<property name="hibernate.c3p0.idle_test_period">120</property>
<!-- 最大statments数量 -->
<property name="hibernate.c3p0.max_statements">120</property>
<!-- 连接用完后,每次增加的连接数 -->
<property name="hibernate.c3p0.acquire_increment">2</property>
<!-- 每次都验证连接是否可用 -->
<property name="hibernate.c3p0.validate">false</property>
<!-- 4.加载映射配置 -->
<mapping resource="com/itdream/domain/Customer.hbm.xml"/>
<mapping resource="com/itdream/domain/Linkman.hbm.xml"/>
</session-factory>
</hibernate-configuration>
6.Hibernate:1对多关系测试
/**
* 测试数据库表的1对多关系:
*/
@Test
//保存一个客户与两个联系人
public void test() {
//获取Session对象
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction transaction = session.beginTransaction();
//------------------------------------------
//1> 创建一个客户
Customer customer = new Customer();
customer.setCust_name("阿里巴巴");
//2> 创建两个联系人
Linkman linkeman = new Linkman();
linkeman.setLkm_name("小马");
Linkman linkeman2 = new Linkman();
linkeman2.setLkm_name("小云");
//3> 表达一对多,客户下有多个联系人
customer.getLinkemans().add(linkeman);
customer.getLinkemans().add(linkeman2);
//4> 表达多对一,联系人属于哪个客户
linkeman.setCustomer(customer);
linkeman2.setCustomer(customer);
//5> 保存.将瞬时态对象转换成持久态对象
session.save(customer);
session.save(linkeman);
session.save(linkeman2);
//------------------------------------------
//提交事务
transaction.commit();
}
@Test
//为客户1添加一个联系人
public void test2() {
//获取Session对象
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction transaction = session.beginTransaction();
//------------------------------------------
//1> 获取客户1
Customer customer = session.get(Customer.class, 1L);
//2> 创建待添加的联系人
Linkman linkeman = new Linkman();
linkeman.setLkm_name("刘总");
//3> 建立关系:表达一对多,客户下有多个联系人
customer.getLinkemans().add(linkeman);
//4> 建立关系:表达多对一,联系人属于哪个客户
linkeman.setCustomer(customer);
//5> 保存.将瞬时态对象转换成持久态对象
//session.save(customer);(customer是持久态对象无需手动保存,会自动更新数据库)
session.save(linkeman);
//------------------------------------------
//提交事务
transaction.commit();
}
@Test
//为客户1删除一个联系人刘总
public void test3() {
//获取Session对象
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction transaction = session.beginTransaction();
//------------------------------------------
//1> 获取客户1
Customer customer = session.get(Customer.class, 1L);
//2> 获取待删除的联系人
Linkman linkeman = session.get(Linkman.class, 3L);
//3> 建立关系:从客户的联系人集合中删除该联系人
customer.getLinkemans().remove(linkeman);
//4> 建立关系:从联系人的客户引用中删除该客户
linkeman.setCustomer(null);
//5> 保存.将瞬时态对象转换成持久态对象(customer与linkman都是持久态对象,无需保存会自动更新)
//session.save(customer);
//session.save(linkeman);
//------------------------------------------
//提交事务
transaction.commit();
}
上面在操作瞬时态对象时,每个瞬时态对象都要手动进行save保存,为了节省代码,Hibernate提供了级联操作。只要设置了级联的这一方添加了级联配置,与它产生关系的另一方就无需在进行save或者update等操作了。
级联测试:
在客户的一端设置级联保存,那么只要客户是持久化状态,就无需对与其产生关系的联系人进行save或者update操作了.
<!--
级联操作: cascade
save-update: 级联保存更新
delete:级联删除
all:save-update+delete
级联操作: 简化操作.目的就是为了少些两行代码.
-->
<set name="linkemans" cascade="save-update">
<key column="lkm_cust_id"></key>
<one-to-many class="com.itdream.domain.Linkman"/>
</set>
@Test
//保存一个客户与两个联系人(级联保存)
//为了简化代码操作,在做session.save时,save客户就可以同时将客户中的ecustomer对象保存.
//但是,如果直接省略,会报错:瞬时态与执行态关联的错误.因此在需要做级联保存的映射文件中配置,将cascad属性设为save-update即可
public void test1() {
//获取Session对象
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction transaction = session.beginTransaction();
//------------------------------------------
//1> 创建一个客户
Customer customer = new Customer();
customer.setCust_name("阿里巴巴");
//2> 创建两个联系人
Linkman linkeman = new Linkman();
linkeman.setLkm_name("小马");
Linkman linkeman2 = new Linkman();
linkeman2.setLkm_name("小云");
//3> 建立关系:将联系人添加到客户的集合中
customer.getLinkemans().add(linkeman);
customer.getLinkemans().add(linkeman2);
//4> 建立关系:将客户添加到联系人中
linkeman.setCustomer(customer);
linkeman2.setCustomer(customer);
//5> 保存.将瞬时态对象转换成持久态对象
session.save(customer);
//在customer的映射文件中配置级联保存,在保存customer的时候会自动保存与它产生关联的联系人
// session.save(linkeman);
// session.save(linkeman2);
//------------------------------------------
//提交事务
transaction.commit();
}
@Test
//为添加一个联系人(级联保存)
//为了简化代码操作,在做session.save时,save客户就可以同时将客户中的ecustomer对象保存.
//但是,如果直接省略,会报错:瞬时态与执行态关联的错误.因此在需要做级联保存的映射文件中配置,将cascad属性设为save-update即可
public void test2() {
//获取Session对象
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction transaction = session.beginTransaction();
//------------------------------------------
//1> 获取客户1
Customer customer = session.get(Customer.class, 1L);
//2> 创建一个联系人
Linkman linkeman = new Linkman();
linkeman.setLkm_name("小白");
//3> 建立关系:将联系人添加到客户的集合中
customer.getLinkemans().add(linkeman);
//4> 建立关系:将客户添加到联系人中
linkeman.setCustomer(customer);
//5> 保存.customer通过get方法取出就是持久态了,无需手动保存
//因为配置了级联保存,保存customer的时候Hibernate会自动将与这个customer产生关系的linkman设为持久态保存
//session.save(customer);
//------------------------------------------
//提交事务
transaction.commit();
}
@Test
//为客户1删除联系人id为1的小马,增加一个联系人为小宋
public void test4() {
//获取Session对象
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction transaction = session.beginTransaction();
//-------------------------------
//实现业务逻辑 : 为客户1删除联系人id为1的小马,增加一个联系人为小宋
//1> 获取客户1
Customer customer = session.get(Customer.class, 1L);
//2> 获取要删除的联系人小马
Linkman linkman = session.get(Linkman.class, 1L);
//3> 创建一个联系人小宋
Linkman linkman2 = new Linkman();
linkman2.setLkm_name("小宋");
//4> 建立关系: 在客户1方建立对联系人的关系
//从联系人集合中删除要删除的联系人
customer.getLinkemans().remove(linkman);
//添加要添加的联系人到联系人集合
customer.getLinkemans().add(linkman2);
//5> 建立关系: 在联系人放建立对客户1的关系
//解除客户在联系人的引用
linkman.setCustomer(null);
//在添加的联系人的客户引用中添加客户1
linkman2.setCustomer(customer);
//6> 保存
//customer是持久态,删除的联系人也是持久态,待添加的联系人不是持久态,但是由于customer设置了级联保存因此也无需save保存
//-------------------------------
//提交事务
transaction.commit();
//关闭资源,getCurrentSession方法获得与线程绑定的Session对象无需手动关闭
}
@Test
//级联删除 : 在实际开发中基本不用,这里测试使用
//目的:在删除联系人的时候将它的客户也删除掉
public void test5() {
//获取Session对象
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction transaction = session.beginTransaction();
//-------------------------------
//实现业务逻辑 : 删除联系人1的时候将它的客户也删除掉
//1> 获取要删除的联系人
Linkman linkman = session.get(Linkman.class, 1L);
//2> 删除联系人
session.delete(linkman);
//3> 保存
//linkman是持久态,无需手动保存,同时它设置了级联删除,因此删除联系人的同时,会将它对应的客户1也删除掉
//如果这个时候在客户的一方也设置级联删除,会导致删除一个客户或者删除一个联系人,会将与它有关联的所有数据都删除掉
//-------------------------------
//提交事务
transaction.commit();
//关闭资源,getCurrentSession方法获得与线程绑定的Session对象无需手动关闭
}
上面通过级联的配置,节省了一些多余代码的操作,但是我们通过查看Hibernate打印的sql语句发现(例如执行test1的代码),Hibernate会对外键lkm_cst_id
进行多余的两次操作.Linkman自身在insert添加新联系人时已经维护了外键lkm_cst_id
,那么下面update进行的维护很显然就是客户customer
在进行维护,这是很冗余的操作。因此我们需要通过配置进行优化,Hibernate提供了inverse
的属性来设置。
<!-- inverse属性: 配置关系是否维护.
true: customer不维护关系
false(默认值): customer维护关系
inverse属性: 性能优化.提高关系维护的性能.
原则: 无论怎么放弃,总有一方必须要维护关系.
一对多关系中: 一的一方放弃.也只能一的一方放弃.多的一方不能放弃.
-->
<set name="linkemans" cascade="save-update" inverse="true">
<key column="lkm_cust_id"></key>
<one-to-many class="com.itdream.domain.Linkman"/>
</set>
//操作进阶--关系维护属性
public class Demo3 {
@Test
//保存客户 以及客户 下的联系人
public void fun1(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 开启事务
Transaction tx = session.beginTransaction();
//-------------------------------------------------
//3操作
Customer c = new Customer();
c.setCust_name("阿里巴巴");
LinkMan lm1 = new LinkMan();
lm1.setLkm_name("小马");
LinkMan lm2 = new LinkMan();
lm2.setLkm_name("小云");
//表达一对多,客户下有多个联系人.
// 如果客户放弃维护与联系人的关系. 维护关系的代码可以省略
//c.getLinkMens().add(lm1);
//c.getLinkMens().add(lm2);
//表达多对多,联系人属于哪个客户
lm1.setCustomer(c);
lm2.setCustomer(c);
//如果Linkman的配置文件中配置了级联保存,则这条保存客户的语句可以省略
//session.save(c);
session.save(lm1);
session.save(lm2);
//-------------------------------------------------
//4提交事务
tx.commit();
//5关闭资源
session.close();
}
上面的配置表示:customer放弃外键维护lkm_cst_id
.那么同样再执行test1的代码时,下面update
的冗余的sql语句就消失了,达到了优化的目的.
二、 多对多的ORM关系映射
ORM : Object Relational Mapping 。 对象 关系 映射。
- 首先完成Relational数据库表的多对多的关系表达(外键)
- 完成Object实体对象的多对多关系表达(分别创建Set集合存储对方的对象)
- 完成Mapping映射文件中一对多关系的映射配置(配置)
多对多的实例
用户与角色(岗位) User & Role
一个用户可以在公司中担任多个岗位
一个岗位上也有多个员工用户在工作
1.jar包上面一对多的例子已经导入
2.创建多对多的两个数据库表,进行关系表达 Relational
多对多的数据库多表关系表达,需要创建一个第三方表,这个第三方表至少有两个字段,分别指向两张表的主键.
-- 用户表
CREATE TABLE `sys_user` (
`user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用户id',
`user_code` varchar(32) NOT NULL COMMENT '用户账号',
`user_name` varchar(64) NOT NULL COMMENT '用户名称',
`user_password` varchar(32) NOT NULL COMMENT '用户密码',
`user_state` char(1) NOT NULL COMMENT '1:正常,0:暂停',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
-- 用户角色表.
CREATE TABLE `sys_role` (
`role_id` bigint(32) NOT NULL AUTO_INCREMENT,
`role_name` varchar(32) NOT NULL COMMENT '角色名称',
`role_memo` varchar(128) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- 第三方表,负责维护user与role表的关系
CREATE TABLE `sys_user_role` (
`role_id` bigint(32) NOT NULL COMMENT '角色id',
`user_id` bigint(32) NOT NULL COMMENT '用户id',
PRIMARY KEY (`role_id`,`user_id`),
KEY `FK_user_role_user_id` (`user_id`),
CONSTRAINT `FK_user_role_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`role_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_user_role_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3.创建多对多的两个实体类,进行关系表达 Object
/**
* 持久化类:用户实体类
*/
public class User {
// 普通属性
private Long user_id;//外键
private String user_code;
private String user_name;
private String user_password;
private Character user_state;
// 建立多对多关系:Set集合存储当前User对象持有的Role角色信息
private Set<Role> roles = new HashSet<>();
/**
* 持久化类:用户角色的实体类
*/
public class Role {
// 普通属性
private Long role_id;//外键
private String role_name;
private String role_memo;
// 建立实体类多对多关系:Set集合存储属于该Role角色的所有User对象
private Set<User> users = new HashSet<>();
4.配置两个映射文件,进行关系表达 Mapping
User.hbm.xml映射文件配置多对多关系:
<!-- 多对多的关系映射配置 -->
<!--
name: 集合属性名
table: 配置中间表名
key
|-column:外键,"我"的外键列名
class: 我与哪个类是多对多关系
column:外键.与我产生多对多关系的外键列名
-->
<set name="roles" table="sys_user_role">
<key column="user_id"></key>
<many-to-many class="com.itdream.domain.Role" column="role_id" ></many-to-many>
</set>
------------------------------------------------------------
Role.hbm.xml映射文件配置多对多关系:
<!-- 多对多的关系映射配置 -->
<!--
name: 集合属性名
table: 配置中间表名
key
|-column:外键,"我"的外键列名
class: 我与哪个类是多对多关系
column:外键.与我产生多对多关系的外键列名
-->
<set name="roles" table="sys_user_role">
<key column="user_id"></key>
<many-to-many class="com.itdream.domain.Role" column="role_id" ></many-to-many>
</set>
4.在hibernate.cfg.xml
中加载映射文件:
<mapping resource="com/itdream/domain/User.hbm.xml"/>
<mapping resource="com/itdream/domain/Role.hbm.xml"/>
4.Hibernate多对多关系测试
/**
* Hibernate多对多关系测试
*/
@Test
//多对多关系:创建两个用户,创建三个角色由两个用户分配保存
public void test1() {
//获取Session对象
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction transaction = session.beginTransaction();
//----------------------------
//实现业务逻辑
//1> 创建两个用户
User user = new User();
User user2 = new User();
user.setUser_name("杨幂");
user2.setUser_name("唐嫣");
//2> 创建三个角色(岗位)
Role role = new Role();
Role role2 = new Role();
Role role3 = new Role();
role.setRole_name("明星");
role2.setRole_name("女儿");
role.setRole_name("妻子");
//3> 用户表达关系:分别在用户中添加角色
//杨幂持有明星,女儿,妻子三个角色
user.getRoles().add(role);
user.getRoles().add(role2);
user.getRoles().add(role3);
//唐嫣持有明星和女儿两个角色
user2.getRoles().add(role);
user2.getRoles().add(role2);
//4> 角色表达关系:分别在角色中添加用户
role.getUsers().add(user);
role2.getUsers().add(user);
role3.getUsers().add(user);
role.getUsers().add(user2);
role2.getUsers().add(user2);
//5> 保存
session.save(user);
session.save(user2);
session.save(role);
session.save(role2);
session.save(role3);
//----------------------------
//提交事务
transaction.commit();
}
==================================================
我们按照非常正规的方式,分别建立了user和role对象然后逐个保存,但是就是这么看起来正确的操作却报错了。
org.hibernate.exception.ConstraintViolationException: could not execute statement
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
Duplicate entry '1-1' for key 'PRIMARY'
根据日志错误信息,我们知道产生了重复的主键:1-1.
这是因为我们的第三方表只有两个字段,就是两张表的外键,这张表的主键就是两个外键组成的联合主键。
那为什么会产生重复的主键1-1呢?
我们在上面inverse外键维护关系的属性时讲了,inverse默认值是false,默认维护外键.
我们在建立关系的时候,user一方建立了关系-getRoles.add(user).role一方也建立了关系-getUsers.add(role);
这样它们在维护外键时会导致user维护了外键往第三方表插入了1 1. role在维护外键时也会往第三方表插入1 1.导致联合主键重复了。
======================================================
要解决这一问题,有两种方式:
方式1: 只让一方建立关系
即: 注释掉一方的建立关系,例如注释掉role方建立与user的关系表达
将 role.getUsers.add(role)的语句全都注释掉就可以了。
但是这种方式,我们看起来不是很舒服,因为感觉不够符合逻辑。
建立关系应该是两方都建立。因此就可以使用方式2.
--------------------------------------------------------
方式2: 让被动的一方放弃外键维护
在这里因为user是主动选择的role角色,因此让role放弃外键维护
<!-- 使用inverse属性
true: 放弃维护外键关系
false(默认值):维护关系
结论: 将来在开发中,如果遇到多对多关系.一定要选择一方放弃维护关系.
一般谁来放弃要看业务方向(谁处于被动状态). 例如录入员工时,需要为员工指定所属角色,角色是被动的.
那么业务方向就是由员工维护角色. 角色不需要维护与员工关系.角色放弃维护。
-->
<set name="users" table="sys_user_role" inverse="true" >
<key column="role_id" ></key>
<many-to-many class="User" column="user_id" ></many-to-many>
</set>
设置级联保存
在上面的测试中,在保存瞬时态对象到一级缓存中时,需要将每一个瞬时态对象都一一save,为了节省代码,我们使用级联保存来解决。
// 5> 保存
//正常操作需要save保存每一个瞬时态对象,为了节省这些代码,我们使用级联保存cascade
//分别在User.hbm.xml与Role.hbm.xml映射文件中设置级联配置
<!-- 使用inverse属性
true: 放弃维护外键关系
false(默认值):维护关系
-->
Role.hbm.xml :
<set name="users" table="sys_user_role" inverse="true" >
<key column="role_id" ></key>
<many-to-many class="User" column="user_id" ></many-to-many>
</set>
User.hbm.xml :
<set name="roles" table="sys_user_role" cascade="save-update" >
<key column="user_id" ></key>
<many-to-many class="Role" column="role_id" ></many-to-many>
</set>
==================================================================
//这样设置之后,只需要保存其中一个对象都能级联将其它瞬时对象全都保存.(要保证所有的对象都能被关联到)
//例如,保存user,会级联保存与它建立关系的role,role2,role3.而role,role2,与user和uer2产生了关联,因此user2也成功保存
//又如,保存role3,会级联保存与role3建立了关系的user2,user2会级联保存role和role2,role和role2又能级联保存user
session.save(user);
// session.save(user2);
// session.save(role);
// session.save(role2);
// session.save(role3);
为用户添加角色:
//为唐嫣增加一个角色 : 女神
@Test
public void test2() {
//获得Session对象
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction transaction = session.beginTransaction();
//----------------------------------
//实现业务逻辑
//1> 获取user对象唐嫣
User user = session.get(User.class, 2L);
//2> 创建一个新的角色:女神
Role role = new Role();
role.setRole_name("女神");
//3> user表达关系: 将新建的角色添加到user对象的角色集合中
user.getRoles().add(role);
//4> role表达关系 : 将user对象添加到role对象的用户集合中
role.getUsers().add(user);
//5> 保存转化为持久态
// user对象已经是持久态了,无需手动保存,又因为添加了级联保存,所以会级联保存role对象
//----------------------------------
//提交事务(事务提交时,数据更新到数据库,清空一级缓存区)
transaction.commit();
}
为唐嫣解除明星角色
@Test
//为唐嫣解除明星角色
public void test3() {
//获取Session对象
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction transaction = session.beginTransaction();
//---------------------------------------------
//处理业务逻辑
//1> 获取User对象唐嫣:id为2
User user = session.get(User.class, 2L);
//2> 获取Role对象明星:id为2
Role role = session.get(Role.class, 2L);
//3> User表达关系:从role集合中移除明星角色
user.getRoles().remove(role);
//4> Role表达关系:从user集合中移除用户唐嫣
role.getUsers().remove(user);
//5> 保存,将瞬时态对象转换为持久态
//user与role对象已经持久态了
//---------------------------------------------
//提交事务
transaction.commit();
}
cascade与inverse使用总结:
- cascade : 实质上是为了节省代码操作
cascade级联操作:
save-update: 级联保存更新
delete:级联删除
all:级联保存更新+级联删除
结论: cascade简化代码书写.该属性使不使用无所谓. 建议要用只用save-update.
使用delete操作太过危险.尤其在多对多中.不建议使用.
- inverse : 减少多余的sql语句,优化Hibernate的性能
双向建立关系时,双方都进行外键维护产生了多余的sql语句,使用inverse属性使一方放弃外键维护。
使用inverse属性
true: 放弃维护外键关系
false(默认值):维护关系
结论:
在一对多关系中,通常选择一的一方放弃外键维护。
多对多关系中.一定要选择一方放弃维护关系.
一般谁来放弃要看业务方向(被动一方放弃维护). 例如录入员工时,需要为员工指定所属角色.
那么业务方向就是由员工维护角色. 角色不需要维护与员工关系.角色放弃维护
网友评论