表关系的分析
数据库中多表之间存在着三种关系:
表与表的三种关系:
一对多|多对一
建表原则:在多的一方创建外键指向一的一方的主键
表中的表达
实体中的表达
orm元数据表达
- 多对一配置
<!--表示多对一-->
<many-to-one name="customer"
column="lkm_cust_id"
class="Customer">
</many-to-one>
这里的这个配置表示的是:当前的这个配置对象存在多对一的情况,其中name表示实体中多对一的一的名字,column表示外键的列名 class表示的是多个对一个的一的实体类名
- 一对多配置
<!--配置集合 一对多的关系-->
<set name="linkMens">
<!--指定外键列名-->
<key column="lkm_cust_id"></key>
<!--指定一对多还是多对多
class指定是与哪个类型一对多
-->
<one-to-many class="LinkMan"/>
</set>
这里的这个配置表示的是:当前的这个配置对象存在多对一的情况,其中name表示实体中多个的set集合,key column表示外键的列名 class表示的是一对多的多的实体类名
/**
*保存客户以及客户下的联系人
*/
@Test
public void fun1() {
//获得session
Session session = HibernateUtils.openSession();
//开启事物
Transaction tx = session.beginTransaction();
//----------------------------
//操作数据
Customer customer = new Customer();
customer.setCust_name("xxx");
LinkMan lm1 = new LinkMan();
lm1.setLkm_name("aaa1");
LinkMan lm2 = new LinkMan();
lm2.setLkm_name("aaa2");
//表达一对多关系 客户下有多个联系人
customer.getLinkMens().add(lm1);
customer.getLinkMens().add(lm2);
//表达多对一 联系人属于哪个客户
lm1.setCustomer(customer);
lm2.setCustomer(customer);
//瞬时状态对象---->持久化状态
session.save(customer);
session.save(lm1);
session.save(lm2);
//----------------------------
//提交事物
tx.commit();
//关闭资源
session.close();
}
/**
* 为客户添加联系人
*/
@Test
public void fun2() {
//获得session
Session session = HibernateUtils.openSession();
//开启事物
Transaction tx = session.beginTransaction();
//----------------------------
//操作数据
//获得要操作的客户对象
Customer customer = session.get(Customer.class, 1l);
//创建联系人
LinkMan lm1 = new LinkMan();
//将联系人添加到客户,将客户设置到联系人中
customer.getLinkMens().add(lm1);
lm1.setCustomer(customer);
//执行保存
session.save(customer);
session.save(lm1);
//----------------------------
//提交事物
tx.commit();
//关闭资源
session.close();
}
/**
* 从客户中删除某个联系人
*/
@Test
public void fun3() {
//获得session
Session session = HibernateUtils.openSession();
//开启事物
Transaction tx = session.beginTransaction();
//----------------------------
//操作数据
//获得客户
Customer c = session.get(Customer.class, 1l);
LinkMan lm = session.get(LinkMan.class, 3l);
//获得客户的联系人
//将要移除的联系人从客户的联系人集合中移除
c.getLinkMens().remove(lm);
//清空联系人的客户
lm.setCustomer(null);
//----------------------------
//提交事物
tx.commit();
//关闭资源
session.close();
}
上面的代码再删除某个联系人时由于数据都是查询出来的,都是出于持久态的对象,所以再事务进行提交时Hibernate会查看数据是否发生变化,发生变化后会将数据更新,所以不需要执行session.save()方法进行保存数据
操作进阶
在上面的操作中,我们在操作保存客户的联系人的时候,我们需要执行多条保存的代码,这样显然很麻烦,我们希望再保存客户的时候,将客户的联系人也一起保存。所以我们需要进行一些别的配置
- 级联操作
<!--级联操作:cascade
save-update:级联保存更新
delete:级联删除
all: save-update delete 级联保存更新和级联删除
级联操作:简化操作
-->
<set name="linkMens" cascade="all">
<!--配置集合 一对多的关系-->
<!--指定外键列名-->
<key column="lkm_cust_id"></key>
<!--指定一对多还是多对多
class指定是与哪个类型一对多
-->
<one-to-many class="LinkMan"/>
</set>
级联操作应用在一对多的多的时候,也可以实现保存联系人的时候将客户保存
- linkman.hbm.xml 配置级联操作
<!--表示多对一-->
<many-to-one name="customer"
column="lkm_cust_id"
class="Customer"
cascade="all"
>
操作代码
/**
* 级联操作 保存联系人 级联保存客户
*/
@Test
public void fun5() {
//获得session
Session session = HibernateUtils.openSession();
//开启事物
Transaction tx = session.beginTransaction();
//----------------------------
//操作数据
//创建客户
Customer customer = new Customer();
customer.setCust_name("google company");
//创建联系人
LinkMan lm = new LinkMan();
lm.setLkm_name("劈柴哥");
//为客户添加联系人
customer.getLinkMens().add(lm);
//为联系人指定客户
lm.setCustomer(customer);
//保存客户----> 只保存联系人 通过级联操作保存客户
session.saveOrUpdate(lm);
//----------------------------
//提交事物
tx.commit();
//关闭资源
session.close();
}
在平时开发中,建议使用save-update 不建议使用delete 级联删除会删除相关的所有数据
- 关系维护
我们查看操作时Hibernate产生的SQL语句时发现了如下的语句
我们发现。在保存时,客户和联系人两房都会维护外键的关系,关系会维护两次,这样冗余了。多余的维护关系语句。显然是客户这一端在维护关系。所以我们需要优化这种情况 这就引入了Hibernate在实体类配置的时候提供的配置 inverse
inverse是为了提高效率
<!--
inverse属性:配置关系是否维护
配置当前的Customer是否维护与LinkMan的关系
inverse true 的意思就是将关系维护的工作完全交给对方 作为配置方不进行维护关系
* 不维护关系无法在删除的时候将外键置为空
false(默认值) 意思是将关系维护的工作由配置方来维护
原则:无论关系方如何放弃关系,总有一方必须要维护关系
一对多关系当中,也只能是一的一方放弃维护,多的一方不能放弃
-->
<set name="linkMens" inverse="true">
<key column="lkm_cust_id"></key>
class指定是与哪个类型一对多
<one-to-many class="LinkMan"/>
</set>
tips:在开发中的删除数据,结合inverse 和cascade 实现不同的数据删除策略。以这个demo为例,一个客户可以由多个联系人。在这种情况下 相当于客户和联系人维持着某种关系。我们在有需求删除客户的时候对联系人的删除有两种。
第一种:删除Customer的时候,将LinkMan的外键置为空,LinkMan数据保留
这种情况下,如果Customer不维护数据的话是无法操作LinkMan的外键的,所以在这种情况下我们需要让Customer inverse配置为false 让customer维护关系,在这种情况下删除Customer的时候会删除LinkMan的关系。
第二种:删除Customer的时候,级联操作将LinkMan的数据删除。
在这种情况下,将Customer inverse设置为true 并且将cascade指定为delete 这样在删除Customer的同时就会删除LinkMan子表中的数据。
多对多关联关系映射操作
建表原则:创建一个中间表,中间表中至少两个字段作为外键分别指向多对多双方的主键
表中关系表达
实体中的表达
orm元数据表达
- User表配置
<set name="roles" table="sys_user_role">
<!--
key标签
column:当前对象在中间表中的外键名称
-->
<key column="user_id"></key>
<!--
many-to-many标签:
class:关联另一方的类的全类名
column 关联的另一方在中间表的外键名称
-->
<many-to-many class="Role" column="role_id"></many-to-many>
</set>
- role表配置
<!--Role多对多配置-->
<set name="users" table="sys_user_role">
<key column="role_id"/>
<many-to-many class="User"
column="user_id"/>
</set>
多对多操作
我们使用案例:保存员工以及角色
在多对多关系中一定要有一方放弃关系的维护,否则会出现主键重复错误
放弃关系维护在配置文件中使用 inverse=“true” 表示放弃操作
- 配置文件
<!-- inverse 属性 使得一方放弃维护外键关系-->
<set name="users" table="sys_user_role" inverse="true">
<key column="role_id"/>
<many-to-many class="User"
column="user_id"/>
</set>
- 实例代码
//从工厂获得session
Session session = HibernateUtil.openSession();
//开启事物
Transaction tx = session.beginTransaction();
//操作数据
//-------------------------------
//创建两个User
User user1 = new User();
User user2 = new User();
user1.setUser_name("blackman");
user2.setUser_name("whiteman");
//创建两个role
Role role1 = new Role();
Role role2 = new Role();
Role role3 = new Role();
role1.setRole_name("vp");
role2.setRole_name("cto");
role3.setRole_name("security");
//用户表达关系
user1.getRoles().add(role1);
user1.getRoles().add(role2);
user2.getRoles().add(role1);
user2.getRoles().add(role3);
//角色表达关系
role1.getUsers().add(user1);
role1.getUsers().add(user2);
role2.getUsers().add(user1);
role3.getUsers().add(user2);
//保存到数据库
session.save(user1);
session.save(user2);
session.save(role1);
session.save(role2);
session.save(role3);
//-------------------------------
//提交事务
tx.commit();
//关闭资源
session.close();
@Test
/**
* 添加角色到某个用户中
*/
public void addRoleToUser() {
Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction();
//操作
//------------
//获得要添加的用户
User user = session.get(User.class, 1l);
//创建公关角色
Role role = new Role();
role.setRole_name("公关");
//将角色添加到用户
user.getRoles().add(role);
//将角色转换为持久化
session.save(role);
//------------
tx.commit();
session.close();
}
/**
* 从指定角色删除
*/
@Test
public void delRoleFromUser(){
//获取session
Session session = HibernateUtil.openSession();
//开启事物
Transaction tx = session.beginTransaction();
//操作数据
//-------------
//获得要删除的数据
User user = session.get(User.class, 3l);
//获得要删除的角色数据
Role role = session.get(Role.class, 7l);
user.getRoles().remove(role);
//-------------
//提交事务
tx.commit();
//关闭资源
session.close();
}
进阶操作
- 级联操作
cascade: 级联操作
在User.hbm.xml中配置cascade后
<set name="roles" table="sys_user_role" cascade="save-update">
可以进行级联保存 上面的操作代码不再需要
session.save(role)
一对一
- 建表原则:
- 1 唯一外键对应:假设一对一种的任意一方为多,在多的一方创建外键指向一的一方的主键,然后将外键设置为唯一。
-
2 主键对应:一方的主键作为另一方的主键