JAVA框架——Hibernate(三)实体之间的关系,一对多的关系维护,多对多关系维护

一、 多表之间的关系

1. 一对多

建表原则:在多的一方创建外键指向一方的主键

2. 多对多

建表原则:创建一个中间表,中间表中至少两个字段作为外键分别指向多对多双方的主键

3. 一对一

建表原则有两两种

第一种:唯一外键对应:假设一对一种的任意一方为多,在多的一方创建外键指向一方的主键。

第二种:主键对应:一方的主键作为另外的主键

二、 实体之间的数据关系

数据库的表能够描述实体数据之间的关系,通过对象也可以进行描述,所谓的关联映像就是将关联关系映射到数据库种。在对象模型种就是一个或多个引用。
在Hibernate种采用java对象关系来描述数据表之间的关系如下:

  • 一对一
class A{
  B b;
}
class B{
  A a;
}
  • 一对多

一个A类对应多个B类类型的情况,需要在A类以Set集合的方式引入B类型的对象,在B类种定义A类类型的Set集合

class A{
 Set<B> bs;//B的集合
}

class B{
  A a;
}
  • 多对多

在A类种定义B类类型的Set集合,在B类中定义A类类型的Set集合。

class A{
  Set<B> bs;//B的集合
}

class B{
  Set<A> as;//A的集合
}

三、 Hibernate中表达一对多|多对一

我们新建一个客户表以及联系人表。我们都知道一个客户,也就是相当于一个公司。想要联系公司,就需要联系人。联系人是有多个的。

第一步创建表

在这里插入图片描述

第二步:创建实体(为了篇幅省去 getter/setter方法)

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;
	//使用set集合表达一对多的关系
	private Set<LinkMan> linkMen =new HashSet<LinkMan>();

	public Set<LinkMan> getLinkMen() {
		return linkMen;
	}
	public void setLinkMen(Set<LinkMan> linkMen) {
		this.linkMen = linkMen;
	}
}
public class LinkMan {	
	private Long lkm_id;
	private Character lkm_gender;
	private String lkm_name;
	private String lkm_phone;
	private String lkm_email;
	private String lkm_qq;
	private String lkm_mobile;
	private String lkm_memo;
	private String lkm_position;
	//表达多对一的关系
	private Customer customer;
public Customer getCustomer() {
		return customer;
	}
	public void setCustomer(Customer customer) {
		this.customer = customer;
	}

第三步:写orm元数据

客户orm元数据

<hibernate-mapping package="com.aiit.domain">
	<class name="Customer" table="cst_customer">
		<id name="cust_id" >
			<generator class="native"></generator>
		</id>
	<property name="cust_name" ></property>
	<property name="cust_source" ></property>
	<property name="cust_industry" ></property>
	<property name="cust_level" ></property>
	<property name="cust_linkman" ></property>
	<property name="cust_phone" ></property>
	<property name="cust_mobile" ></property>
	<!-- 将一对多关系在配置文件中配置 -->
	<!-- 
		name属性:集合属性名
		class属性:与本身相关联的对象的完整类名
on-to-many标签:
         class属性:多的一方的类的全路径
	 -->
	<set name="linkMen">
		<key column="lkm_cust_id"></key>

		<one-to-many class="LinkMan"/>
	</set>
	</class>
</hibernate-mapping>

联系人的orm元数据

<hibernate-mapping package="com.aiit.domain">
		<class name="LinkMan" table="cst_linkman">
	<id name="lkm_id">
			<generator class="native"></generator>	
		</id>
	<property name="lkm_name"></property>
	<property name="lkm_cust_id"></property>
	<property name="lkm_gender"></property>
	<property name="lkm_phone"></property>
	<property name="lkm_mobile"></property>
	<property name="lkm_email"></property>
	<property name="lkm_qq"></property>
	<property name="lkm_position"></property>
	<property name="lkm_memo"></property>	
	
