Hibernate中many-to-many的使用情况也是非常之多,有单向的 也有双向的,个人在工作之余也去详细的做了个案例来加深体会,正所谓熟能生巧,平常工作中虽然 接触的也比较多,但是 基本上很少去总结,去整理这一些日常工作中常用到的问题。
有关many-to-many数据一般采用的是以中间表的形式来实现,而在业务代码中使用到的配置常用的有两种:
第一、使用无中间Entity的方式配置,即:不生成中间Table的Entity,这种方法虽然简单了一点,但是如果是中间表比较复杂,如:除了两个外键 还有其他字段的情况下就难以操作了;
第二、使用两个或者多个双向many-to-one的方式配置,即:根据中间Table生成Entity,同时设定各个分元素与中间Entity之间的关系是many-to-one。
通过上面的简述已经大致了解了many-to-many的常用配置方式了,接下来先开始第一种配置方式的简述与记录:
首现创建所需要的数据库表:这里使用Role<角色>与Menu<菜单>来 演示many-to-many 的关系:
MySql中:
<1>创建role Table:
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`rid` int(11) NOT NULL auto_increment,
`rname` varchar(20) default NULL,
PRIMARY KEY (`rid`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=latin1;
<2>创建menu Table:
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (
`mid` int(11) NOT NULL auto_increment,
`mname` varchar(20) NOT NULL,
PRIMARY KEY (`mid`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=latin1;
数据库表创建好了以后,开始创建相应的Entity 以及其ORM隐射文件(这里采用的是XML配置方法):
<1>创建Role Entity:
public class Role implements java.io.Serializable {
// Fields
private Integer rid;
private String rname;
private Set
menus = new HashSet
(); //多对多之间通过Set方式来表示数据的存储
get.....
set.....方法
}
<2>创建Menu Entity:
public class Menu implements java.io.Serializable {
// Fields
private Integer mid;
private String mname;
private Set roles = new HashSet(); //多对多之间通过Set方式来表示数据的存储
}
下面是hbm.xml文件配置:
<1>Role.hbm.xml配置:
<1>Menu.hbm.xml配置:
针对上面的配置做一下说明:都是通过Set的方式来配置,通过table这个属性来指定相应的中间表名称,inverse用于关联控制,cascade用户控制级联操作。
下面是测试代码:
测试级联添加:
public static void manyToManyAdd() {
// 创建Session
SessionFactory factory = HibernateSessionFactory.getSessionFactory();
Session session = factory.openSession();
Transaction st = session.beginTransaction();
try {
// 创建角色
Role role = new Role();
role.setRname("role_A");
// 创建菜单<并将菜单保存至Set集合中>
Set
menu_set = new HashSet
();
Menu menu1 = new Menu();
menu1.setMname("菜单一");
Menu menu2 = new Menu();
menu2.setMname("菜单二");
Menu menu3 = new Menu();
menu3.setMname("菜单三");
menu_set.add(menu1);
menu_set.add(menu2);
menu_set.add(menu3);
// 设置角色中的菜单集合
role.setMenus(menu_set);
// 保存
session.save(role);
st.commit();
System.out.println("添加成功!");
} catch (Exception e) {
System.out.println(e.getMessage());
st.rollback();
}
}
执行完成后将向Role、Menu以及Rome_Menu等三个表中插入数据。Hibernate执行的Sql语句如下:
在此需要注意的是:
(1)cascade设置,如果cascade没有设置,那么当我们在保存时将不能级联保存,Hibernate执行的Sql语句如下:
由此可见如果需要级联操作,那么就必须的设置cascade属性,设置为all或者save-update具体设置还是得看实际需求。
(2)inverse设置:其设置在此尤为关键,inverse表示控制关联关系是否更新到数据库,即:关联关系控制,如果两边都设置为true,即:都不管理、都不主控,那么当执行级联时,如:上面的级联添加,Hibernate执行的Sql语句如下:
此时 Hibernate将不会对关联关系进行维护,即:并没有添加数据到中间表。由此可见inverse的重要性,当然inverse也是可以设置多向的,即:在各方都设置inverse="false",来获取控制权,具体的设置还是得看情况来定。
测试级联删除:
测试代码如下:
// 级联删除
public static void manyToManyDelete() {
// 创建Session
SessionFactory factory = HibernateSessionFactory.getSessionFactory();
Session session = factory.openSession();
Transaction st = session.beginTransaction();
try {
// 查询角色
Role role = (Role) session.get(Role.class, 21);
// 删除
session.delete(role);
st.commit();
System.out.println("删除成功!");
} catch (Exception e) {
System.out.println(e.getMessage());
st.rollback();
}
}
通过上面的代码执行后Hibernate执行代码如下:
仔细观察会发现在删除Role的时候不仅删除了中间表中的数据 还把菜单menu中的数据也删除了,这出现了一个问题,到底是什么呢?经过观察 发现原来是cascade="all" 的问题,即:在删除操作时也将级联的操作menu表,所以也就把menu中的数据也清除了,因此在设定cascade的时候需要注意的是尽量不设成all,多数都是设置为save-update,为的就是防止不仅删除了中间数据同时误把关联数据也删除了。
好了 通过上面简单的描述 基本上把many-to-many常用的配置表示完成!