重量级ORM框架--持久化框架Hibernate【关系映射详解】

hibernate

Hibernate是一款重量级的持久层框架,目前市面上的我很少见还有项目在开发时候使用他,之所以要学习这个,因为公司最近有一个系统升级的项目,之前的老系统用到了Hibernate。

在这里插入图片描述

同样还是老套路,学习一个新技术或者新知识,首先去他的官网看

【官网】:https://hibernate.org/orm/
【官方教程】:https://hibernate.org/orm/documentation/getting-started/
【github地址】:https://github.com/hibernate


目前hibernate已经更新到了6的版本,这里使用的5的版本

在这里插入图片描述

注意: Hibernate5.1;5.2官方推荐使用JDK1.8以及JDBC4


一、generator主键策略

在这里插入图片描述
generator配置:代表hibernate主键生成策略

【我们可以将其分为三大类】:

  1. 由数据库维护
    • identity:Mysql的auto_increment维护主键自增策略
    • sequence:Oracle使用sequence序列维护主键自增策略
    • native :本地策略,由hibernate自己根据数据库选择最优策略
  2. 由hibernate维护
    • uuid:生成32位16进制的无效字符串【记住是一个字串】
    • increment:生成递增的数值类型(每次先查当前的最大的id值+1)
  3. 由开发者维护
    • asigned:必须要有程序员给定id的值,否则报错

二、对象关系映射:一对多映射

需求案例:客户 与 订单的需求关系:典型的一对多的关系

2.1 测试准备

这里我们需要在创建一张表t_order