	<!-- 多对一 -->
	<!-- 
		name属性:引用属性名
		column:外键列名
		class属性:与本身相关联的对象的完整类名
many-to-one标签,代表多对一
	 -->
	<many-to-one name="customer" column="lkm_cust_id" class="Customer"></many-to-one>
	</class>
</hibernate-mapping>

第四步:在主配置文件中引入配置文件

<mapping resource="/com/aiit/domain/Customer.hbm.xml"/>
<mapping resource="/com/aiit/domain/LinkMan.hbm.xml" />

第五步:书写测试代码

public void fun1() {
		//1.获得session
		Session session = HibernateUtils.openSession();
		//2.开启事务
		Transaction tx = session.beginTransaction();
		//3.操作数据
		Customer customer =new Customer();
		customer.setCust_name("哇哈哈");
		LinkMan lm1 =new LinkMan();
		lm1.setLkm_name("gyf");
		
		LinkMan lm2 =new LinkMan();
		lm2.setLkm_name("zyl");
		
		//1.表达一对多,客户下有多个联系人
		customer.getLinkMen().add(lm1);
		customer.getLinkMen().add(lm2);
		
		//2.表达多对一,联系人属于哪个客户
		lm1.setCustomer(customer);
		lm2.setCustomer(customer);
		
		
		session.save(customer);
		session.save(lm1);
		session.save(lm2);
		
		//4.提交事务
		tx.commit();
		//5.关闭session
		session.close();
	}

四、 Hibernate中一对多|多对一的级联操作——cascade

上面的操作不大人性化,如果一个客户下面有多个联系人的话,我们需要不断调用session.save()方法进行保存。由此引入级联操作

1. 什么是级联操作?

级联操作就是指当主控方执行保存、更新或者删除操作时,其关联对象(被控方)也执行相同的操作

在orm元数据配置中,对cascade属性来设置对控制是否对关对象采用级联级操作。级联级操作对各种关联关系都是有效的

2. 关于级联操作的具体展示

级联是具有方向性的,如一对多的级联操作和多对一的级联操作。

一对多操作案例:

我们想要在使用save()方法去保存客户的时候,顺带一起把联系人保存。并且如果我们想要删掉一个客户的话,顺带一起将我们的联系人全部删除。

首先我们需要在orm元数据中进行配置

<!-- 
	 	级联操作:cascade
	 	save-update:级联保存更新
	 	delete:级联删除。我们在删除客户的同时,顺带把联系人一起删除完毕
	 	all: save-update 和delete同时设置完毕
	  -->
	<set name="linkMen" cascade="save-update">
		<key column="lkm_cust_id"></key>
		<one-to-many class="LinkMan"/>
	</set>

我们需要在一对多的集合中设置cascade属性。并且根据需要传入不同的参数。

  • save-update:级联保存更新
  • delete:级联删除。我们在删除客户的同时,顺带把联系人一起删除完毕
  • all: save-update 和delete同时设置完毕

测试级联操作:

public class Demo {
	@Test
	public void fun1() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		Customer customer =new Customer();
		customer.setCust_name("哇哈哈");
		LinkMan lm1 =new LinkMan();
		lm1.setLkm_name("gyf");
		
		LinkMan lm2 =new LinkMan();
		lm2.setLkm_name("zyl");
		
		//1.表达一对多,客户下有多个联系人
		customer.getLinkMen().add(lm1);
		customer.getLinkMen().add(lm2);
		
		//2.表达多对一,联系人属于哪个客户
		lm1.setCustomer(customer);
		lm2.setCustomer(customer);
		
		
		session.save(customer);
		tx.commit();
		session.close();
	}
}

多对一的操作案例:

<!-- 多对一 -->
	<!-- 
		name属性:引用属性名
		column:外键列名
		class属性:与本身相关联的对象的完整类名
	 -->
	 <!-- 
	 	级联操作:cascade
	 	save-update:级联保存更新
	 	delete:级联删除。我们在删除客户的同时,顺带把联系人一起删除完毕
	 	all: save-update 和delete同时设置完毕
	  -->
