一对多映射(重点)
编写JavaBean
- 1的一方
- 保存n的一方对象的Set集合
- hibernate框架默认的集合是set集合,集合必须要手动初始化
- 案例:
public class Customer { private Long cust_id; //客户编号(主键) private String cust_name; //客户名称(公司名称) private Long cust_user_id; //负责人id private Long cust_create_id; //创建人id private String cust_source; //客户信息来源 private String cust_industry; //客户所属行业 private String cust_level; //客户级别 private String cust_linkman; //联系人 private String cust_phone; //固定电话 private String cust_mobile; //移动电话 //1的一方 //hibernate框架默认的集合是set集合,集合必须要手动初始化 private Set<LinkMan> LinkMans = new HashSet<LinkMan>(); set&get... }
- n的一方
- 保存1的一方的对象
- 这个对象千万不要自己new,配置文件提供该类的全路径,hibernate框架会自己new
- 案例:
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; //n的一方 //编写一个对象,千万不要自己new,配置文件提供类的全路径,hibernate框架会自己new private Customer customer; set&get... }
编写一对多的配置文件
- 1的一方
<?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> <!-- 配置类和表结构的映射 --> <class name="com.lousen.domain.Customer" table="cst_customer"> <!-- 配置id,name:JavaBean,column:表结构的字段 --> <id name="cust_id" column="cust_id"> <!-- 主键生成策略 --> <generator class="native"></generator> </id> <!-- 配置其他属性 --> <property name="cust_name" column="cust_name"/> <property name="cust_user_id" column="cust_user_id"/> <property name="cust_create_id" column="cust_create_id"/> <property name="cust_source" column="cust_source"/> <property name="cust_industry" column="cust_industry"/> <property name="cust_level" column="cust_level"/> <property name="cust_linkman" column="cust_linkman"/> <property name="cust_phone" column="cust_phone"/> <property name="cust_mobile" column="cust_mobile"/> <!-- 配置一的一方,name:表示集合属性的名称,cascade:级联 --> <set name="LinkMans" inverse="true"> <!-- column:外键字段 --> <!-- class:集合属性的全路径 --> <key column="lkm_cust_id"></key> <one-to-many class="com.lousen.domain.LinkMan"/> </set> </class> </hibernate-mapping>
- n的一方
<?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> <!-- 配置类和表结构的映射 --> <class name="com.lousen.domain.LinkMan" table="cst_linkman"> <!-- 配置id,name:JavaBean,column:表结构的字段 --> <id name="lkm_id" column="lkm_id"> <!-- 主键生成策略 --> <generator class="native"></generator> </id> <!-- 配置其他属性 --> <property name="lkm_name" column="lkm_name"/> <property name="lkm_gender" column="lkm_gender"/> <property name="lkm_phone" column="lkm_phone"/> <property name="lkm_mobile" column="lkm_mobile"/> <property name="lkm_email" column="lkm_email"/> <property name="lkm_qq" column="lkm_qq"/> <property name="lkm_position" column="lkm_position"/> <property name="lkm_memo" column="lkm_memo"/> <!-- 先配置多的一方 name: 当前JavaBean中的属性 class:该属性的全路径 column:外键字段 cascade: 级联 --> <many-to-one name="customer" class="com.lousen.domain.Customer" column="lkm_cust_id" cascade="save-update"></many-to-one> </class> </hibernate-mapping>
双向关联数据
- 简介
顾客关联联系人,联系人同时也要关联顾客,两者的对象都需要保存。 - 双向关联数据保存
/** * 双向关联保存数据(最麻烦的) */ @Test public void run1(){ Session session = HibernateUtils.getSession(); Transaction tr = session.beginTransaction(); //创建一个顾客 Customer c1 = new Customer(); c1.setCust_name("张三"); //创建两个联系人 LinkMan l1 = new LinkMan(); l1.setLkm_name("老大"); LinkMan l2 = new LinkMan(); l2.setLkm_name("老二"); //演示双向关联 c1.getLinkMans().add(l1); c1.getLinkMans().add(l2); l1.setCustomer(c1); l2.setCustomer(c1); //保存数据 session.save(c1); session.save(l1); session.save(l2); tr.commit(); }
级联保存
- 简介
保存一方的同时可以把关联的对象也保存到数据库中,具有方向性,可以修改顾客的配置文件使顾客级联联系人,也可以修改联系人的配置文件使联系人级联顾客。 - 配置文件
在配置文件添加属性cascade="save-update"
即可 - 级联保存案例
/** * 级联保存(单向关联) * 顾客级联联系人 */ @Test public void run2(){ Session session = HibernateUtils.getSession(); Transaction tr = session.beginTransaction(); //创建一个顾客 Customer c1 = new Customer(); c1.setCust_name("张三"); //创建两个联系人 LinkMan l1 = new LinkMan(); l1.setLkm_name("老大"); LinkMan l2 = new LinkMan(); l2.setLkm_name("老二"); //演示级联 c1.getLinkMans().add(l1); c1.getLinkMans().add(l2); //保存数据 session.save(c1); tr.commit(); }
级联删除(谨慎使用,很少使用)
- 简介
- 普通的直接删除
删除顾客以后,该顾客对应的联系人的外键那一列的值变为null - 级联删除
删除顾客以后,该顾客对应的联系人的所有数据被删除
- 普通的直接删除
- 配置文件
在配置文件添加属性cascade="delete"
即可 - 级联删除案例
/** * 级联删除 * 同样具有方向性,也可两边都级联(很危险,相关数据全删) * 在联系人和顾客两边都加上级联删除 * 删除联系人时联系人级联删除顾客,删除顾客时顾客又级联删除联系人 */ @Test public void run4(){ Session session = HibernateUtils.getSession(); Transaction tr = session.beginTransaction(); //删除主键为3的联系人 LinkMan linkman = session.get(LinkMan.class,3L); session.delete(linkman); tr.commit(); }
孤儿删除
- 简介
- 只存在于一对多
- 在一对多的关系中,可以将一的一方认为是父方。将多的一方认为是子方。孤儿删除:在解除了父子关系的时候。将子方整条记录直接删除。
- 配置文件
在配置文件<set>
标签上添加属性cascade="delete-orphan"
即可
3.孤儿删除案例/** * 孤儿删除(在解除关系的基础上修改了配置文件,cascade="delete-orphan") * 删除整条记录 */ @Test public void run6(){ Session session = HibernateUtils.getSession(); Transaction tr = session.beginTransaction(); LinkMan linkman = session.get(LinkMan.class,9L); Customer customer = session.get(Customer.class,9L); //解除关系 customer.getLinkMans().remove(linkman); tr.commit(); }
放弃外键的维护
- 简介
- 双方都维护外键的时候,会产生多余的SQL语句
- 产生的原因:session的一级缓存中的快照机制,会让双方都更新数据库,产生了多余的SQL语句。
- 如果不想产生多余的SQL语句,那么需要一方来放弃外键的维护
- 配置文件
在<set>
标签上配置一个inverse="true"
。默认值是false。
cascade和inverse
- cascade用来级联操作(保存、修改和删除)
- inverse用来维护外键的
- 两者一般不能同时存在同一个配置文件
- inverse在一对多中必须放在1的一方,在多对多时放在少的一方
- cascade放在inverse的另一方
- 如果同时存在同一个配置文件,将无法对该方的外键进行保存等操作,因为这一方已经放弃了对外键的维护。
多对多映射(重点)
编写JavaBean
同一对多中多的一方
案例:
public class User {
private Long uid;
private String username;
private String password;
private Set<Role> roles = new HashSet<Role>();
set&get...
}
public class Role {
private Long rid;
private String rname;
private Set<User> users = new HashSet<User>();
set&get...
}
编写多对多的配置文件
- 多-用户
<?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> <!-- 配置类和表结构的映射 --> <class name="com.lousen.domain.User" table="sys_user"> <!-- 配置id,name:JavaBean,column:表结构的字段 --> <id name="uid" column="uid"> <!-- 主键生成策略 --> <generator class="native"></generator> </id> <!-- 配置其他属性 --> <property name="username" column="username"/> <property name="password" column="password"/> <!-- 配置多对多 name:集合的名称 table:中间表的名称 --> <set name="roles" table="sys_user_role" cascade="save-update"> <!-- 当前对象在中间表外键的名称 --> <key column="uid" /> <!-- class:集合中所存对象的全路径 column:集合中所存对象在中间表的外键名称 --> <many-to-many class="com.lousen.domain.Role" column="rid"></many-to-many> </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> <!-- 配置类和表结构的映射 --> <class name="com.lousen.domain.Role" table="sys_role"> <!-- 配置id,name:JavaBean,column:表结构的字段 --> <id name="rid" column="rid"> <!-- 主键生成策略 --> <generator class="native"></generator> </id> <!-- 配置其他属性 --> <property name="rname" column="rname"/> <!-- 配置多对多 name:集合的名称 table:中间表的名称 --> <set name="users" table="sys_user_role" inverse="true"> <!-- 当前对象在中间表外键的名称 --> <key column="rid" /> <!-- class:集合中所存对象的全路径 column:集合中所存对象在中间表的外键名称 --> <many-to-many class="com.lousen.domain.User" column="uid"></many-to-many> </set> </class> </hibernate-mapping>
- 中间表
在多对多中,只要正确配置了文件,Hibernate框架会自动生成中间表
双向关联
多对多进行双向关联的时候:必须有一方放弃外键维护权,否则会报错!!!
级联操作
- 简介
同一对多 - 案例
/** * 多对多 * role放弃外键维护 * 保存user时级联保存role */ @Test public void run1(){ Session session = HibernateUtils.getSession(); Transaction tr = session.beginTransaction(); //创建用户 User u1 = new User(); u1.setUsername("张三"); User u2 = new User(); u2.setUsername("李四"); //创建角色 Role r1 = new Role(); r1.setRname("上校"); Role r2 = new Role(); r2.setRname("下士"); //级联保存 u1.getRoles().add(r1); u1.getRoles().add(r2); u2.getRoles().add(r1); session.save(u1); session.save(u2); tr.commit(); }
维护中间表
- 简介
- 在多对多中,只要正确配置了文件,Hibernate框架会自动生成中间表
- 通过操作对应集合来操作对应的外键
- 案例:
/** * 维护中间表 * 通过操作集合 */ @Test public void run2(){ Session session = HibernateUtils.getSession(); Transaction tr = session.beginTransaction(); //查询李四用户 User u = session.get(User.class, 2L); //查询角色 Role r = session.get(Role.class, 2L); //从中间表移除该条数据 u.getRoles().remove(r); tr.commit(); }