Hibernate系列---(三)表关系

本文深入探讨了Hibernate中的三种表关系:一对多、多对多和一对一。通过详细的例子和配置展示了如何建立表关系,进行级联操作,并讨论了级联保存和删除的实现。在一对多关系中,重点讲解了级联保存和删除的配置。在多对多关系中,介绍了中间表的创建和级联保存,但建议在实际业务中谨慎使用级联删除。最后,对一对一关系进行了简单介绍。
摘要由CSDN通过智能技术生成

一、一对多

1.1 一对多建表关系

一对多关系,如Category和Product。一种分类Category对应多个商品Product,一个商品product对应一个Category

一对多建表:在多的一方创建外键,外键指向一的一方的主键

1.2 一对多实例

例子如下:
Product和Category,在Product表中创建外键,指向Category的主键

建表语句:

CREATE TABLE category (
  c_id int(11) NOT NULL AUTO_INCREMENT COMMENT '分类id',
  c_name varchar(30) DEFAULT NULL COMMENT '分类名称',
	PRIMARY KEY(c_id)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE product (
  p_id int(11) NOT NULL AUTO_INCREMENT COMMENT '商品id',
  p_name varchar(20) DEFAULT NULL COMMENT '商品名称',
  p_price double DEFAULT NULL COMMENT '商品价格',
  p_descript varchar(200) DEFAULT NULL COMMENT '商品描述',
  p_c_id int(11) DEFAULT NULL COMMENT '商品分类-外键',
	PRIMARY KEY(p_id),
  KEY FK_p_c_id_c_id (p_c_id),
  CONSTRAINT FK_p_c_id_c_id FOREIGN KEY(p_c_id) REFERENCES category (c_id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

实体类 :

product.java

package com.hibernate.demo;

public class Product {
	private int p_id;
	private String p_name;
	private double p_price;
	private String p_descript;
	// 增加Category的属性(一个产品只能属于一个分类)
	private Category category;

	public Category getCategory() {
		return category;
	}

	public void setCategory(Category category) {
		this.category = category;
	}

	public String getP_descript() {
		return p_descript;
	}

	public void setP_descript(String p_descript) {
		this.p_descript = p_descript;
	}

	public int getP_id() {
		return p_id;
	}

	public void setP_id(int p_id) {
		this.p_id = p_id;
	}

	public String getP_name() {
		return p_name;
	}

	public void setP_name(String p_name) {
		this.p_name = p_name;
	}

	public double getP_price() {
		return p_price;
	}

	public void setP_price(double p_price) {
		this.p_price = p_price;
	}
}

category.java

package com.hibernate.demo;

import java.util.HashSet;
import java.util.Set;

public class Category {
	private int c_id;
	private String c_name;
	// 一个category对应多个prodcut
	private Set<Product> products = new HashSet<Product>();

	public Set<Product> getProducts() {
		return products;
	}

	public void setProducts(Set<Product> products) {
		this.products = products;
	}

	public int getC_id() {
		return c_id;
	}

	public void setC_id(int c_id) {
		this.c_id = c_id;
	}

	public String getC_name() {
		return c_name;
	}

	public void setC_name(String c_name) {
		this.c_name = c_name;
	}

	@Override
	public String toString() {
		return "Category [c_id=" + c_id + ", c_name=" + c_name + "]";
	}
}

映射配置

映射配置十分重要,有些属性比如必须使用全路径则必须使用全路径。注意映射配置内容

product.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!-- hibernate约束 -->
<!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.hibernate.demo.Product" table="product">
 	<!-- id标签建立类中的属性与表中主键映射关系 -->
 		<id name="p_id" column="p_id">
 		<!-- 主键生成策略 -->
 			<generator class="native"></generator>
 		</id>
 		
 		<!-- 建立表中的普通属性和表的其他字段的映射关系 -->
 		<property name="p_name"></property>
 		<property name="p_price"></property>
 		<property name="p_descript"></property>
 		
 		<!-- 配置多对一的关系 -->
 		<!-- 
 			name : 一的一方的对象的属性名称
 			class : 一的一方的全路径
 			column : 多的一方的表的外键的名称	
 		 -->
 		<many-to-one name="category" class="com.hibernate.demo.Category" column="p_c_id"></many-to-one>
 		
 	</class>
 </hibernate-mapping>

category.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!-- hibernate约束 -->
<!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.hibernate.demo.Category"  table="category">
 	<!-- id标签建立类中的属性与表中主键映射关系 -->
 		<id name="c_id" column="c_id">
 		<!-- 主键生成策略 -->
 			<generator class="native"></generator>
 		</id>
 		
 		<!-- 建立表中的普通属性和表的其他字段的映射关系 -->
 		<property name="c_name"></property>
 		<!-- 一对多 放置的是多的一方的集合-->
 		<!-- name : Category中的集合的属性名称 -->
 		<set name="products">
 		 <!-- column:多的一方的外键名称 -->
 			<key column="p_c_id"></key>
 			<!--  多的一方的全路径 -->
 			<one-to-many class="com.hibernate.demo.Product"/>
 		</set>
 		
 	</class>
 </hibernate-mapping>

核心配置:

hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
	
<hibernate-configuration>
	<session-factory>
	<!-- 连接数据库的基本参数 -->
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">jdbc:mysql://localhost:3305/hibernate_01</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">root</property>
	
	<!-- 配置hibernate方言 -->
	<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
	
	<!-- 以下为可选配置 -->
	<!-- 打印Sql语句 -->
	<property name="hibernate.show_sql">true</property>
	<!-- 格式化sql语句 -->
	<property name="hibernate.format_sql">true</property>
	<!-- 自动创建表 -->
	<property name="hibernate.hbm2ddl.auto">update</property>
	<!-- 以上为可选配置 -->
	
	<!-- 事务隔离级别 
		hibernate.connection.isolation - 4
		1 - Read uncommitted isolation
		2 - Read committed isolation
		4 - Repeatable read isolation
		8 - Serializable isolation
	-->
	<property name="hibernate.connection.isolation">4</property>
	
	<!-- 配置当前线程绑定的session -->
	<property name="hibernate.current_session_context_class">thread</property>
	
	<!-- 引入映射文件 -->
	<mapping resource="com/hibernate/demo/Category.hbm.xml"/>
	<mapping resource="com/hibernate/demo/Product.hbm.xml"/>
	</session-factory>
</hibernate-configuration>

测试代码:

package com.hibernate.demo;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.utils.HibernateUtils;

/**
 * 一对多测试
 * @author cf
 *
 */
public class HibernateDemo01 {
	
	@Test
	public void demo01() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tx = session.beginTransaction();
		//两个分类
		Category category1 = new Category();
		category1.setC_name("水果");
		Category category2 = new Category();
		category2.setC_name("蔬菜");
		//三种商品
		Product product1 = new Product();
		product1.setP_name("火龙果");
		Product product2 = new Product();
		product2.setP_name("苹果");
		Product product3 = new Product();
		product3.setP_name("白菜");
		//设置关系
		product1.setCategory(category1);
		product2.setCategory(category1);
		product3.setCategory(category2);
		category1.getProducts().add(product1);
		category1.getProducts().add(product2);
		category2.getProducts().add(product3);
		//保存数据
		//需保存两边,否则会报瞬时态对象异常
		session.save(product1);
		session.save(product2);
		session.save(product3);
		session.save(category1);
		session.save(category2);
		tx.commit();
	}
}

结果如下:
在这里插入图片描述
在这里插入图片描述

1.3 级联操作

1.3.1 级联简介

级联:操作一个对象时,是否会同时操作相关联的对象。

即没有配置级联的时候,删除分类,其对应的产品不会被删除。 但是如果配置了恰当的级联,那么删除分类的时候,其对应的产品都会被删除掉。

1.3.2 级联保存:

  • 保存分类级联商品 :

保存category级联保存product

此处<set name="products" cascade="save-update">更新cascade属性

category.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!-- hibernate约束 -->
<!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.hibernate.demo.Category"  table="category">
 	<!-- id标签建立类中的属性与表中主键映射关系 -->
 		<id name="c_id" column="c_id">
 		<!-- 主键生成策略 -->
 			<generator class="native"></generator>
 		</id>
 		
 		<!-- 建立表中的普通属性和表的其他字段的映射关系 -->
 		<property name="c_name"></property>
 		<!-- 一对多 放置的是多的一方的集合-->
 		<!-- name : Category中的集合的属性名称 -->
 		<set name="products" cascade="save-update">
 		 <!-- column:多的一方的外键名称 -->
 			<key column="p_c_id"></key>
 			<!--  多的一方的全路径 -->
 			<one-to-many class="com.hibernate.demo.Product"/>
 		</set>
 		
 	</class>
 </hibernate-mapping>

测试的代码如下:

  @Test
    	//级联保存
    	//保存category级联保存product,操作主体为category
    	//	category.hbm.xml 更新<set name="products" cascade="save-update">
    	public void demo02() {
    		Session session = HibernateUtils.getCurrentSession();
    		Transaction tx = session.beginTransaction();
		
		//两个分类
		Category category1 = new Category();
		category1.setC_name("手机");

		//三种商品
		Product product1 = new Product();
		product1.setP_name("iphone");

		//设置关系
		product1.setCategory(category1);
		category1.getProducts().add(product1);
		//保存数据
		session.save(category1);
		
		tx.commit();
	}

结果如下:
在这里插入图片描述
在这里插入图片描述

  • 保存商品级联分类

保存product级联保存category

product.hbm.xml中增加cascade属性

<many-to-one name="category"  cascade="save-update" class="com.hibernate.demo.Category" column="p_c_id"></many-to-one>

测试代码:

@Test
//级联保存
//操作主体为product
public void demo03() {
	Session session = HibernateUtils.getCurrentSession();
	Transaction tx = session.beginTransaction();
	
	//两个分类
Category category1 = new Category();
category1.setC_name("电脑");

//三种商品
Product product1 = new Product();
product1.setP_name("Y470");

//设置关系
product1.setCategory(category1);
category1.getProducts().add(product1);
//保存数据
session.save(product1);

tx.commit();
}

结果如下:
在这里插入图片描述
在这里插入图片描述
1.3.3 级联删除

级联删除:删除一方的同时,另一方的数据也被删除

删除category级联删除对应的product

Category.hbm.xml配置以下语句更新:

<set name="products" cascade="save-update,delete">

测试代码:

@Test
//级联删除
//操作主体为category
public void demo04() {
	Session session = HibernateUtils.getCurrentSession();
	Transaction tx = session.beginTransaction();

//删除category
Category category = session.get(Category.class, 1);
session.delete(category);

tx.commit();
}

结果如下:
在这里插入图片描述
在这里插入图片描述

解决多余的SQL语句,减轻服务器压力
一的一方放弃外键维护权

Category.hbm.xml增加inverse属性

<set name="products" cascade="save-update,delete"  inverse=“true”>

二、多对多

2.1 多对多关系简介

一个用户user可以购买多种商品product,一种商品product也可以被多种商品购买,需建立多对多关系。

*多对多建表:*需建立中间表user_product维护user和product之间的关系

2.2 实例

2.2.1 数据库表建立

table product第一节已建立

CREATE TABLE user (
  u_id int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  u_name varchar(20) DEFAULT NULL COMMENT '用户名',
  PRIMARY KEY(u_id)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE user_product (
  u_id int(11) NOT NULL COMMENT '用户id',
  p_id int(11) NOT NULL COMMENT '商品id',
	PRIMARY KEY(u_id),
  KEY FK_u_id_p_id (u_id),
  CONSTRAINT FK_u_id_p_id FOREIGN KEY(u_id) REFERENCES user (u_id) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT FK_p_id_p_id FOREIGN KEY(p_id) REFERENCES product (p_id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

实体类:

User.java

package com.hibernate.demo;

import java.util.HashSet;
import java.util.Set;

public class User {
	private int u_id;
	private String u_name;
	// 一个用户可以选择多个商品
	private Set<Product> products = new HashSet<Product>();
	
	public int getU_id() {
		return u_id;
	}
	public void setU_id(int u_id) {
		this.u_id = u_id;
	}
	public String getU_name() {
		return u_name;
	}
	public void setU_name(String u_name) {
		this.u_name = u_name;
	}
	public Set<Product> getProducts() {
		return products;
	}
	public void setProducts(Set<Product> products) {
		this.products = products;
	}
}

Product.java增加以下属性及getter、setter方法

// 一个产品对应多个用户
	private Set<User> users = new HashSet<User>();

配置映射:

User.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!-- hibernate约束 -->
<!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.hibernate.demo.User" table="user">
 	<!-- id标签建立类中的属性与表中主键映射关系 -->
 		<id name="u_id" column="u_id">
 		<!-- 主键生成策略 -->
 			<generator class="native"></generator>
 		</id>
 		
 		<!-- 建立表中的普通属性和表的其他字段的映射关系 -->
 		<property name="u_name"></property>
 		
 		<!-- 配置与Product多对多的映射关系 -->
 		<!-- 
 			name : product集合的属性名称 
 			table : 中间表的名称	
 			-->
 		<set name="products" table="user_product">
 		<!--  column : 当前对象对应中间表的外键的名称 -->
 			<key column="u_id"></key>
 			<!-- 
 			class : 对方类的全路径
 			column : 对方对象在表中的外键的名称
 			 -->
 			<many-to-many class="com.hibernate.demo.Product"  column="p_id" ></many-to-many>
 		</set>
 	</class>
 </hibernate-mapping>

Product.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!-- hibernate约束 -->
<!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.hibernate.demo.Product" table="product">
 	<!-- id标签建立类中的属性与表中主键映射关系 -->
 		<id name="p_id" column="p_id">
 		<!-- 主键生成策略 -->
 			<generator class="native"></generator>
 		</id>
 		
 		<!-- 建立表中的普通属性和表的其他字段的映射关系 -->
 		<property name="p_name"></property>
 		<property name="p_price"></property>
 		<property name="p_descript"></property>
 		
 		<!-- 配置多对一的关系 -->
 		<!-- 
 			name : 一的一方的对象的属性名称
 			class : 一的一方的全路径
 			column : 多的一方的表的外键的名称	
 		 -->
 		<many-to-one name="category"  cascade="save-update" class="com.hibernate.demo.Category" column="p_c_id"></many-to-one>
 		
 			<!-- 配置与User多对多的映射关系 -->
 		<!-- 
 			name : User集合的属性名称 
 			table : 中间表的名称	
 			-->
 		<set name="users" table="user_product" inverse="true">
 		<!--  column : 当前对象对应中间表的外键的名称 -->
 			<key column="p_id"></key>
 			<!-- 
 			class : 对方类的全路径
 			column : 对方对象在表中的外键的名称
 			 -->
 			<many-to-many class="com.hibernate.demo.User"  column="u_id" ></many-to-many>
 		</set>
 		
 	</class>
 </hibernate-mapping>

核心文件配置:

增加引入user的映射文件

<mapping resource="com/hibernate/demo/User.hbm.xml"/>

测试代码:

package com.hibernate.demo;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.utils.HibernateUtils;

public class HibernateDemo02 {
	
	@Test
	//保存多对多
	public void demo01() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tx = session.beginTransaction();
		
		//创建用户
		User user1 = new User();
		user1.setU_name("陈星星");
		User user2 = new User();
		user2.setU_name("小逗比");
		//创建商品
		Product product1 = new Product();
		product1.setP_name("imac");
		Product product2 = new Product();
		product2.setP_name("Diory口红");
		Product product3 = new Product();
		product3.setP_name("CK包包");
		//设置双向的关联关系
		user1.getProducts().add(product1);
		user2.getProducts().add(product2);
		user2.getProducts().add(product3);
		product1.getUsers().add(user1);
		product2.getUsers().add(user2);
		product3.getUsers().add(user2);
		//保存操作;多对多建表双向关系必须有一方放弃外键维护
		//一般为被动方放弃,此处为product放弃外键维护
		session.save(user1);
		session.save(user2);
		session.save(product1);
		session.save(product2);
		session.save(product3);
		
		tx.commit();
	}
}

结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.3 级联保存

  • 保存用户级联保存商品

User.hbm.xml更新以下配置,增加cascade属性

<set name="products" table="user_product" cascade="save-update">

测试代码

@Test
//级联保存
public void demo02() {
	Session session = HibernateUtils.getCurrentSession();
	Transaction tx = session.beginTransaction();
	
	//创建用户
	User user1 = new User();
	user1.setU_name("饿了吗");
	//创建商品
	Product product1 = new Product();
	product1.setP_name("面包");
	//设置双向的关联关系
	user1.getProducts().add(product1);
	product1.getUsers().add(user1);
	//保存用户,级联保存商品
	session.save(user1);
	
	tx.commit();
}

结果如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 保存商品级联保存用户

跟上面相反,主体映射配置更新cascade属性,设置外键维护inverse即可。

此例省略了

2.4 级联删除

级联删除,删除user则product也被删掉,或者删除product则user也被删掉,业务逻辑上不合理。一般不使用。

cascade="delete"

此例省略了

2.5 更新操作

  • 已存在的User从Product中添加新的商品关联

实例如下:

@Test
	//用户选择新商品
	public void demo03() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tx = session.beginTransaction();
		
		//陈星星再选CK
		User user = session.get(User.class, 5);
		
		//查询商品
		Product product = session.get(Product.class, 14);
		//商品添加至user中
		user.getProducts().add(product);
		
		tx.commit();
	}

结果:
在这里插入图片描述

  • 改选商品

实例如下:

//用户改选商品
	public void demo04() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tx = session.beginTransaction();
	
	//查询用户
	User user = session.get(User.class, 6);
	
	//查询商品
	Product product1 = session.get(Product.class, 13);
	Product product2 = session.get(Product.class, 15);
	//删除不需要的商品,增加要的商品
	user.getProducts().remove(product1);
	user.getProducts().add(product2);
	
	tx.commit();
}

三、一对一

相当于一个表就可以操作

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值