CREATE TABLE `t_order` (
	`id` INT(10) NOT NULL AUTO_INCREMENT,
	`order_no` VARCHAR(20) NULL DEFAULT NULL COMMENT '订单编号' COLLATE 'utf8mb4_general_ci',
	`product_name` VARCHAR(20) NULL DEFAULT NULL COMMENT '产品名称' COLLATE 'utf8mb4_general_ci',
	`cust_id` INT(10) NULL DEFAULT NULL,
	PRIMARY KEY (`id`) USING BTREE,
	UNIQUE INDEX `t_order_id_uindex` (`id`) USING BTREE,
	INDEX `t_order_t_customer_c_id_fk` (`cust_id`) USING BTREE,
	CONSTRAINT `t_order_t_customer_c_id_fk` FOREIGN KEY (`cust_id`) REFERENCES `hibernate_db`.`t_customer` (`c_id`) ON UPDATE NO ACTION ON DELETE NO ACTION
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=2;

2.2 创建实体

注意事项:

  1. 需要在Custom实体中先给一个List<Order>来关联多方的实体信息

  2. order实体表也需要给一个Custom的JavaBean对其进行关联

在这里插入图片描述

【Order】

public class Order implements Serializable {
    private static final long serialVersionUID = 1L;

    private String id; // 订单表主键
    private String orderNo; // 订单编号
    private String prodName; // 产品名称

    private Integer custId; // 外键客户id

    /*一个订单对一个客户,一个客户对多个订单*/
    private Customer customer;
}

2.3 hbm.xml文件修改

【customer.hbm.xml】
在Custom.hbm.xml文件里,我们需要做的是将order进行关联用set标签

<class name="com.wei.domain.Customer" table="t_customer">
        <id name="id" column="c_id">
            <generator class="identity"></generator>
        </id>
        <property name="name" column="c_name"></property>
        <property name="age" column="c_age"></property>
        <property name="gender" column="c_gender"></property>
        <property name="level" column="c_level"></property>

        <!--一对多配置-->
        <!--name设置本实体类(customer)关联的属性字段名-->
        <set name="orders" >
            <!--外键的字段名称-->
            <key column="cust_id"></key>
			<!--关联的实体对象的完整类路径-->
            <one-to-many class="com.wei.domain.Order"/>
        </set>
    </class>

一表中的关联配置使用的标签是:<one-to-many class="所关联的类的全类名"></on-to-mony>

【order.hbm.xml】

<class name="com.wei.domain.Order" table="t_order">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="orderNo" column="order_no"/>
        <property name="prodName" column="product_name"/>

        <!--多对一配置-->
        <many-to-one name="customer" column="cust_id" class="com.wei.domain.Customer"></many-to-one>
    </class>

多表中所关联的字段是用的标签是<many-to-one name="关联的本实体类在的字段名" column="外键字段" class=“所引用的实体类的全类名”></many-to-one>

2.4 编写测试类

    @Test
    public void test1() throws HibernateException {
        // 准备数据
        /*
        * 需求:一个客户两个订单
        * */
        Customer lisi  = new Customer();
        lisi.setName("里斯");
        lisi.setGender("1");
        lisi.setAge(32);
        lisi.setLevel("至尊VIP,年卡8级");
        
        Set<Order> orders = new HashSet<>();
        // 订单1
        Order order1 = new Order();
        order1.setOrderNo("20220807111");
        order1.setProdName("小米Ultr11");
        order1.setCustomer(lisi);
        // 订单2
        Order order2 = new Order();
        order2.setOrderNo("20220807134");
        order2.setProdName("华为MateBook_Pro 512G+8G");
        order2.setCustomer(lisi);
        // 客户添加订单
        orders.add(order1);
        orders.add(order2);
        lisi.setOrders(orders);
        
        Session session = HibernateUtil.getConnection();
        //4、开启事务
        Transaction tx = session.beginTransaction();
        //5、执行添加操作
        session.save(lisi);
        session.save(order1);
        session.save(order2);
        //6、提交事务
        tx.commit();
        //7、关闭资源
        HibernateUtil.closeSession(session);
    }

【订单表】
在这里插入图片描述
【客户表】
在这里插入图片描述

为了可以证实是否插入成功,我么可以再写一个方法来查询某一个客户的订单

/**
     * 查询用户guan连查询其所的订单
     *
     * @throws HibernateException
     */
    @Test
    public void test2() throws HibernateException {

        Session session = HibernateUtil.getConnection();
        //4、开启事务
        Transaction tx = session.beginTransaction();
        //5、执行添加操作
        Customer customer = session.get(Customer.class, 5);
        System.out.println("用户名称:"+customer.getName());
        Set<Order> orders = customer.getOrders();
        for (Order order : orders) {
            System.out.println("订单:"+order.getOrderNo());
        }
        //6、提交事务
        tx.commit();
        //7、关闭资源
        HibernateUtil.closeSession(session);
    }

在这里插入图片描述

三、casecode和inverse配置

3.1 CaseCade级联操作

级联操作,就是操作一个对象的时候,相同时操作他的关联对象。

  • 级联保存
<set name="orders" cascade="save-update">
     <!--外键的字段名称-->
     <key column="cust_id"></key>
     <one-to-many class="com.wei.domain.Order"/>
</set>

【测试】

@Test
    public void test3() throws HibernateException {
        // 准备数据
        Customer lisi = new Customer();
        lisi.setName("马云云");
        lisi.setGender("1");
        lisi.setAge(34);
        lisi.setLevel("至尊VIP,年卡n级");

        Set<Order> orders = new HashSet<>();
        // 订单1
        Order order1 = new Order();
        order1.setOrderNo("20220808123");
        order1.setProdName("阿里巴巴");
        order1.setCustomer(lisi);
        // 订单2
        Order order2 = new Order();
        order2.setOrderNo("20220808141");
        order2.setProdName("华强北 只能IPHONE watch");
        order2.setCustomer(lisi);
        // 客户添加订单
        orders.add(order1);
        orders.add(order2);
        lisi.setOrders(orders);

        Session session = HibernateUtil.getConnection();
        //4、开启事务
        Transaction tx = session.beginTransaction();
        //5、执行添加操作
        session.save(lisi); // 保存客户
        // 级联保存订单数据,所以配置在客户的映射文件中
        //6、提交事务
        tx.commit();
        //7、关闭资源
        HibernateUtil.closeSession(session);
    }

在这里插入图片描述

  • 级联删除
<set name="orders" cascade="save-update,delete" >
     <!--外键的字段名称-->
     <key column="cust_id"></key>
     <one-to-many class="com.wei.domain.Order"/>
</set>

【测试】

 @Test
    public void test4() throws HibernateException {

        Session session = HibernateUtil.getConnection();
        //4、开启事务
        Transaction tx = session.beginTransaction();
        //5、执行添加操作
        Customer cus = session.get(Customer.class, 6);// 级联删除
        //6、提交事务
        session.delete(cus);
        tx.commit();
        //7、关闭资源
        HibernateUtil.closeSession(session);
    }

在这里插入图片描述

如果没有级联删除,则会把外键的值置为空。级联删除则会在删除一个对象的同时将其关联的的对象也删除。


【实现级联操作的注意事项】:🔥🔥🔥

  1. 搞清楚谁级联操作谁?添加A的时候,级联添加B,那么级联操作的就是B,配置就在A

  2. 级联操作的核心是cascade="级联操作的属性"

    • save-update:级联保存
    • delete:级联删除
    • all:级联保存和删除

3.2 INVERSE反转配置

第一次听到这个概念让我想起了Spring的IOC

inverse配置:表示是否把关联表的维护权反转(放弃)

  • true:放弃
  • false:不放弃(不反转)

这里我们可以看一下级联保存的的时候,wmendeHibernate会自动地有两条SQL帮助我们去维护被关联的表
在这里插入图片描述
在这里插入图片描述
结论:
通常在一对多的关联关系中,多方不能放弃维护权,建议放弃一方,意味着需要在一方加上inverse=true的配置

四、对象关系映射:多对多映射

需求:用户 与 角色的关联关系:多对多的关系

4.1 多对多插入操作

4.1.1 数据库设计

在这里插入图片描述

4.1.2 对象设计

【User表】

public class User implements Serializable {

    private Integer id;
    private String name;
	// getter、setter...
 }

【Role表】

public class Role implements Serializable {

    private Integer id;
    private String name;
	// getter、setter...
 }

4.1.3 映射的配置🔥🔥

【Role.hbm.xml】

<?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.wei.domain.Role" table="t_role">
        <id name="id" column="id"/>
        <property name="name" column="name"/>
        <!--多对多配置-->
        <!--
            table;配中间表的名字
        -->
        <set name="users" table="t_user_role" inverse="true">
            <!--当前表中间表的外键-->
            <key column="role_id"/>
            <!--class关联表的类名,column为对方关联的表在中间表的外键-->
            <many-to-many class="com.wei.domain.User" column="user_id"/>
        </set>
      </class>

</hibernate-mapping>

【User.hbm.xml】

<?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.wei.domain.User" table="t_user">
        <id name="id" column="id"/>
        <property name="name" column="name"/>
        <!--多对多配置-->
        <!--
            table;配中间表的名字
        -->
        <set name="roles" table="t_user_role">
            <!--当前表中间表的外键-->
            <key column="user_id"/>
            <!--class关联表的类名,column为对方关联的表在中间表的外键-->
            <many-to-many class="com.wei.domain.Role" column="role_id"/>
        </set>
      </class>

</hibernate-mapping>

注意事项:

  1. 在配置任意一方的set集合关联字段的时候,我们知道hibernate是默认inverse=false的,这样一来在多对多的关系中,就会出现重复的维护冲突,所以我们需要在任意一方设置inverse=true
  2. 这里建议使用MySQL5的版本,因为hibernate创建表的时候会将存储引擎设置为myisam,如果使用了Mysql8的版本,需要在全局的核心配置文件中设置上
    <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>,否则会报SQL语法错误
  3. 在任意一方配置是都不能配置主键策略,否则无法关联外键的关联关系
  <!-- <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> -->
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>

在这里插入图片描述

4.2 多对多查询

@Test
    public void test6() throws HibernateException {
        // 准备数据

        Session session = HibernateUtil.getConnection();
        //4、开启事务
        Transaction tx = session.beginTransaction();
        //5、执行添加操作
        User user = session.get(User.class, 1);
        System.out.println("当前用户:"+user.getName());
        Set<Role> roles1 = user.getRoles();
        for (Role role : roles1) {
            System.out.println("当前用户的角色:"+role.getName());
        }

        User user2 = session.get(User.class, 2);
        System.out.println("当前用户:"+user2.getName());
        Set<Role> roles = user2.getRoles();
        for (Role role : roles) {
            System.out.println("当前用户的角色:"+role.getName());
        }
        //6、提交事务
        tx.commit();
        //7、关闭资源
        HibernateUtil.closeSession(session);
    }

在这里插入图片描述

4.3 多对多级联添加操作

User.hbm.xml配置

<set name="roles" table="t_user_role" cascade="save-update">
     <!--当前表中间表的外键-->
     <key column="user_id"/>
     <!--class关联表的类名,column为对方关联的表在中间表的外键-->
     <many-to-many class="com.wei.domain.Role" column="role_id"/>
</set>

注意事项:
1.级联操作就是在任意一方设置casecade=级联配置—> 比如级联保存是save-update

 @Test
    public void test7() throws HibernateException {
        // 准备数据

        Session session = HibernateUtil.getConnection();
        //4、开启事务
        Transaction tx = session.beginTransaction();
        //5、执行添加操作
        User user1 = new User();
        user1.setId(3);
        user1.setName("大公鸡");
        Role r1 = new Role();
        r1.setName("五星红钻");
        r1.setId(3);
        Set<Role> roles = new HashSet<>();
        roles.add(r1);
        user1.setRoles(roles);
        
        session.save(user1);
        //6、提交事务
        tx.commit();
        //7、关闭资源
        HibernateUtil.closeSession(session);
    }

不过通常我更偏向于使用all 包括了级联保存和删除

五、对象关系映射:一对一映射

需求:中华人民共和国公民 与 个人身份证 是典型的一对一关系

5.1 数据库设计

在这里插入图片描述

5.2 实体类设计

5.2.1 外键关联

【IdCard实体类】

public class IdCard implements Serializable {
    private Integer id;
    private String name;

    private Person person;
	//getter、setter...
}

【Person实体类】

public class Personimplements Serializable {
    private Integer id;
    private String name;

    private IdCard card;
	//getter、setter...
}

5.2.2 主键关联

【IdCard实体类】

public class IdCard implements Serializable {
    private Integer id;
    private String name;

    private Person person;
	//getter、setter...
}

【Person实体类】

public class Personimplements Serializable {
    private Integer id;
    private String name;

    private IdCard card;
	//getter、setter...
}

5.3 配置文件

5.3.1 外键关联

【Person.hbm.xml】

<?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.wei.domain.onetoone_fk.Person" table="t_person_fk">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>
        <!--一对一配置-->
        <one-to-one name="card" class="com.wei.domain.onetoone_fk.IdCard"/>
      </class>

</hibernate-mapping>

【IdCard.hbm.xml】

<?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.wei.domain.onetoone_fk.IdCard" table="t_card_fk">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>
        <!--一对一配置 使用unique设置唯一外键 -->
        <many-to-one  name="person" class="com.wei.domain.onetoone_fk.Person" column="person_id" unique="true"/>
      </class>

</hibernate-mapping>

注意事项

  1. 在一对一的配置中给,我们需要设置任意一方的属性为many-to-one,然后通过核心的unique设置其外键是唯一

5.3.2 主键关联

【Person.hbm.xml】
与外键关联保持一致
【IdCard.hbm.xml】

<?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.wei.domain.onetoone_pk.IdCard" table="t_card_pk">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>
        <!--一对一配置 主键关联 -->
        <one-to-one  name="person" class="com.wei.domain.onetoone_pk.Person" constrained="true"/>
      </class>

</hibernate-mapping>

注意事项:

  1. 逐渐概念需要使用one-to-one标签 其中使用constraint=”true“配置主键关联

5.4 测试

5.4.1 外键关联

@Test
    public void test8() throws HibernateException {
        // 准备数据
        Person p = new Person();
        p.setName("魏巍");
        IdCard idCard = new IdCard();
        idCard.setName("中华人民共和国个人身份证:61020210213826465456");
        idCard.setPerson(p);
        p.setCard(idCard);
        Session session = HibernateUtil.getConnection();
        //4、开启事务
        Transaction tx = session.beginTransaction();
        //5、执行添加操作
        session.save(p);
        session.save(idCard);

        //6、提交事务
        tx.commit();
        //7、关闭资源
        HibernateUtil.closeSession(session);
    }

5.4.2 主键关联

@Test
    public  void test10(){
        Session session = HibernateUtil.getConnection();

        Transaction tx = session.beginTransaction();
        com.wei.domain.onetoone_pk.Person p = new com.wei.domain.onetoone_pk.Person();

        p.setName("刘虎");
        com.wei.domain.onetoone_pk.IdCard card = new com.wei.domain.onetoone_pk.IdCard();
        card.setName("中国台湾身份证6675677457576386");
        card.setPerson(p);
        p.setCard(card);

        session.save(p);
        session.save(card);

        tx.commit();

        HibernateUtil.closeSession(session);
    }

【至此,第二章的hibernate的学习就此结束】
总结:

  1. 配置文件的方式配置一对一,一对多或者是多对多的关系都比较冗余(个人觉得),后期会学习注解是的JPA开发
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@WAT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值