hibernate

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

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

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

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


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

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

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

文章目录


一、generator主键策略

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


generator配置:代表hibernate主键生成策略

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

  1. 由数据库维护
  • identity:Mysql的​​auto_increment​​维护主键自增策略
  • sequence:Oracle使用​​sequence​​序列维护主键自增策略
  • native:本地策略,由hibernate自己根据数据库选择最优策略
  1. 由hibernate维护
  • uuid:生成32位16进制的无效​​字符串​​【记住是一个字串】
  • increment:生成递增的数值类型(每次先查当前的最大的id值+1)
  1. 由开发者维护
  • 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对其进行关联

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

【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);
}

【订单表】

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


【客户表】

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

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

/**
* 查询用户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);
}

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

三、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);
}

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

  • 级联删除
<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);
}

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

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


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

  1. 搞清楚谁级联操作谁?​​添加A的时候,级联添加B,那么级联操作的就是B,配置就在A​
  2. 级联操作的核心是​​cascade="级联操作的属性"​
  • save-update:级联保存
  • delete:级联删除
  • all:级联保存和删除

3.2 INVERSE反转配置

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

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

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

这里我们可以看一下级联保存的的时候,wmendeHibernate会自动地有两条SQL帮助我们去维护被关联的表

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


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


结论:

通常在一对多的关联关系中,多方不能放弃维护权,建议放弃一方,意味着需要在一方加上​​inverse=true的配置​

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

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

4.1 多对多插入操作

4.1.1 数据库设计

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

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>

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

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);
}

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

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 数据库设计

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

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开发