当前位置: 移动技术网 > IT编程>开发语言>Java > Java持久层之Hibernate(Hibernate操作表之间的关系)

Java持久层之Hibernate(Hibernate操作表之间的关系)

2020年08月11日  | 移动技术网IT编程  | 我要评论




表与表之间关系

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

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多对多操作

本文地址:https://blog.csdn.net/MOOG007/article/details/107900140

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网