Java持久层之Hibernate:03_Hibernate操作表之间的关系


表与表之间关系

  • 一对多:分类与商品之间的关系:一个分类中可以有多种商品,一种商品只能属于某一分类
  • 多对多:订单和商品之间的关系:一个订单里面有多种商品,一种商品可以属于多个订单
  • 一对一:在实际开发中使用的不是很多

Hibernate一对多操作

一对多关联关系的建立

  • 第一步:创建两个实体类
package com.ycom.hibernate.crm.po;

// 实体类:客户类
public class Customer {
	private Integer id; 	// 客户id
	private String name;	// 客户名称
	private String level;	// 客户级别
	private String source; 	// 客户来源
	private String phone; 	// 联系电话
	private String mobile;	// 联系手机
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getLevel() {
		return level;
	}
	public void setLevel(String level) {
		this.level = level;
	}
	public String getSource() {
		return source;
	}
	public void setSource(String source) {
		this.source = source;
	}
	public String getPhone() {
		return phone;
	}
	public void setPhone(String phone) {
		this.phone = phone;
	}
	public String getMobile() {
		return mobile;
	}
	public void setMobile(String mobile) {
		this.mobile = mobile;
	}
}
==========================================================================
package com.ycom.hibernate.crm.po;

// 实体类:联系人
public class LinkMan {
	private Integer id; 	// 联系人id
	private String name; 	// 联系人名称
	private String gender;	// 联系人性别
	private String phone; 	// 联系人电话
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public String getPhone() {
		return phone;
	}
	public void setPhone(String phone) {
		this.phone = phone;
	}
}
  • 第二步:让两个实体类关联起来:对象关系
    (1) 一方关联多方
    在这里插入图片描述
    (2) 多方关联一方
    在这里插入图片描述
    在这里插入图片描述
  • 第三步:配置映射文件:一般一个实体类对应一个映射文件
    (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.ycom.hibernate.crm.po.Customer" table="t_customer">
		<!-- 第一部分: 配置实体类唯一属性与表主键的映射关系 -->
		<id name="id" column="id">
			<generator class="native"></generator>
		</id>
		
		<!-- 第二部分:配置实体类其他属性与表的字段的映射关系 -->
		<property name="name" column="name"></property>
		<property name="level" column="level"></property>
		<property name="source" column="source"></property>
		<property name="phone" column="phone"></property>
		<property name="mobile" column="mobile"></property>
		
		<!-- 第三部分:配置两个实体类之间的映射关系 -->
		
	</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.ycom.hibernate.crm.po.LinkMan" table="t_linkman">
		<!-- 第一部分: 配置实体类唯一属性与表主键的映射关系 -->
		<id name="id" column="id">
			<generator class="native"></generator>
		</id>
		
		<!-- 第二部分:配置实体类其他属性与表的字段的映射关系 -->
		<property name="name" column="name"></property>
		<property name="gender" column="gender"></property>
		<property name="phone" column="phone"></property>
		
		<!-- 第三部分:配置两个实体类之间的映射关系 -->
		
	</class>
</hibernate-mapping>

(2) 在映射配置文件中配置一对多的关系

<?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.ycom.hibernate.crm.po.Customer" table="t_customer">
		<!-- 第一部分: 配置实体类唯一属性与表主键的映射关系 -->
		<id name="id" column="id">
			<generator class="native"></generator>
		</id>
		
		<!-- 第二部分:配置实体类其他属性与表的字段的映射关系 -->
		<property name="name" column="name"></property>
		<property name="level" column="level"></property>
		<property name="source" column="source"></property>
		<property name="phone" column="phone"></property>
		<property name="mobile" column="mobile"></property>
		
		<!-- 第三部分:配置两个实体类之间的映射关系
			name属性:就是客户实体类中联系人Set集合的属性名称
		-->
		<set name="linkMans" >
			<!-- 外键配置
				一对多建表有外键:Hibernate机制:双向维护外键,即在一方与多方都要配置外键
				column属性:外键名称
			-->
			<key column="fk_limlman_customer"></key>
			<!-- 一方关联多方 
				class属性:一方关联多方的多方的实体类的全限定类名 
			-->
			<one-to-many class="com.ycom.hibernate.crm.po.LinkMan"/>
		</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.ycom.hibernate.crm.po.LinkMan" table="t_linkman">
		<!-- 第一部分: 配置实体类唯一属性与表主键的映射关系 -->
		<id name="id" column="id">
			<generator class="native"></generator>
		</id>
		
		<!-- 第二部分:配置实体类其他属性与表的字段的映射关系 -->
		<property name="name" column="name"></property>
		<property name="gender" column="gender"></property>
		<property name="phone" column="phone"></property>
		
		<!-- 第三部分:配置两个实体类之间的映射关系 -->
		<!-- 多方关联一方 
			name属性:实体类中关联的一方的属性名称
			class属性:多方关联的一方的全限定类名
			column属性:外键名称,Hibernate要求关联双方必须双向维护外键	
		-->
		<many-to-one name="customer" 
				     class="com.ycom.hibernate.crm.po.Customer" 
				     column="fk_limlman_customer">
		</many-to-one>
	</class>
</hibernate-mapping>
  • 第四步:编写核心配置文件:加载映射配置文件
<?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>
		<!-- 访问数据库服务器的url -->
		<property name="hibernate.connection.url">jdbc:mysql:///hibernate05</property>
		<!-- 访问数据库服务器的用户名 -->
		<property name="hibernate.connection.username">root</property>
		<!-- 访问数据库服务器的密码 -->
		<property name="hibernate.connection.password">root</property>
		
		
		<!-- 第二部分:Hibernate自身的配置 -->
		<!-- 让Hibernate输出SQL语句 -->
		<property name="hibernate.show_sql">true</property>
		<!-- 让Hibenate输出的SQL语句是格式化的SQL语句 -->
		<property name="hibernate.format_sql">true</property>
		<!-- 让Hibernate帮助我们创建数据表 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- 配置MySQL数据库方言 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		<!-- 获取本地线程Session的配置 -->
		<property name="hibernate.current_session_context_class">thread</property>
		
		
		<!-- 第三部分:加载映射配置文件 -->
		<mapping resource="com/ycom/hibernate/crm/po/Customer.hbm.xml"/>
		<mapping resource="com/ycom/hibernate/crm/po/LinkMan.hbm.xml"/>
	</session-factory>
</hibernate-configuration>
  • 第五步:测试能否自动创建两张表
    在这里插入图片描述
    在这里插入图片描述

一对多操作:级联保存

  • 复杂的操作
@Test
public void testSave() {
	Customer customer = new Customer();
	customer.setName("AAAAA");
	customer.setLevel("Vip");
	customer.setSource("网络");
	customer.setPhone("110");
	customer.setMobile("123456");
	
	LinkMan man1 = new LinkMan();
	man1.setName("M1");
	man1.setGender("male");
	man1.setPhone("123456");
	
	LinkMan man2 = new LinkMan();
	man2.setName("M2");
	man2.setGender("female");
	man2.setPhone("12345678");
	
	// 建立对象的关联关系
	// 一方关联多方
	customer.getLinkMans().add(man1);
	customer.getLinkMans().add(man2);
	// 多方关联一方
	man1.setCustomer(customer);
	man2.setCustomer(customer);
	
	SessionFactory sessionFactory = null;
	Session session = null;
	Transaction tx = null;
	try {
		sessionFactory = HibernateUtils.getSessionFactory();
		session  = sessionFactory.openSession();
		tx =  session.beginTransaction();
		
		session.save(customer);
		session.save(man1);
		session.save(man2);
		
		tx.commit();
	} catch (Exception e) {
		tx.rollback();
	} finally {
		session.close();
		sessionFactory.close();//在Web项目开发中不关闭
	}
	
}
-------------------------------------------------------------------------------
结果:
Hibernate: 
    insert 
    into
        t_customer
        (name, level, source, phone, mobile) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        t_linkman
        (name, gender, phone, fk_limlman_customer) 
    values
        (?, ?, ?, ?)
Hibernate: 
    insert 
    into
        t_linkman
        (name, gender, phone, fk_limlman_customer) 
    values
        (?, ?, ?, ?)
Hibernate: 
    update
        t_linkman 
    set
        fk_limlman_customer=? 
    where
        id=?
Hibernate: 
    update
        t_linkman 
    set
        fk_limlman_customer=? 
    where
        id=?

在这里插入图片描述
(1) 需要一方关联多方,同样需要多方关联一方
(2) session.sqve()既需要保存一方,也需要报错多方:例如多方有100个,那么需要save一百次

  • 简单的操作:就是对上面的关联关系进行简化,同时对session.save()操作进行简化:前提:需要在一方的映射配置文件中进行相关属性的配置:在客户实体类的映射配置文件中的set标签中进行配置
    (1) 映射配置文件如下
    在这里插入图片描述
    (2)程序代码如下
@Test
public void testSave2() {
	Customer customer = new Customer();
	customer.setName("AAAAA");
	customer.setLevel("Vip");
	customer.setSource("网络");
	customer.setPhone("110");
	customer.setMobile("123456");
	
	LinkMan man1 = new LinkMan();
	man1.setName("M1");
	man1.setGender("male");
	man1.setPhone("123456");
	
	LinkMan man2 = new LinkMan();
	man2.setName("M2");
	man2.setGender("female");
	man2.setPhone("12345678");
	
	// 建立对象的关联关系:只需要关联一次即可
	customer.getLinkMans().add(man1);
	customer.getLinkMans().add(man2);

	SessionFactory sessionFactory = null;
	Session session = null;
	Transaction tx = null;
	try {
		sessionFactory = HibernateUtils.getSessionFactory();
		session  = sessionFactory.openSession();
		tx =  session.beginTransaction();
		
		// 只需要保存一方对象即可
		session.save(customer);
		
		tx.commit();
	} catch (Exception e) {
		tx.rollback();
	} finally {
		session.close();
		sessionFactory.close();//在Web项目开发中不关闭
	}
	
}
------------------------------------------------------------------------------
Hibernate: 
    insert 
    into
        t_customer
        (name, level, source, phone, mobile) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        t_linkman
        (name, gender, phone, fk_limlman_customer) 
    values
        (?, ?, ?, ?)
Hibernate: 
    insert 
    into
        t_linkman
        (name, gender, phone, fk_limlman_customer) 
    values
        (?, ?, ?, ?)
Hibernate: 
    update
        t_linkman 
    set
        fk_limlman_customer=? 
    where
        id=?
Hibernate: 
    update
        t_linkman 
    set
        fk_limlman_customer=? 
    where
        id=?

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

一对多操作:级联删除

  • 级联删除:删除主表中的记录,子表中的所有关联的记录都被删除
  • 先查询主表中的记录得到持久化的对象,执行删除操作
  • 代码
@Test
public void testDeleteCascade() {

	SessionFactory sessionFactory = null;
	Session session = null;
	Transaction tx = null;
	try {
		sessionFactory = HibernateUtils.getSessionFactory();
		session  = sessionFactory.openSession();
		tx =  session.beginTransaction();
		
		Customer customer = session.get(Customer.class, 4);
		session.delete(customer);
		
		tx.commit();
	} catch (Exception e) {
		tx.rollback();
	} finally {
		session.close();
		sessionFactory.close();//在Web项目开发中不关闭
	}
}

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

  • 删除主表中的记录,子表中关联的记录是逻辑删除,即并不是真正的删除子表中的关联记录,而是将子表中关联的记录的外键字段置为NULL:如果要达到级联删除的目的,需要在映射配置文件中进行相关的配置:在一方的映射配置文件中的set标签中的cascade属性添加delete值
    在这里插入图片描述
  • 执行过程
    在这里插入图片描述
  • 那么问题来了:既然是要删除子表中关联的记录,那么在删除之间为什么还需要进行外键字段的置空操作呢?实际上直接先删除子表中的关联记录再删除主表中的记录即可!不需要再搞什么update操作了:之所以有这个操作是因为:Hibernate双向维护外键,一方与多方都需要维护外键!解决办法:让其中一方放弃维护外键:在一对多的关系中让一方放弃维护外键:需要在一方的映射配置文件中的set标签的inverse属性值为true
    在这里插入图片描述
  • 注意:不能在任何时候都放弃外键关系的维护:如果是这样那就不能做级联添加了:子表记录的外键字段为NULL,因为一方没有维护外键是不行的

一对多修改(inverse=true)

在这里插入图片描述

@Test
public void testUpdateCascade() {
	SessionFactory sessionFactory = null;
	Session session = null;
	Transaction tx = null;
	try {
		sessionFactory = HibernateUtils.getSessionFactory();
		session  = sessionFactory.openSession();
		tx =  session.beginTransaction();
		
		
		Customer customer = session.get(Customer.class, 12);
		LinkMan man = session.get(LinkMan.class, 21);
		customer.getLinkMans().add(man);
		man.setCustomer(customer);
		
		System.out.println(customer);
		System.out.println(man);

		
		tx.commit();
	} catch (Exception e) {
		tx.rollback();
	} finally {
		session.close();
		sessionFactory.close();//在Web项目开发中不关闭
	}
}
----------------------------------------------------------------------------
Hibernate: 
    select
        customer0_.id as id1_0_0_,
        customer0_.name as name2_0_0_,
        customer0_.level as level3_0_0_,
        customer0_.source as source4_0_0_,
        customer0_.phone as phone5_0_0_,
        customer0_.mobile as mobile6_0_0_ 
    from
        t_customer customer0_ 
    where
        customer0_.id=?
Hibernate: 
    select
        linkman0_.id as id1_1_0_,
        linkman0_.name as name2_1_0_,
        linkman0_.gender as gender3_1_0_,
        linkman0_.phone as phone4_1_0_,
        linkman0_.fk_limlman_customer as fk_limlm5_1_0_ 
    from
        t_linkman linkman0_ 
    where
        linkman0_.id=?
Hibernate: 
    select
        linkmans0_.fk_limlman_customer as fk_limlm5_1_0_,
        linkmans0_.id as id1_1_0_,
        linkmans0_.id as id1_1_1_,
        linkmans0_.name as name2_1_1_,
        linkmans0_.gender as gender3_1_1_,
        linkmans0_.phone as phone4_1_1_,
        linkmans0_.fk_limlman_customer as fk_limlm5_1_1_ 
    from
        t_linkman linkmans0_ 
    where
        linkmans0_.fk_limlman_customer=?
Customer [id=12, name=BBBBB, level=null, source=null, phone=null, mobile=null]
LinkMan [id=21, name=M2, gender=female, phone=12345678, customer=Customer [id=12, name=BBBBB, level=null, source=null, phone=null, mobile=null]]
Hibernate: 
    update
        t_linkman 
    set
        name=?,
        gender=?,
        phone=?,
        fk_limlman_customer=? 
    where
        id=?
Hibernate: 
    update
        t_linkman 
    set
        fk_limlman_customer=? 
    where
        id=?

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

  • inverse属性:因为Hibernate双向维护外键,在客户和联系人里面都需要维护外键,修改客户时候修改一次外键,修改联系人时候也修改一次外键,造成效率问题
  • 解决方式:让其中的一方不维护外键:一对多里面,让其中一方放弃外键维护
    在这里插入图片描述
    在这里插入图片描述
  • 上面说错了 :

Hibernate多对多操作

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值