Hibernate学习笔记(四)—— 表与表的关系
一、一对多|多对一
1.1 关系表达
【表中的表达】
建表原则:在多的一方创建外键指向一的一方的主键。
【实体中的表达】
客户实体
public class Customer { private Long cust_id;
private String cust_name;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_linkman;
private String cust_phone;
private String cust_mobile; // 一个客户有多个联系人:客户中应该放有联系人的集合
private Set<LinkMan> linkMans = new HashSet<>(); get/set...
}
联系人实体
public class LinkMan { private Long lkm_id;
private String lkm_name;
private String lkm_gender;
private String lkm_phone;
private String lkm_mobile;
private String lkm_email;
private String lkm_qq;
private String lkm_position;
private String lkm_memo; // Hibernate是一个ORM框架:在关系型数据库中描述表与表之间的关系,使用的是外键。开发语言使用的是Java,面向对象的
private Customer customer;
get/set...
}
【配置文件中的表达】
客户的映射
<?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 package="cn.itcast.domain" >
<class name="Customer" table="cst_customer" >
<id name="cust_id" >
<generator class="native"></generator>
</id> <property name="cust_name" column="cust_name" ></property>
<property name="cust_source" column="cust_source" ></property>
<property name="cust_industry" column="cust_industry" ></property>
<property name="cust_level" column="cust_level" ></property>
<property name="cust_linkman" column="cust_linkman" ></property>
<property name="cust_phone" column="cust_phone" ></property>
<property name="cust_mobile" column="cust_mobile" ></property> <!-- 配置关联对象 -->
<!-- name属性:多的一方集合的属性名称 -->
<set name="linkMans">
<!-- column属性:多的一方外键的名称 -->
<key column="lkm_cust_id"></key>
<!-- 多的一方类的全路径,如果前面的package中设置了包名,这里填类名即可 -->
<one-to-many class="LinkMan"/>
</set>
</class>
</hibernate-mapping>
联系人的映射
<?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 package="cn.itcast.domain" >
<class name="LinkMan" table="cst_linkman" >
<id name="lkm_id" >
<generator class="native"></generator>
</id> <property name="lkm_name" column="lkm_name" ></property>
<property name="lkm_gender" column="lkm_gender" ></property>
<property name="lkm_phone" column="lkm_phone" ></property>
<property name="lkm_mobile" column="lkm_mobile" ></property>
<property name="lkm_email" column="lkm_email" ></property>
<property name="lkm_qq" column="lkm_qq" ></property>
<property name="lkm_position" column="lkm_position" ></property>
<property name="lkm_memo" column="lkm_memo" ></property> <!-- 配置关联对象 -->
<!-- name:一的一方的对象的名称
class:一的一方类的全路径
column:表中外键的名称
-->
<many-to-one name="customer" class="Customer" column="lkm_cust_id"/>
</class>
</hibernate-mapping>
1.2 测试代码
- 保存
// 保存一个客户和两个联系人
@Test
public void testSave() throws Exception {
// 获得session
Session session = HibernateUtils.openSession();
// 开启事务
Transaction tx = session.beginTransaction();
// 创建一个客户
Customer customer = new Customer();
customer.setCust_name("李总"); // 创建两个联系人
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("张秘书");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("王助理"); // 表达一对多,客户下有多个联系人
customer.getLinkMans().add(linkMan1);
customer.getLinkMans().add(linkMan2); // 表达多对一,联系人属于哪个客户
linkMan1.setCustomer(customer);
linkMan2.setCustomer(customer); session.save(customer);
session.save(linkMan1);
session.save(linkMan2); // 提交事务
tx.commit();
// 关闭资源
session.close();
} - 增加
// 为客户增加联系人
@Test
public void testAdd() throws Exception {
// 获得session
Session session = HibernateUtils.openSession();
// 开启事务
Transaction tx = session.beginTransaction(); // 获得要操作的客户对象
Customer customer = session.get(Customer.class, 1l);
// 创建联系人
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("赵秘书");
// 将联系人添加到客户中
customer.getLinkMans().add(linkMan);
// 将客户设置到联系人中
linkMan.setCustomer(customer);
// 执行保存
session.save(linkMan); tx.commit();
session.close();
} - 删除
// 为客户删除联系人
@Test
public void testDelete() throws Exception {
// 获得session
Session session = HibernateUtils.openSession();
// 开启事务
Transaction tx = session.beginTransaction(); // 获得要操作的客户对象
Customer customer = session.get(Customer.class, 1l);
// 获得要移除的联系人
LinkMan linkMan = session.get(LinkMan.class, 2l);
// 将联系人从客户集合中移除
customer.getLinkMans().remove(linkMan);
linkMan.setCustomer(null); tx.commit();
session.close();
}
1.3 级联保存或更新
级联操作是指当主控方执行保存、更新或者删除操作时,其关联的对象(被控方)也执行相同的操作。在映射文件中通过对cascade属性的设置来控制是否对关联对象采用级联操作,级联操作对各种关联关系都是有效的。
级联是有方向性的,所谓的方向性指的是,在保存一的一方级联多的一方,和在保存多的一方级联一的一方。
【保存客户级联联系人】
首先要确定我们要保存的主控方是哪一方,我们要保存客户,所以客户是主控方,那么需要在客户的映射文件中(Customer.hbm.xml)进行如下配置:
<!-- 配置关联对象 -->
<!-- name属性:多的一方集合的属性名称 -->
<set name="linkMans" cascade="save-update">
<!-- column属性:多的一方外键的名称 -->
<key column="lkm_cust_id"></key>
<!-- 多的一方类的全路径,如果前面的package中设置了包名,这里填类名即可 -->
<one-to-many class="LinkMan"/>
</set>
编写测试代码:
// 级联保存:只保存一边的问题
// 级联是有方向性的,保存客户同时级联客户的联系人
// 在Customer.hbm.xml的<set>标签上配置cascade="save-update"
@Test
public void testCascade() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 创建一个客户
Customer customer = new Customer();
customer.setCust_name("刘总"); // 创建联系人
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("王秘书"); // 建立关系
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer); session.save(customer); tx.commit();
session.close();
}
【保存联系人级联客户】
同样我们需要确定主控方,现在我们的主控方是联系人。所以需要在联系人的映射文件中(LinkMan.hbm.xml)进行配置:
<!-- 配置关联对象 -->
<!-- name:一的一方的对象的名称
class:一的一方类的全路径
column:表中外键的名称
-->
<many-to-one name="customer" cascade="save-update" class="Customer" column="lkm_cust_id"/>
测试代码:
// 级联保存:只保存一边的问题
// 级联是有方向性的,保存联系人同时级联客户
// 在LinkMan.hbm.xml的<many-to-one>标签上配置cascade="save-update"
@Test
public void testCascade2() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); // 创建客户
Customer customer = new Customer();
customer.setCust_name("张总"); // 创建联系人
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("柳助理"); // 建立关系
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer); session.save(linkMan); tx.commit();
session.close();
}
到这我们已经可以看到级联保存或更新的效果了。那么我们维护的时候都是双向的关系维护。这种关系到底表述的是什么含义呢?我们可以通过下面的测试来更进一步了解关系的维护(对象导航)的含义。
1.4 测试对象导航的问题
我们所说的对象导航其实就是在维护双方的关系。
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);
这种关系有什么用途呢?我们可以通过下面的测试来更进一步学习Hibernate。
我们在客户和联系人端都配置了cascade="save-update",然后进行如下的关系设置。会产生什么样的效果呢?
// 测试对象导航和级联操作
@Test
public void testCascade3() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); // 创建一个客户
Customer customer = new Customer();
customer.setCust_name("赵总"); // 创建三个联系人
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("李秘书");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("王秘书");
LinkMan linkMan3 = new LinkMan();
linkMan3.setLkm_name("张助理"); // 建立关系
linkMan1.setCustomer(customer);
customer.getLinkMans().add(linkMan2);
customer.getLinkMans().add(linkMan3); // 条件是双方都设置了cascade="save-update"
session.save(linkMan1); // 数据库中有几条记录?发送了几条insert语句? 4条
// session.save(customer); // 发送了几条insert语句? 3条
// session.save(linkMan2); // 发送了几条insert语句? 1条 tx.commit();
session.close();
}
我们在执行第25行代码时,问会执行几条insert语句?其实发现会有4条insert语句,因为联系人1关联了客户,客户又关联了联系人2和联系人3,所以当保存联系人1的时候,联系人1是可以进入到数据库的,它关联的客户也是会进入到数据库的(因为联系人一端配置了级联),那么客户进入到数据库以后,联系人2和联系人3也同样会进入到数据库(因为客户一端配置了级联),所以会有4条insert语句。
我们在执行26行的时候,问会有几条insert语句?这时我们保存的客户对象关联了联系人2和联系人3,那么客户在进入数据库的时候,联系人2和联系人3也会进入到数据库,所以是3条insert语句。
同理我们执行27行代码的时候,只会执行1条insert语句。因为联系人2会保存到数据库,但是联系人2没有对客户建立关系,所以客户不会保存到数据库。
1.5 Hibernate的级联删除
我们之前学习过级联保存或更新,那么再来看级联删除也就不难理解了,级联删除也是有方向性的,删除客户同时级联删除联系人,也可以删除联系人同时级联删除客户(这种需求很少)。
原来JDBC中删除客户和联系人的时候,如果有外键的关系是不可以删除的,但是现在我们使用了Hibernate,其实Hibernate可以实现这样的功能,但是不会删除客户的同时删除联系人,默认情况下Hibernate会怎么做呢?我们来看下面的测试:
// 删除有级联关系的对象:(默认情况:先将关联对象的外键置为null,再删除)
@Test
public void demo5() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); Customer customer = session.get(Customer.class, 1l);
session.delete(customer); tx.commit();
session.close();
}
默认情况下如果客户下面还有联系人,Hibernate会将联系人的外键置为null,然后区删除客户。那么其实有的时候我们需要删除客户的时候,同时将客户关联的联系人一起删除。这个时候我们就需要使用Hibernate的级联删除操作了。
【删除客户的同时删除客户的联系人】
确定删除的主控方是客户,所以需要在客户端(Customer.hbm.xml)配置:
<!-- 配置关联对象 -->
<!-- name属性:多的一方集合的属性名称 -->
<set name="linkMans" cascade="delete">
<!-- column属性:多的一方外键的名称 -->
<key column="lkm_cust_id"></key>
<!-- 多的一方类的全路径,如果前面的package中设置了包名,这里填类名即可 -->
<one-to-many class="LinkMan"/>
</set>
如果还想有之前的级联保存或更新,同时还想有级联删除,那么我们可以进行如下的配置:
<!-- 配置关联对象 -->
<!-- name属性:多的一方集合的属性名称 -->
<set name="linkMans" cascade="delete,save-update">
<!-- column属性:多的一方外键的名称 -->
<key column="lkm_cust_id"></key>
<!-- 多的一方类的全路径,如果前面的package中设置了包名,这里填类名即可 -->
<one-to-many class="LinkMan"/>
</set>
测试代码:
// 级联删除:级联删除有方向性
// 删除客户的同时删除联系人
// 在Customer.hbm.xml的<set>标签上配置cascade="delete"
@Test
public void demo6() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); // 级联删除:必须是先查询再删除
// 因为查询到客户,这个时候客户的联系人集合就会有数据
Customer customer = session.get(Customer.class, 7l);
session.delete(customer); tx.commit();
session.close();
}
【删除联系人的同时删除客户】
这时候我们删除的是联系人,那么联系人就是主控方,需要在联系人端(LinkMan.hbm.xml)配置:
<!-- 配置关联对象 -->
<!-- name:一的一方的对象的名称
class:一的一方类的全路径
column:表中外键的名称
-->
<many-to-one name="customer" cascade="delete" class="Customer" column="lkm_cust_id"/>
如果既要保存或更新有级联操作,又要有级联删除的功能,也可以如下配置:
<!-- 配置关联对象 -->
<!-- name:一的一方的对象的名称
class:一的一方类的全路径
column:表中外键的名称
-->
<many-to-one name="customer" cascade="delete,save-update" class="Customer" column="lkm_cust_id"/>
测试代码:
// 级联删除:级联删除有方向性
// 删除联系人的同时删除客户
// 在LinkMan.hbm.xml中的<many-to-one>标签上配置cascade="delete"
@Test
public void demo7() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); // 级联删除:必须是先查询再删除
LinkMan linkMan = session.get(LinkMan.class, 4l);
session.delete(linkMan); tx.commit();
session.close();
}
1.6 双向关联产生多余的SQL语句
【客户表】
【联系人表】
有时候我们需要进行如下操作:将2号联系人关联给2号客户,也就是将2号李秘书这个联系人关联给2号刘总这个客户。
编写修改2号客户关联的联系人的代码:
// 将2号联系人关联的客户改为2号客户
@Test
public void demo8() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); Customer customer = session.get(Customer.class, 2l);
LinkMan linkMan = session.get(LinkMan.class, 2l); linkMan.setCustomer(customer);
customer.getLinkMans().add(linkMan); tx.commit();
session.close();
}
运行上述代码,控制台输出如下内容:
我们发现执行了两次update语句,其实这两个update都是修改了外键的操作,那么为什么发送两次呢?我们来分析一下产生这个问题的原因:
我们已经分析过了,因为双向维护了关系,而且持久态对象可以自动更新数据库,更新客户的时候会修改一次外键,更新联系人的时候同样会修改一次外键。这样就会产生多余的SQL。问题产生了,我们该如何解决呢?
其实解决的办法很简单,只需要一方放弃外键维护权即可。也就是说关系不是双方维护的,只需要交给某一方去维护就可以了。通常我们都是交给多的一方去维护。为什么呢?因为多的一方才是维护关系最好的地方。举个例子,一个老师对应多个学生,一个学生对应一个老师,这时典型的一对多。一个老师如果要记住所有学生的名字很难,但如果让每个学生记住老师的名字应该不难。其实就是这个道理。所有在一对多中,一的一方都会放弃外键的维护权(关系的维护);
这个时候如果想让一的一方放弃外键的维护权,只需要进行如下配置即可:
<!-- 配置关联对象 -->
<!-- name属性:多的一方集合的属性名称 -->
<set name="linkMans" cascade="save-update" inverse="true">
<!-- column属性:多的一方外键的名称 -->
<key column="lkm_cust_id"></key>
<!-- 多的一方类的全路径,如果前面的package中设置了包名,这里填类名即可 -->
<one-to-many class="LinkMan"/>
</set>
inverse的默认值是false,代表不放弃外键的维护权,配置值为true,代表放弃了外键的维护权。这个时候再来执行之前的操作:
这时候就不会出现上述问题了,不会产生多余的SQL了(因为一的一方已经放弃了外键的维护权)。
1.7 区分cascade和inverse
// cascade和inverse
// cascade强调的是操作一个对象的时候,是否操作其关联的对象
// inverse强调的是外键的维护权
@Test
public void demo9() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); Customer customer = new Customer();
customer.setCust_name("王总"); LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("赵秘书"); // 在Customer.hbm.xml的<set>标签上配置cascade="save-update" inverse="true"
customer.getLinkMans().add(linkMan); session.save(customer); tx.commit();
session.close();
}
这时候我们会发现,如果在<set>标签上配置cascade="save-update" inverse="true",那么执行保存客户的操作时,会发现客户和联系人都进入到数据库了,但是没有外键。是因为配置了cascade="save-update"所以客户关联的联系人会进入到数据库中,但是客户一端放弃了外键的维护权(inverse="true"),所以联系人插入到数据库以后是没有外键的。
二、多对多
2.1 关系表达
【表中的表达】
【对象中的表达】
用户实体
public class User {
private Long user_id;
private String user_code;
private String user_name;
private String user_password;
private String user_state; // 用户所属的角色集合
private Set<Role> roles = new HashSet<>(); get/set...
}
角色实体
public class Role {
private Long role_id;
private String role_name;
private String role_memo; // 一个角色包含多个用户
private Set<User> users = new HashSet<>(); get/set...
}
【配置文件中的表达】
用户的映射——User.hbm.xml
<?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 package="cn.itcast.domain" >
<class name="User" table="sys_user" >
<id name="user_id" >
<generator class="native"></generator>
</id> <property name="user_code"/>
<property name="user_name"/>
<property name="user_password"/>
<property name="user_state"/> <!-- 多对多关系表达 -->
<!-- name:集合属性名
table:配置中间表名
-->
<set name="roles" table="sys_user_role">
<!-- column:外键,别人引用"我"的外键列名 -->
<key column="user_id"></key>
<!-- class:我与哪个类是多对多关系
column:外键.我引用别人的外键列名
-->
<many-to-many class="Role" column="role_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
角色的映射——Role.hbm.xml
<?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 package="cn.itcast.domain" >
<class name="Role" table="sys_role" >
<id name="role_id" >
<generator class="native"></generator>
</id> <property name="role_name"/>
<property name="role_memo"/> <!-- 多对多关系表达 -->
<!-- name:集合属性名
table:配置中间表名
-->
<set name="users" table="sys_user_role">
<!-- column:外键,别人引用"我"的外键列名 -->
<key column="role_id"></key>
<!-- class:我与哪个类是多对多关系
column:外键.我引用别人的外键列名
-->
<many-to-many class="User" column="user_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
在hibernate.cfg.xml中加入映射文件
<mapping resource="cn/itcast/domain/User.hbm.xml" />
<mapping resource="cn/itcast/domain/Role.hbm.xml" />
2.2 测试方法
- 保存
// 保存用户及角色
@Test
public void fun1() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); // 创建两个User
User user1 = new User();
user1.setUser_name("张三"); User user2 = new User();
user2.setUser_name("李四"); // 创建两个Role
Role role1 = new Role();
role1.setRole_name("保洁"); Role role2 = new Role();
role2.setRole_name("保安"); // 用户表达关系
user1.getRoles().add(role1);
user1.getRoles().add(role2); user2.getRoles().add(role2); // 角色表达关系
role1.getUsers().add(user1);
role2.getUsers().add(user1);
role2.getUsers().add(user2); // 调用save方法一次保存
session.save(user1);
session.save(user2);
session.save(role1);
session.save(role2); tx.commit();
session.close();
}执行上述代码时,我们发现报错了:
【错误原因分析】
映射文件中有一个inverse属性,不配置时默认为false,即要维护关系。这里两方关系都设置了{user1.getRoles().add(role1);role1.getUsers().add(user1);},从配置上讲,这两方都没放弃维护。多对多维护关系的手段是向中间表(sys_user_role)插入记录,如果要表达1号用户的角色是1号,那么中间表就会插入1-1记录。现在的情况是两方都默认维护关系,role在维护关系的时候,表达1-1,会在中间表插入1-1记录;user在维护关系的时候,也要往中间表插入数据,这时候user又要插入1-1记录。中间表往往是这两个id共同作为主键的,这两个1-1就会出现主键重复。
之前的一对多两方都维护关系,却没发生异常,是因为它们维护关系,就是修改了外键字段,不存在向中间表插入记录,所以修改两次是没有问题的,至少不会报错,虽然效率会低点。而多对多维护关系是向中间表插入记录,两方都维护的时候,就会插入两条一样的记录,就会产生主键重复。
【解决方案】
方法一:映射文件不改,从代码上来讲,只要一方不表达关系即可,这样在保存的时候,因为代码上并没有表达关系的逻辑,就不会给你操作了。这里我们可以让role的一方不表达关系,在代码上把相关代码注释,此时方法就可执行成功
方法二:代码不改,在一方的映射文件中修改inverse为true,让它放弃维护关系。这里修改Role.hbm.xml
【结论】
在多对多的保存操作中,如果进行了双向维护关系,就必须有一方放弃外键维护权。一般由被动方放弃,用户主动选择角色,角色是被选择的,所以一般角色要放弃外键维护权。但如果只进行单向维护关系,那么就不需要放弃外键维护权了。
2.3 级联保存或更新
【保存用户级联角色】
保存的主控方是用户,需要在用户一端(User.hbm,xml)配置
测试代码:
// 保存用户级联角色
@Test
public void fun2() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); // 创建两个User
User user1 = new User();
user1.setUser_name("刘备"); User user2 = new User();
user2.setUser_name("关羽"); // 创建三个角色
Role role1 = new Role();
role1.setRole_name("人事管理");
Role role2 = new Role();
role2.setRole_name("行政管理");
Role role3 = new Role();
role3.setRole_name("财务管理"); // 建立关系:如果建立了双向的关系,一定要有一方放弃外键维护权
user1.getRoles().add(role1);
user1.getRoles().add(role2); user2.getRoles().add(role3);
user2.getRoles().add(role2); role1.getUsers().add(user1);
role2.getUsers().add(user1);
role2.getUsers().add(user2);
role3.getUsers().add(user2); session.save(user1);
session.save(user2);
/*session.save(role1);
session.save(role2);
session.save(role3);*/ tx.commit();
}
【保存角色级联用户】
保存的主控方是角色,需要在角色一端配置:
测试代码:
// 保存角色级联用户
@Test
public void fun2() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); // 创建两个User
User user1 = new User();
user1.setUser_name("刘备"); User user2 = new User();
user2.setUser_name("关羽"); // 创建三个角色
Role role1 = new Role();
role1.setRole_name("人事管理");
Role role2 = new Role();
role2.setRole_name("行政管理");
Role role3 = new Role();
role3.setRole_name("财务管理"); // 建立关系:如果建立了双向的关系,一定要有一方放弃外键维护权
user1.getRoles().add(role1);
user1.getRoles().add(role2); user2.getRoles().add(role3);
user2.getRoles().add(role2); role1.getUsers().add(user1);
role2.getUsers().add(user1);
role2.getUsers().add(user2);
role3.getUsers().add(user2); /*session.save(user1);
session.save(user2);*/
session.save(role1);
session.save(role2);
session.save(role3); tx.commit();
}
2.4 级联删除(了解)
在多对多中级联删除是不会使用的,因为我们不会有这类的需求,比如删除用户,将用户关联的角色一起删除,或者删除角色的时候将用户删除掉,这时不合理的。但是级联删除的功能Hibernate已经提供了此功能,所以我们只需要了解即可。
【删除用户级联角色】
主控方是用户,所以需要在用户端配置
测试代码:
// 级联删除:删除用户,级联删除角色
@Test
public void fun3() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); User user = session.get(User.class, 1l);
session.delete(user); tx.commit();
session.close();
}
2.5 多对多的其他操作
- 删除某个用户的角色
// 将1号用户的1号角色去掉
@Test
public void fun4() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); // 查询1号用户
User user = session.get(User.class, 1l);
// 查询1号角色
Role role = session.get(Role.class, 1l); // 操作集合 相当于操作数据库
user.getRoles().remove(role); tx.commit();
} - 将某个用户的角色改选
// 将1号用户的2号角色去掉,改为3号角色
@Test
public void fun5() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); // 查询1号用户
User user = session.get(User.class, 1l);
// 查询2号角色
Role role1 = session.get(Role.class, 2l);
// 查询3号角色
Role role2 = session.get(Role.class, 3l); user.getRoles().remove(role1);
user.getRoles().add(role2); tx.commit();
} - 给某个用户添加新角色
// 给1号用户添加2号角色
@Test
public void fun6() throws Exception {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction(); // 查询1号用户
User user = session.get(User.class, 1l);
// 查询2号角色
Role role = session.get(Role.class, 2l); user.getRoles().add(role); tx.commit();
}
Hibernate学习笔记(四)—— 表与表的关系的更多相关文章
- Hibernate学习笔记(一)-->数据库单表操作
Hibernate框架是一个全ORM映射框架,是一个非常流行的数据库操作框架之一,现在比较流行的还有MyBatis半ORM映射框架 在MyEclipse IDE开发工具中,可以很轻松的搭建Hibern ...
- hibernate学习笔记(4)表单操作
User.hbm.xml的表单配置: ①主键 <id name="id" type="java.lang.Integer"> <column ...
- iView学习笔记(四):Form表单操作
1.后端准备 环境说明 python版本:3.6.6 Django版本:1.11.8 数据库:MariaDB 5.5.60 新建Django项目,在项目中新建app,配置好数据库 2.后端代码(基于C ...
- Hibernate学习笔记四 查询
HQL语法 1.基本语法 String hql = " from com.yyb.domain.Customer ";//完整写法 String hql2 = " fro ...
- Hibernate学习笔记四:事务管理
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6768298.html 一:需要事务的Session操作 Session操作中,查询类操作是不需要事务就能生效 ...
- Hibernate学习笔记四
1 整合log4j(了解) l slf4j 核心jar : slf4j-api-1.6.1.jar .slf4j是日志框架,将其他优秀的日志第三方进行整合. l 整合导入jar包 log4j 核心包 ...
- Django学习笔记(五)—— 表单
疯狂的暑假学习之 Django学习笔记(五)-- 表单 參考:<The Django Book> 第7章 1. HttpRequest对象的信息 request.path ...
- Flutter学习笔记(13)--表单组件
如需转载,请注明出处:Flutter学习笔记(13)--表单组件 表单组件是个包含表单元素的区域,表单元素允许用户输入内容,比如:文本区域,下拉表单,单选框.复选框等,常见的应用场景有:登陆.注册.输 ...
- 【转载】salesforce 零基础开发入门学习(四)多表关联下的SOQL以及表字段Data type详解
salesforce 零基础开发入门学习(四)多表关联下的SOQL以及表字段Data type详解 建立好的数据表在数据库中查看有很多方式,本人目前采用以下两种方式查看数据表. 1.采用schem ...
- Hibernate学习笔记(二)
2016/4/22 23:19:44 Hibernate学习笔记(二) 1.1 Hibernate的持久化类状态 1.1.1 Hibernate的持久化类状态 持久化:就是一个实体类与数据库表建立了映 ...
随机推荐
- Python将两个数组合并成一个数组,多维数组变成一维数组
1.extend方法 c1 = ["Red","Green","Blue"] c2 = ["Orange"," ...
- 代码查看import的类是出自哪个jar包的方法(转)
import java.security.ProtectionDomain; import java.security.CodeSource; public static void main(Stri ...
- Server嵌套事务处理的方法
源文档 http://wenku.baidu.com/link?url=yUH8Yhb8isIvJb8A7c0Hv_ktFSLt-JTvrQd2e2TGmFwzwGWqkjFfb1tXv5ZR1FmP ...
- 再谈JavaScript的closure--JavaScript 闭包
关于JavaScript的闭包,在我的博客上之前有一篇文章 https://www.cnblogs.com/wphl-27/p/8491327.html 今天看了几篇文章,感觉又有了一些更深的理解,特 ...
- 《the art of software testing》 第三章 人工测试
在深入研究较为传统的计算机测试技术之前,要先进行"人工测试". 代码检查与走查是两种主要的人工测试方法. 代码检查与走查是对过去桌面检查过程(在提交测试前由程序员阅读自己程序的过程 ...
- backquote
character (`) A backquote or backtick. echo 'date' date echo `date` 2015年 07月 03日 星期五 16:11:13 CST j ...
- 编写高质量代码改善C#程序的157个建议——建议44:理解委托中的协变
建议44:理解委托中的协变 委托中的泛型变量天然是部分支持协变的.为什么是“部分支持协变”?看下面示例: class Program { public delegate T GetEmployeeHa ...
- fork()的写时复制技术(转载)
本文转载自http://www.cnblogs.com/wuchanming/p/4495479.html,为了方便以后查看... 写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当 ...
- LibreOJ 6003 魔术球 (最大流)
题解:每次加入两个点,对于和为平方数的两个值所对应的点建边,反正网络流可以跑残量网络,所以就没有什么关系了…… 代码如下: #include<cmath> #include<queu ...
- Js杂谈-插件包读后感
最近有幸得到了一份项目上的前端封装的插件库代码,花了一个下午时间,仔细地研读了一下.对于我很想做自己的类库,搞自己的组件包很有启蒙意义. 相比较我之前阅过的框架或是类库,这份比较简单. 项目是jQue ...