hibernate_day03

Hibernate的一对多关联映射
案例:一个老师对应多个学生

1.创建实体类
@Data
@ToString	//lombok提供的注解
public class Teacher{
	private Long t_id;
	private String name;
	private Set<Student> studentSet = new HashSet();
}

@Data
@ToString	//lombok提供的注解
public class Student{
	private Long s_id;
	private String name;
	private Teacher teacher;
}

2.配置实体类的映射配置文件
一的一方:
<class name="com.baidu.hibernate.domain.Teacher" table="tb_teacher">
	<id name="t_id" column="t_id">
		<generator class="identity"></generator>
	</id>
	<property name="name" column="name"></property>
	<set name="studentSet">
		<key column="fk_id"></key>	//外键
		<one-to-many class="com.baidu.hibernate.domain.Student"></one-to-many>
	</set>
</class>
多的一方:
<class name="com.baidu.hibernate.domain.Student" table="tb_teacher">
	<id name="s_id" column="s_id">
		<generator class="identity"></generator>
	</id>
	<property name="name" column="name"></property>
	<many-to-one name="teacher" column="fk_id" class="com.baidu.hibernate.domain.Teacher">		</may-to-one>
</class>

3.在hibernate.cfg.xml中引入: Teacher.hbm.xml和Student.hbm.xml
<mapping resource="com/baidu/hibernate/domain/Teacher.hbm.xml"></mapping>
<mapping resource="com/baidu/hibernate/domain/Student.hbm.xml"></mapping>

4.测试保存,结果:一的一方和多的一方都必须为持久态,也就是都要执行session.save();如果只对一方进行保存,另一方不保存就会报异常TransientObjectException;即一个持久态对象关联了一个瞬时态对象,就会报错
        Teacher t1 = new Teacher();
        t1.setName("张三");
        Student s1 = new Student();
        s1.setName("李四");
        t1.getStudentSet.add(s1);
        session.save(t1);
        session.save(s1);
	少save一个对象,都会报错

5.解决方案:级联操作(cascade)
    级联操作是指当主控方执行DDL时,与其关联的被控方也执行相应的DDL操作
步骤:
	修改Teacher.hbm.xml或者Student.hbm.xml
Teacher.hbm.xml:
	在set标签上加 cascade="save-update" 属性
Student.hbm.xml:
	在many-to-one标签上加 cascade="save-update" 属性
	如果在Teacher.hbm.xml中加上级联操作,当执行session.save(teacher),会自动对student也进行保存;如果在Student.hbm.xml中加上级联操作,当执行session.save(student),会自动对teacher也进行保存;

6.级联操作扩展
<set ... cascade="save-update,delete"></set>
save-update 级联保存或修改
delete 级联删除,要想获得级联删除的效果,session.delete()方法删除的必须是持久态对象,如果是自己new出来的瞬时态/托管态对象,由于外键属性中没有值,将达不到级联删除的效果

7.放弃外键维护inverse
	当我们保存一组被外键关联了的数据时,会发现双方都对外键进行了维护,这样影响了性能;所以通常一对多的情况下,会让一的一方放弃对外键的维护,在Customer.hbm.xml中set标签上加inverse属性(默认是false),改成inverse="true"	
 
8.cascade跟inverse的区别:
cascade强调的是级联操作,操作一个对象的同时操作关联的对象
inverse强调的是对外键的维护权

一对多双方共同维护外键,发送两条sql影响性能
多对多共同维护外键,就会报错,因为同一组数据会被插入两次,
Hibernate的多对多关系映射
案例:一个用户对应多个角色,一个角色也对应多个用户

1.创建实体类
@Data
@ToString
public class User{
	private Long u_id;
	private String u_name;
	private Set<Role> roleSet = new HashSet();
}

@Data
@ToString
public class Role{
	private Long r_id;
	private String r_name;
	private Set<User> userSet = new HashSet();
}

