一、一对多
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();
}
三、一对一
相当于一个表就可以操作