<many-to-one name="customer" column="lkm_cust_id" class="Customer"  cascade="save-update"></many-to-one>
</class>

一对多和多对一的级联操作是类似,参数一模一样

开发建议使用save-update,不建议使用delete。如果我们我们将联系人和客户联系,我们仅删除了一个联系人可能导致客户删除完毕。

五、 双向关联产生多条SQL语句—inverse属性

我们在定义了两个实体之间一对多和多对多的关系之后,双方均进行了一次更新,用于维护双方之间的关系实际上,持久态对象可以自动更新数据库。更新客户的时候,已经修改了一次外键。所以不需要双方再次进行维护外键。这样会影响效率。
所以我们需要找到我们的orm元数据。我们只需要一方放弃外键的维护即可。也就是说关系不是双方维护的,只需要交给某一方去维护就可以了。通常多的一方进行维护。也就是说多的一方不能放弃自己的外键。一的一方进行放弃。
举例:一个老师对应多个学生,一个学生对应一个老师。老师记住所有学生名字是很难的,但是如果让每个学生记住老师姓名并不困难。学生不能放弃老师

<!-- 
		inverse属性:配置关系是否维护
		true:customer不维护关系
		flase(默认值):维护关系
	 -->
	<set name="linkMen" inverse="true">
		<key column="lkm_cust_id"></key>

		<one-to-many class="LinkMan"/>
	</set>

因此如果我们一的一方放弃维护关系,那么在代码上可以不去书写

//表达一对多,客户下有多个联系人
			//	c.getLinkMen().add(lm1);
			//	c.getLinkMen().add(lm2);
由于客户放弃和联系人之间关系,可以省去上述代码
				//表达多对一,联系人属于哪个客户
				lm1.setCustomer(c);
				lm2.setCustomer(c);

其次

  • 如果我们维护了关系,我们我们将从表,也就是一这边的表删除的话,那么我们外键中的值也会全部删除,设置为null
  • 如果我们不维护关系,并且设置为delete。那么删除客户的同时,也会把我们联系人删完。
<set name="linkMen" inverse="true"  cascade=“delete”>
		<key column="lkm_cust_id"></key>
		<one-to-many class="LinkMan"/>
	</set>

六、 Hibernate中表达多对多

第一步:

同样我们举例员工 和 职位。在数据库中的表达
在这里插入图片描述

第二步:在实体中进行表达

public class User {
	private Long user_id;
	private String user_code;
	private String user_name;
	private String user_password;
	private Character user_state;
	
	//表达多对多
	private Set<Role> roles = new HashSet<Role>();
	
	
	public Set<Role> getRoles() {
		return roles;
	}
	public void setRoles(Set<Role> roles) {
		this.roles = roles;
	}
}
public class Role {
	private Long role_id;
	private String role_name;
	private String role_memo;
	//表达多对多
	private Set<User> users = new HashSet<User>();
	public Set<User> getUsers() {
		return users;
	}
	public void setUsers(Set<User> users) {
		this.users = users;
	}
}

第三步:orm元数据中的多对多表达

user.hbm.xml文件

<hibernate-mapping package="cn.aiit.domain" >
	<class name="User" table="sys_user" >
		<id name="user_id"  >
			<generator class="native"></generator>
		</id>
		<property name="user_code"></property>
		<property name="user_name"></property>
		<property name="user_password"></property>
		<property name="user_state"></property>
		<!-- 多对多的关系 -->
		<!-- 
			name:集合属性名
			table:配置中间名
		 	key下的column,别人引用我的外键
		 	后面的class表示我与那个类是多对多关系
		 	column:外键,我引用别人的外键列名
		 -->
		<set name="roles" table="sys_user_role">
			<key column="user_id"></key>
			<many-to-many class="Role" column="role_id"></many-to-many>
		</set>
	</class>