2.配置实体类的映射配置文件
//用户:
<class name="com.baidu.hibernate.domain.User" table="tb_user">
	<id name="u_id" column="u_id">
		<generator class="identity"></generator>
	</id>
	<property name="u_name" column="u_name"></property>
	<set name="userSet" table="tb_user_role">	//table指定中间表的表名,name指定集合属性
		<key column="user_no"></key>		//column指定中间表跟user表关联的外键字段
		<many-to-many class="com.baidu.hibernate.domain.Role" column="role_no">
				//此处column指定中间表Role表的外键字段
		</many-to-many>
	</set>
</class>

//角色:
<class name="com.baidu.hibernate.domain.Role" table="tb_role">
	<id name="r_id" column="r_id">
		<generator class="identity"></generator>
	</id>
	<property name="r_name" column="r_name"></property>
	<set name="roleSet" table="tb_user_role">	//table指定中间表的表名,name指定集合属性
		<key column="role_no"></key>		//column指定中间表跟role表关联的外键字段
		<many-to-many class="com.baidu.hibernate.domain.User" column="user_no">
				//此处column指定中间表User表的外键字段
		</many-to-many>
	</set>
</class>

3.在hibernate.cfg.xml中引入: User.hbm.xml和Role.hbm.xml
<mapping resource="com/baidu/hibernate/domain/User.hbm.xml"></mapping>
<mapping resource="com/baidu/hibernate/domain/Role.hbm.xml"></mapping>

4.多对多的常用操作:
* 给某个用户添加角色
	User user = session.get(User.class,1L);
	Role role = session.get(Role.class,1L);
	user.getRoleSet().add(role);
	transaction.commit();
* 删除某个用户的角色
	User user = session.get(User.class,1L);
	Role role = session.get(Role.class,1L);
	user.getRoleSet().romove(role);
	transaction.commit();
* 改变某个用户的角色
	User user = session.get(User.class,1L);
	Role role1 = session.get(Role.class,1L);
	Role role2 = session.get(Role.class,2L);
	user.getRoleSet().romove(role1);
	user.getRoleSet().add(role2);
	transaction.commit();

表关系映射注意事项:
1.在多对多的表关系下,必须有一方放弃对外键的维护,否则双方都在中间表insert一条相同的数据,会导致异常;如果是一对多,双方都不放弃对外键的维护,最终一条insert和update,只会影响性能,但是不会造成异常
2.当执行保存操作时,如果一个对象中关联了另外一张表的对象,那么这两个对象都必须执行save方法,否则报TransientObjectException异常
3.在多对多的表关系下,不能双方都开启级联删除,否则可能会导致所有有关联的数据全部被删除
4.在多对多的表关系下,如果放弃对外键的维护,这一方将不能操作中间表;而一对多中,如果放弃外键的维护,这一方将不能操作外键
5.级联和外检维护权测试:
	 User user = session.get(User.class, 1L);
       session.delete(user);
* User : inverse = "false" ;Role: inverse = "false"
	sql: delete from tb_user_role where user_no = ?
		delete from tb_user where user_id = ?
	删除成功,删除tb_user/tb_user_role
* User : inverse = "true" ,Role: inverse = "false"
	sql: delete from tb_user where user_id = ?
	删除失败,user表放弃了对外键的维护,而user表中的数据受中间表的约束,因此删除失败
* User : inverse = "true" , cascade = "delete" ;Role: inverse = "false"
	sql: delete from tb_user_role where role_no = ?
        delete from tb_role where r_id = ?
        delete from tb_user where u_id = ?
	删除成功,由于对User设置了级联,user的持久态对象中保存了关联的roleSet,而role拥有维护外键的权限,根据roleSet删除中间表的关联数据,自然也删除了对tb_user表的约束,因此都能成功,删除tb_user/tb_user_role/tb_role
* User : inverse = "true" , cascade = "delete" ;Role: inverse = "true"
	删除失败,双方都放弃对外键的维护,都被中间表约束
6.配置实体类映射配置文件多表对应关系的三大属性:name-->column-->class
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值