导航
表与表关系的回顾
一对一
- 一夫一妻制
- 通常一张表就能表达
一对多(客户和联系人)
- 分类和商品的关系,一个分类有多个商品而一个商品只能属于一个分类
- 例子:客户:与公司有业务往来;联系人:公司里面的员工,百度里面有很多的员工,联系员工就能练习客户,类似于公司和公司员工的关系
- 建表时通过
外键
建立关系,一为主表,多为从表
多对多(用户和角色)
- 订单和商品的关系,一个订单可以有多个商品且一个商品可以属于多个订单
- 建表时通过
维护第三张表
建立关系
Hibernate 一对多操作
一对多映射配置
- 第一步 创建实体类,客户(一) and 联系人(多)
- 第二步 让这两个实体类之间互相表示
(1)在客户实体类中表示多个联系人:一个客户里面有多个联系人
(2)在联系人实体类里面表示所属客户:一个联系人只能属于一个客户//hibernate 要求使用集合表示多的数据,使用set集合 //set集合是无序的,可有重复元素 //在客户实体类中加上下列属性以及相应set和get方法 //表示一个客户中有多个联系人 private Set<LinkMan> setLinkMan = new HashSet<LinkMan>(); //在联系人实体类中加上下列属性以及相应set和get方法 //表示一个联系人只有一个客户 private Customer customer;
- 第三步 配置映射关系
(1)一个实体类对应一个映射文件
(2)把映射的最基本配置完成
(3)在映射文件中配置一对多关系(重点
)<!-- Customer.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> <!-- 1 配置类和表对应 class标签 name属性:实体类全路径 table属性:数据库表名称 --> <class name="cn.itcast.entity.Customer" table="t_customer"> <id name="cid" column="cid"> <generator class="native"></generator> </id> <property name="custName" column="custName"></property> <property name="custLevel" column="custLevel"></property> <property name="custSource" column="custSource"></property> <property name="custPhone" column="custPhone"></property> <property name="custMobile" column="custMobile"></property> <!-- 在客户映射文件中,表示所有联系人 使用set标签表示所有联系人 set标签里面有name属性: 属性值写在客户实体类里面表示联系人的set集合名称 inverse属性默认值:false不放弃关系维护 true表示放弃关系维护 --> <set name="setLinkMan" inverse="true"> <!-- 一对多建表,有外键 hibernate机制:双向维护外键,在一和多那一方都配置外键 column属性值:外键名称 --> <key column="clid"></key> <!-- 客户所有的联系人,class里面写联系人实体类全路径 --> <one-to-many class="cn.itcast.entity.LinkMan"/> </set> </class> </hibernate-mapping> <!-- LinkMan.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> <!-- 1 配置类和表对应 class标签 name属性:实体类全路径 table属性:数据库表名称 --> <class name="cn.itcast.entity.LinkMan" table="t_linkman"> <id name="lkm_id" column="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> <!-- 表示联系人所属客户 name属性:因为在联系人实体类使用customer对象表示,写customer名称 class属性:customer全路径 column属性:外键名称 --> <many-to-one name="customer" class="cn.itcast.entity.Customer" column="clid"></many-to-one> </class> </hibernate-mapping>
- 第四步 创建核心配置文件,把映射文件引入核心配置文件
<!-- hibernate.cfg.xml --> <mapping resource="../../../LinkMan.hbm.xml"/>
- 测试:运行 HibernateUtils 工具类,检查是否报错以及是否创建了两个表格即可
一对多级联保存
- 添加一个客户,为该客户添加多个联系人
- 复杂版本
public void tesdAddDemo1(){ Session session = null; Transaction tx = null; try{ //获得session session = HibernateUtils.getSessionObject(); //开启事务 tx = session.beginTransaction(); //添加一个客户,并为这个客户添加联系人 //1 创建对象 Customer customer = new Customer(); customer.setCustName("DEMO1"); customer.setCustPhone("999"); LinkMan l = new LinkMan(); l.setLinkManName("demo1"); l.setLinkManPhone("911"); //2 在客户表示联系人,在联系人表示客户 //建立两个实体类对象之间的关系 //2.1 把联系人对象放入到客户实体类对象中的set集合里面 customer.getSetLinkMan().add(l); //2.2 把客户对象放入到联系人里 l.setCustomer(customer); //3 保存 session.save(customer); session.save(l); //提交 tx.commit(); }catch(Exception e){ //回滚 tx.rollback(); } }
- 简化版本
一般根据客户添加联系人,底层实现就是根据配置文件替代了建立关系的过程
(1)在客户映射文件中进行配置:在客户映射文件里的set标签进行配置,添加一个cascade属性
(2)创建客户和联系人对象,只需要把联系人放到客户里面,最终只需要保存客户<set name="setLinkMan" cascade="save-update">
public void tesdAddDemo2(){ Session session = null; Transaction tx = null; try{ //获得session session = HibernateUtils.getSessionObject(); //开启事务 tx = session.beginTransaction(); //添加一个客户,并为这个客户添加联系人 //1 创建对象 Customer customer = new Customer(); customer.setCustName("DEMO2"); customer.setCustPhone("111"); LinkMan l = new LinkMan(); l.setLinkManName("demo2"); l.setLinkManPhone("222"); //2 把联系人放到客户里面 customer.getSetLinkMan().add(l); //3 保存 session.save(customer); //提交 tx.commit(); }catch(Exception e){ //回滚 tx.rollback(); } }
一对多级联删除
- 删除一个客户,就要把该客户的联系人都删除
- 实现步骤
(1)在客户映射文件的 set 标签进行配置,把cascade
中的值加上delete
(2)在代码中直接删除客户(查询后用delete方法删除)<set name="setLinkMan" cascade="save-update,delete">
public void testDeleteDemo(){
Session session = null;
Transaction tx = null;
try{
//获得session
session = HibernateUtils.getSessionObject();
//开启事务
tx = session.beginTransaction();
//1 根据id查询客户对象
Customer c = session.get(Customer.class,3);
//2 调用方法删除
session.delete(c);
//提交
tx.commit();
}catch(Exception e){
//回滚
tx.rollback();
}
}
- Hibernate级联删除步骤
(1)根据id查询客户
(2)根据外键id值查询联系人
(3)把联系人外键设置为null
(4)删除联系人和客户
一对多修改
- 把
a
联系人所属的客户c
改成另外一个c1
- 主要就是修改联系人表的外键
public void testDeleteDemo(){
Session session = null;
Transaction tx = null;
try{
//获得session
session = HibernateUtils.getSessionObject();
//开启事务
tx = session.beginTransaction();
//1 根据id查询出联系人,根据id查询出客户
Customer c1 = session.get(Customer.class,1);
LinkMan a = session.get(LinkMan.class,2);
//2 设置持久态对象的值,持久太对象会自动提交
//把联系人放到客户
c1.getSetLinkMan().add(a);
//把客户放到联系人里
a.setCustomer(c1);
//提交
tx.commit();
}catch(Exception e){
//回滚
tx.rollback();
}
}
inverse
属性
(1) 在操作时会对外键值更改两次,原因:Hibernate中,外键是双向维护的,在两个对象中都会维护外键,因此在修改其中一个对象时会修改一次,修改另一个对象也会再修改一次。
(2) 多次的修改操作对数据库会有性能影响
(3) 解决方法:让其中一方放弃外键维护,在一对多的关系中,放弃维护外键的是一的那一方,因为对于多的那一方,是一对一的关系
(4) 实现:在放弃关系维护映射文件中进行配置,在set
标签上使用inverse
属性<!-- inverse 的默认属性值是FALSE,表示不放弃关系维护; 因此TRUE则表示放弃关系维护 --> <set name="setLinkMan" inverse="false">
Hibernate多对多操作
以用户和角色为例
多对多映射配置
- 第一步 创建实体类,用户和角色
- 第二步 让两个实体类之间互相表示(两端都是用
set
集合)
(1)一个用户里面有多个角色
(2)一个角色里面有多个用户private Set<Role> setRole = new HashSet<Role>(); private Set<User> setUser = new HashSet<User>();
- 第三步 配置映射关系
(1)基本配置(详细代码查看一对多关系中的基本配置)
(2)配置多对多关系<!-- User.hbm.xml --> <!-- 使用set标签 name:角色set集合的名称 table:第三张表名称 --> <set name="setRole" table="user_role"> <!-- key标签里面配置当前映射文件在第三张表外键名称 表示User在第三张表的外键名称 --> <key column="userid"></key> <!-- class:实体类全路径 column:在第三张表外键的名称 --> <many-to-many class="cn.itcast.manytomany.Role" column="roleid"></many-to-many> </set> <!-- Role.hbm.xml --> <set name="setUser" table="user_role"> <key column="roleid"></key> <many-to-many class="cn.itcast.manytomany.Role" column="userid"></many-to-many> </set>
- 第四步 在核心配置文件中引入映射文件
<mapping resource="User.hbm.xml"/> <mapping resource="Role.hbm.xml"/>
多对多级联保存
- 第一步 在用户配置文件中的
set
标签进行配置,cascade
值save-update
<set name="setRole" table="user_role" cascade="save-update">
- 第二步 代码实现: 创建用户和角色对象,把角色放到用户里面,最终保存就可以了
public void testCascadeSave(){
Session session = null;
Transaction tx = null;
try{
//获得session
session = HibernateUtils.getSessionObject();
//开启事务
tx = session.beginTransaction();
//1 创建对象并设置相应的属性值
User user1 = new User();
User user2 = new User();
Role r1 = new Role();
Role r2 = new Role();
Role r3 = new Role();
//2 建立关系
//user1 ---- r1&r2
user1.getSetRole().add(r1);
user1.getSetROle().add(r2);
//user2 ---- r2&r3
user2.getSetRole().add(r2);
user2.getSetROle().add(r3);
//3 保存
session.save(user1);
session.save(user2);
//提交
tx.commit();
}catch(Exception e){
//回滚
tx.rollback();
}
}
多对多级联删除
- 第一步
set
标签配置cascade
值为delete
<set name="setRole" table="user_role" cascade="save-update,delete">
- 第二步 删除用户代码实现
//删除操作
User user = session.get(User.class,1);
session.delete(user);
多对多一般不用级联删除,因为一旦级联删除,可能会把别的关系也给删除了,而且两边的关系通过第三张表维护,因此如要删除关系,直接在第三张表操作即可。
维护第三张表
- 用户和角色多对多的关系是通过第三张表维护
- 让某个用户有某个角色
第一步 根据 id 查询用户和角色
第二步 把角色放到用户里面(set集合的add方法) - 让某个用户没有某个角色
第一步 根据 id 查询用户和角色
第二步 从用户里面把角色去掉(set集合的remove方法)
// 让C用户拥有A角色
User c = session.get(User.class,1);
Role a = session.get(Role.class,1);
c.getSetRole().add(a);//把角色放到用户的set集合中
// 让C用户失去B角色
User c = session.get(User.class,1);
Role b = session.get(Role.class,1);
c.getSetRole().remove(b);//从用户的set集合中去掉该角色