在我们实际项目中,多对多的情况也时长存在,比如最常见的就是系统管理的五张表,如下面的一个结构:
在本文学习hibernate多对多关联映射的实验中我简单的写几个字段,达到学习目的即可。
1.多对多的关系表达
1.关系型数据库(RDB)中的表达:
2.Java实体中的表达
3.orm配置文件中的表达:(注意每次添加了ORM映射文件都要加到主配置文件中)
User.hbm.xml
/p>
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
Role.hbm.xml
/p>
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
4.测试建表结果:
随便执行一个hibernate测试,会加载主配置文件并进行建表。
mysql> descsys_user;+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| user_id | bigint(20) | NO | PRI | NULL | auto_increment |
| User_name | varchar(20) | YES | | NULL | |
| user_state | char(1) | YES | | NULL | |
+------------+-------------+------+-----+---------+----------------+
3 rows in set (0.00sec)
mysql> descsys_role;+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| role_id | bigint(20) | NO | PRI | NULL | auto_increment |
| role_name | varchar(20) | YES | | NULL | |
| role_state | varchar(255) | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
3 rows in set (0.00sec)
mysql> descsys_user_role;+---------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+------------+------+-----+---------+-------+
| user_id | bigint(20) | NO | PRI | NULL | |
| role_id | bigint(20) | NO | PRI | NULL | |
+---------+------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
5.修改ORM配置中set元素配置对set元素的进一步解释
User.hbm.xml
Role.hbm.xml
修改之后查看建表语句:
mysql> show create tablesys_user_role\G*************************** 1. row ***************************
Table: sys_user_roleCreate Table: CREATE TABLE`sys_user_role` (
`user_id1`bigint(20) NOT NULL,
`role_id1`bigint(20) NOT NULL,PRIMARY KEY(`role_id1`,`user_id1`),KEY`FKgxeccay1ha1igqs2myct720lt` (`user_id1`),CONSTRAINT `FKgxeccay1ha1igqs2myct720lt` FOREIGN KEY (`user_id1`) REFERENCES `sys_user` (`user_id`)CONSTRAINT `FKtfvi95hnfedqqxi3rk1qmrwy7` FOREIGN KEY (`role_id1`) REFERENCES`sys_role` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf81 row in set (0.00 sec)
总结:
个人认为set元素更像是配置我与中间表的关系,key元素可以理解为中间表指向我的外键的名字,
many-to-many中的class指的是与我是多对多的对象类名,column是在中间表中其对应的外键名字。
2.多对多操作
--------------------------第一次测试-----------------
1.测试保存:
/***测试保存(多对多)*/@Testpublic voidfun1() {//1.获取session
Session session =HibernateUtil.openSession();//2.开启事务
Transaction tx =session.beginTransaction();//3.构造数据(张三-总监、经理,李四---总监)
User u1 = newUser();
u1.setUser_name("张三");
User u2= newUser();
u2.setUser_name("李四");
Role r1= newRole();
r1.setRole_name("总监");
Role r2= newRole();
r2.setRole_name("经理");
u1.getRoles().add(r1);
u1.getRoles().add(r2);
u2.getRoles().add(r1);
r1.getUsers().add(u1);
r1.getUsers().add(u2);
r2.getUsers().add(u1);
session.save(u1);
session.save(u2);
session.save(r1);
session.save(r1);//4.提交数据
tx.commit();//5.关闭session
session.close();
}
执行会报错,因为两个表都维护关系,所以会像中间表插入两次记录,导致插入重复。如下错误:
解决办法:
第一钟:将一方维护关系的代码注释掉:(一般开发中多对多的关系中都有一方放弃维护关系)
第二种:配置文件中,设置一方放弃维护关系,例如:设置user放弃维护关系(一般开发中多对多的关系中都有一方放弃维护关系)
2.测试修改:
/***新增一个角色,并给张三新增加一个角色*/@Testpublic voidfun2() {//1.获取session
Session session =HibernateUtil.openSession();//2.开启事务
Transaction tx =session.beginTransaction();//3.构造数据(张三-总监、经理,李四---总监)
User u1 = session.get(User.class, 1l);
Role r3= newRole();
r3.setRole_name("太监");
session.save(r3);//保存新角色
u1.getRoles().add(r3);//u1处于持久态,因此不用执行更新语句也可以更新//4.提交数据
tx.commit();//5.关闭session
session.close();
}
3.测试给张三删除一个角色
/***为张三删除一个太监角色*/@Testpublic voidfun3() {//1.获取session
Session session =HibernateUtil.openSession();//2.开启事务
Transaction tx =session.beginTransaction();//3.构造数据(张三-总监、经理,李四---总监)
User u1 = session.get(User.class, 1l);
Role r1= session.get(Role.class, 3l);
u1.getRoles().remove(r1);//u1处于持久态,因此不用执行更新语句也可以更新//4.提交数据
tx.commit();//5.关闭session
session.close();
}
查看发出的SQL:
Hibernate:selectuser0_.user_id asuser_id1_3_0_,
user0_.User_name asUser_nam2_3_0_,
user0_.user_stateasuser_sta3_3_0_fromsys_user user0_whereuser0_.user_id=?
Hibernate:selectrole0_.role_idasrole_id1_2_0_,
role0_.role_nameasrole_nam2_2_0_,
role0_.role_stateasrole_sta3_2_0_fromsys_role role0_whererole0_.role_id=?
Hibernate:selectroles0_.user_id asuser_id1_4_0_,
roles0_.role_idasrole_id2_4_0_,
role1_.role_idasrole_id1_2_1_,
role1_.role_nameasrole_nam2_2_1_,
role1_.role_stateasrole_sta3_2_1_fromsys_user_role roles0_inner joinsys_role role1_on roles0_.role_id=role1_.role_idwhereroles0_.user_id=?
Hibernate:delete
fromsys_user_rolewhere
user_id=?and role_id=?
---------------------------第二次测试------------------
1.添加一个张三用户,并给其添加两个角色:经理和总监
/*** 测试1:添加一个张三用户
* 添加两个角色:经理、总监*/@Testpublic voidtest1(){
Session session=HibernateUtils.openSession();
Transaction tx=session.beginTransaction();/******S 开始业务逻辑*************/User u1= newUser();
u1.setUser_name("张三");
Role r1= newRole();
Role r2= newRole();
r1.setRole_name("总监");
r2.setRole_name("经理");
u1.getRoles().add(r1);
u1.getRoles().add(r2);
session.save(u1);
session.save(r1);
session.save(r2);/******E 开始业务逻辑*************/tx.commit();
}
SQL:
Hibernate:insert
intosys_user
(user_name, user_state)values(?, ?)
Hibernate:insert
intosys_role
(role_name, role_state)values(?, ?)
Hibernate:insert
intosys_role
(role_name, role_state)values(?, ?)
Hibernate:insert
intosys_user_role
(user_id, role_id)values(?, ?)
Hibernate:insert
intosys_user_role
(user_id, role_id)values(?, ?)
结果:
mysql> select * fromsys_user;+---------+-----------+------------+
| user_id | user_name | user_state |
+---------+-----------+------------+
| 1 | 张三 | NULL |
+---------+-----------+------------+
1 row in set (0.03sec)
mysql> select * fromsys_role;+---------+-----------+------------+
| role_id | role_name | role_state |
+---------+-----------+------------+
| 1 | 总监 | NULL |
| 2 | 经理 | NULL |
+---------+-----------+------------+
2 rows in set (0.00sec)
mysql> select * fromsys_user_role;+---------+---------+
| user_id | role_id |
+---------+---------+
| 1 | 1 |
| 1 | 2 |
+---------+---------+
2 rows in set (0.00 sec)
2.给上面的张三解除与总监角色的关联关系
/*** 测试2:给上面的张三解除与总监角色的关联关系*/@Testpublic voidtest2(){
Session session=HibernateUtils.openSession();
Transaction tx=session.beginTransaction();/******S 开始业务逻辑*************/
//get方法获取的对象都是持久态
User user = session.get(User.class,1l);
Role role= session.get(Role.class, 1l);
user.getRoles().remove(role);/******E 开始业务逻辑*************/tx.commit();
}
SQL:
Hibernate:selectuser0_.user_id asuser_id1_3_0_,
user0_.user_name asuser_nam2_3_0_,
user0_.user_stateasuser_sta3_3_0_fromsys_user user0_whereuser0_.user_id=?
Hibernate:selectrole0_.role_idasrole_id1_2_0_,
role0_.role_nameasrole_nam2_2_0_,
role0_.role_stateasrole_sta3_2_0_fromsys_role role0_whererole0_.role_id=?
Hibernate:selectroles0_.user_id asuser_id1_4_0_,
roles0_.role_idasrole_id2_4_0_,
role1_.role_idasrole_id1_2_1_,
role1_.role_nameasrole_nam2_2_1_,
role1_.role_stateasrole_sta3_2_1_fromsys_user_role roles0_inner joinsys_role role1_on roles0_.role_id=role1_.role_idwhereroles0_.user_id=?
Hibernate:delete
fromsys_user_rolewhere
user_id=?and role_id=?
结果:
mysql> select * fromsys_user;+---------+-----------+------------+
| user_id | user_name | user_state |
+---------+-----------+------------+
| 1 | 张三 | NULL |
+---------+-----------+------------+
1 row in set (0.00sec)
mysql> select * fromsys_role;+---------+-----------+------------+
| role_id | role_name | role_state |
+---------+-----------+------------+
| 1 | 总监 | NULL |
| 2 | 经理 | NULL |
+---------+-----------+------------+
2 rows in set (0.00sec)
mysql> select * fromsys_user_role;+---------+---------+
| user_id | role_id |
+---------+---------+
| 1 | 2 |
+---------+---------+
1 row in set (0.00 sec)
3.删除经理角色,同时删除与张三的绑定关系
/*** 测试3:删除经理角色,同时删除与张三的绑定关系*/@Testpublic voidtest3(){
Session session=HibernateUtils.openSession();
Transaction tx=session.beginTransaction();/******S 开始业务逻辑*************/
//get方法获取的对象都是持久态
User user = session.get(User.class,1l);
Role role= session.get(Role.class, 2l);
session.delete(role);/******E 开始业务逻辑*************/tx.commit();
}
SQL:
Hibernate:selectuser0_.user_id asuser_id1_3_0_,
user0_.user_name asuser_nam2_3_0_,
user0_.user_stateasuser_sta3_3_0_fromsys_user user0_whereuser0_.user_id=?
Hibernate:selectrole0_.role_idasrole_id1_2_0_,
role0_.role_nameasrole_nam2_2_0_,
role0_.role_stateasrole_sta3_2_0_fromsys_role role0_whererole0_.role_id=?
Hibernate:delete
fromsys_user_rolewhererole_id=?
Hibernate:delete
fromsys_rolewhererole_id=?
结果:
mysql> select * fromsys_role;+---------+-----------+------------+
| role_id | role_name | role_state |
+---------+-----------+------------+
| 1 | 总监 | NULL |
+---------+-----------+------------+
1 row in set (0.00sec)
mysql> select * fromsys_user_role;
Emptyset (0.00 sec)
4.新增一个秘书角色,并赋给张三该角色
/*** 测试4:新增一个秘书角色,并赋给张三该角色*/@Testpublic voidtest4(){
Session session=HibernateUtils.openSession();
Transaction tx=session.beginTransaction();/******S 开始业务逻辑*************/
//get方法获取的对象都是持久态
Role r1 = newRole();
r1.setRole_name("秘书");
User user= session.get(User.class,1l);
user.getRoles().add(r1);
session.save(r1);
session.update(user);/******E 开始业务逻辑*************/tx.commit();
}
SQL:
Hibernate:selectuser0_.user_id asuser_id1_3_0_,
user0_.user_name asuser_nam2_3_0_,
user0_.user_stateasuser_sta3_3_0_fromsys_user user0_whereuser0_.user_id=?
Hibernate:selectroles0_.user_id asuser_id1_4_0_,
roles0_.role_idasrole_id2_4_0_,
role1_.role_idasrole_id1_2_1_,
role1_.role_nameasrole_nam2_2_1_,
role1_.role_stateasrole_sta3_2_1_fromsys_user_role roles0_inner joinsys_role role1_on roles0_.role_id=role1_.role_idwhereroles0_.user_id=?
Hibernate:insert
intosys_role
(role_name, role_state)values(?, ?)
Hibernate:insert
intosys_user_role
(user_id, role_id)values(?, ?)
结果:
mysql> select * fromsys_role;+---------+-----------+------------+
| role_id | role_name | role_state |
+---------+-----------+------------+
| 1 | 总监 | NULL |
| 3 | 秘书 | NULL |
+---------+-----------+------------+
2 rows in set (0.00sec)
mysql> select * fromsys_user_role;+---------+---------+
| user_id | role_id |
+---------+---------+
| 1 | 3 |
+---------+---------+
1 row in set (0.00 sec)
5.修改秘书角色为总监秘书(不影响与用户的关联)
/*** 测试5:修改秘书角色为总监秘书(不影响与用户的关联)*/@Testpublic voidtest5(){
Session session=HibernateUtils.openSession();
Transaction tx=session.beginTransaction();/******S 开始业务逻辑*************/
//get方法获取的对象都是持久态
Role role = session.get(Role.class,3l);
role.setRole_name("总监秘书");
session.update(role);/******E 开始业务逻辑*************/tx.commit();
}
SQL:
Hibernate:selectrole0_.role_idasrole_id1_2_0_,
role0_.role_nameasrole_nam2_2_0_,
role0_.role_stateasrole_sta3_2_0_fromsys_role role0_whererole0_.role_id=?
Hibernate:updatesys_rolesetrole_name=?,
role_state=?whererole_id=?
结果:
mysql> select * fromsys_user_role;+---------+---------+
| user_id | role_id |
+---------+---------+
| 1 | 3 |
+---------+---------+
1 row in set (0.00sec)
mysql> select * fromsys_role;+---------+-----------+------------+
| role_id | role_name | role_state |
+---------+-----------+------------+
| 1 | 总监 | NULL |
| 3 | 总监秘书 | NULL |
+---------+-----------+------------+
2 rows in set (0.00 sec)
6.删除一个用户,会自动删除中间表
mysql> select *from sys_user_role;+---------+---------+
| user_id | role_id |
+---------+---------+
| 1 | 3 |
+---------+---------+
1 row in set (0.00sec)
mysql> select *from sys_role;+---------+-----------+------------+
| role_id | role_name | role_state |
+---------+-----------+------------+
| 1 | 总监 | NULL |
| 3 | 总监秘书 | NULL |
+---------+-----------+------------+
2 rows in set (0.00 sec)
SQL:
Hibernate:selectuser0_.user_id asuser_id1_3_0_,
user0_.user_name asuser_nam2_3_0_,
user0_.user_stateasuser_sta3_3_0_fromsys_user user0_whereuser0_.user_id=?
Hibernate:delete
fromsys_user_rolewhere
user_id=?
Hibernate:delete
fromsys_userwhere
user_id=?
结果:
mysql> select * fromsys_user;
Emptyset (0.00sec)
mysql> select * fromsys_role;+---------+-----------+------------+
| role_id | role_name | role_state |
+---------+-----------+------------+
| 1 | 总监 | NULL |
| 3 | 总监秘书 | NULL |
+---------+-----------+------------+
2 rows in set (0.00sec)
mysql> select * fromsys_user_role;
Emptyset (0.00 sec)
3.进阶操作
1.inverse属性:
一般开发中遇到多对多的关系总有一方放弃维护关系,至于是哪一方要看具体的业务需求。可以在代码中放弃维护,也可以在配置文件中放弃维护。例如录入员工时需要为员工指定所属角色,那么业务方向就是由员工维护角色,角色不需要维护与员工的关系。
代码中放弃维护角色:
/***测试保存(多对多)*/@Testpublic voidfun1() {//1.获取session
Session session =HibernateUtil.openSession();//2.开启事务
Transaction tx =session.beginTransaction();//3.构造数据(张三-总监、经理,李四---总监)
User u1 = newUser();
u1.setUser_name("张三");
User u2= newUser();
u2.setUser_name("李四");
Role r1= newRole();
r1.setRole_name("总监");
Role r2= newRole();
r2.setRole_name("经理");
u1.getRoles().add(r1);
u1.getRoles().add(r2);
u2.getRoles().add(r1);//角色放弃维护与员工的关系//r1.getUsers().add(u1);//r1.getUsers().add(u2);//r2.getUsers().add(u1);
session.save(r1);
session.save(r2);
session.save(u1);
session.save(u2);//4.提交数据
tx.commit();//5.关闭session
session.close();
}
Role.hbm.xml配置文件中放弃维护关系inverse属性为true即是放弃维护关系
2.级联操作 cascade属性
save-update :级联保存或更新
delete:级联删除
all:等于save-update+delete
1.级联保存或者更新:
例如我们新增一个角色并且给张三赋予新角色的代码如下:
@Testpublic voidfun2() {//1.获取session
Session session =HibernateUtil.openSession();//2.开启事务
Transaction tx =session.beginTransaction();//3.构造数据(张三-总监、经理,李四---总监)
User u1 = session.get(User.class, 1l);
Role r3= newRole();
r3.setRole_name("太监222");
session.save(r3);//保存新角色
u1.getRoles().add(r3);//u1处于持久态,因此不用执行更新语句也可以更新//4.提交数据
tx.commit();//5.关闭session
session.close();
}
我们希望在更新user的时候会级联保存角色,也就是省区一行session.save(r3)对象。
(1)修改User.hbm.xml
(2)将代码保存角色的那一行代码注释掉
(3)测试并查看SQL:
Hibernate:selectuser0_.user_id asuser_id1_3_0_,
user0_.User_name asUser_nam2_3_0_,
user0_.user_stateasuser_sta3_3_0_fromsys_user user0_whereuser0_.user_id=?
Hibernate:selectroles0_.user_id asuser_id1_4_0_,
roles0_.role_idasrole_id2_4_0_,
role1_.role_idasrole_id1_2_1_,
role1_.role_nameasrole_nam2_2_1_,
role1_.role_stateasrole_sta3_2_1_fromsys_user_role roles0_inner joinsys_role role1_on roles0_.role_id=role1_.role_idwhereroles0_.user_id=?
Hibernate:insert
intosys_role
(role_name, role_state)values(?, ?)
Hibernate:insert
intosys_user_role
(user_id, role_id)values(?, ?)
2.级联删除:
修改User.hbm.xml,开启级联删除:
测试代码:(会级联去删除角色表,如果角色表对应记录被其他用户引用会报错)
/***删除张三,查看级联删除角色*/@Testpublic voidfun4() {//1.获取session
Session session =HibernateUtil.openSession();//2.开启事务
Transaction tx =session.beginTransaction();//3.构造数据(张三-总监、经理,李四---总监)
User u1 = session.get(User.class, 5l);
session.delete(u1);//4.提交数据
tx.commit();//5.关闭session
session.close();
}
查看发出的SQL:
Hibernate:selectuser0_.user_id asuser_id1_3_0_,
user0_.User_name asUser_nam2_3_0_,
user0_.user_stateasuser_sta3_3_0_fromsys_user user0_whereuser0_.user_id=?
Hibernate:selectroles0_.user_id asuser_id1_4_0_,
roles0_.role_idasrole_id2_4_0_,
role1_.role_idasrole_id1_2_1_,
role1_.role_nameasrole_nam2_2_1_,
role1_.role_stateasrole_sta3_2_1_fromsys_user_role roles0_inner joinsys_role role1_on roles0_.role_id=role1_.role_idwhereroles0_.user_id=?
Hibernate:delete
fromsys_user_rolewhere
user_id=?
Hibernate:delete
fromsys_rolewhererole_id=?
Hibernate:delete
fromsys_rolewhererole_id=?
Hibernate:delete
fromsys_userwhere
user_id=?
总结:
O 对象 两方都使用集合.
R 关系型数据库 使用中间表.至少两列.作为外键引用两张表的主键.
M 映射文件 多:
操作:操作管理级别属性.
cascade: 级联操作
减少我们书写的操作代码.
none(默认值) 不级联
save-update: 级联保存
delete: 级联删除
all: 级联保存+级联删除
结论: 可以使用save-update.不推荐使用delete. 也可以不用cascade.
inverse: 反转关系维护
属于性能优化.必须选择一方放弃维护主键关系.哪方放弃要看业务方向.
补充:多对多的时候经常用到查询,比如上面查询用户ID为1的用户具有的角色
用HQL判断两个实体间是否存在一对多关系,用的 in elements, 注意in elements 只能用于where 从句中
语法如下:
@Testpublic voidfun5() {//1.获取session
Session session =HibernateUtil.openSession();
String hql= "from Role r where ? in elements(r.users)";
Query query=session.createQuery(hql);
User user= newUser();
user.setUser_id(1L);
query.setParameter(0, user);
List roles =query.list();
System.out.println(roles);
}
查看SQL语句如下:
Hibernate:
select
role0_.role_id as role_id1_0_,
role0_.role_name as role_nam2_0_,
role0_.role_state as role_sta3_0_
from
sys_role role0_
where
? in (
select
users1_.user_id
from
sys_user_role users1_
where
role0_.role_id=users1_.role_id
)
Hibernate:
select
users0_.role_id as role_id2_2_0_,
users0_.user_id as user_id1_2_0_,
user1_.user_id as user_id1_1_1_,
user1_.User_name as User_nam2_1_1_,
user1_.user_state as user_sta3_1_1_
from
sys_user_role users0_
inner join
sys_user user1_
on users0_.user_id=user1_.user_id
where
users0_.role_id=?
Hibernate:
select
users0_.role_id as role_id2_2_0_,
users0_.user_id as user_id1_2_0_,
user1_.user_id as user_id1_1_1_,
user1_.User_name as User_nam2_1_1_,
user1_.user_state as user_sta3_1_1_
from
sys_user_role users0_
inner join
sys_user user1_
on users0_.user_id=user1_.user_id
where
users0_.role_id=?