</hibernate-mapping>

role.hbm.xml文件

<hibernate-mapping package="cn.aiit.domain" >
	<class name="Role" table="sys_role" >
		<id name="role_id"  >
			<generator class="native"></generator>
		</id>
		<property name="role_name"  ></property>
		<property name="role_memo"  ></property>
<!-- 多对多的关系 -->
		<!-- 
			name:集合属性名
			table:配置中间名
		 	key下的column,别人引用我的外键
		 	后面的class表示我与那个类是多对多关系
		 	column:外键,我引用别人的外键列名
		 -->
		<set name="users" table="sys_user_role">
			<key column="role_id"></key>
			<many-to-many class="User" column="user_id"></many-to-many>
		</set>
	</class>
</hibernate-mapping>

第四步: 主配置文件中进行配置

<mapping resource="/com/aiit/domain/Customer.hbm.xml" />
<mapping resource= "/com/aiit/domain/LinkMan.hbm.xml"/>
<mapping resource= "/com/aiit/domain/User.hbm.xml"/>
<mapping resource= "/com/aiit/domain/Role.hbm.xml"/>

七、 多对多之间的关联属性——inverse属性

根据上面的配置完毕,我们进行代码测试

public void fun1() {
		//1 获得session
		Session session = HibernateUtils.openSession();
		//2 开启事务
		Transaction tx = session.beginTransaction();
		//-------------------------------------------------
		//3操作
		User user1 = new User();
		User user2 = new User();
		user1.setUser_name("zhangsan");
		user2.setUser_name("lisi");
		
		Role role1 = new Role();
		Role role2 = new Role();
		role1.setRole_name("保安");
		role2.setRole_name("保洁");
		
		
		//用户表达关系
		user1.getRoles().add(role1);
		user1.getRoles().add(role2);
		user2.getRoles().add(role1);
		user2.getRoles().add(role2);
		
		//角色表达关系
		role1.getUsers().add(user1);
		role1.getUsers().add(user2);
		role2.getUsers().add(user1);
		role2.getUsers().add(user2);
		session.save(user1);
		session.save(user2);
		session.save(role1);
		session.save(role2);
		//-------------------------------------------------
		//4提交事务
		tx.commit();
		//5关闭资源
		session.close();
	}

此时会进行报错,原因和一对多或者多对多一样。我们多对多关系中,第三方表存在存储冲突。双方均维护关系。导致第三方表中的外键,值存在冲突。所以在多对多关系中,我们必须有一方放弃维护关系至于哪一方需要放弃维护,需要根据我们的业务进行选择。

<!-- 使用inverse属性
			true: 放弃维护外键关系
			false(默认值):维护关系
		 -->		
		<set name="users" table="sys_user_role" inverse="true" >
			<key column="role_id" ></key>
			<many-to-many class="User" column="user_id" ></many-to-many>
		</set>

此时我们在测试中,可以放弃一系列的role1.getUsers().add(user1);等一系列操作

八、 多对多的级联操作

我们在多对多中,给某一用户添加角色属性。级联操作的目的就是简化我们书写代码行数。可以方便我们不对用户进行save()方法。因为我们可以在User的orm元数据,进行文件的配置

<!-- cascade级联操作:
		 			save-update: 级联保存更新
		 			delete:级联删除
		 			all:级联保存更新+级联删除
		 	结论: cascade简化代码书写.该属性使不使用无所谓. 建议要用只用save-update.
		 		 如果使用delete操作太过危险.尤其在多对多中.不建议使用.
		 			 -->

<set name="roles" table="sys_user_role" cascade="save-update">
			<key column="user_id"></key>
			<many-to-many class="Role" column="role_id"></many-to-many>
		</set>

我们尽量在多对多中不要使用delete,因为多对多使用过于危险,因为进行删除之后,会连同另外一个表的属性全部删除

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值