Hibernate 是 Java 生态中著名的 ORM 框架之一。Hibernate 现在也在扩展自己的生态,开始支持多种异构数据的持久化,不仅仅提供 ORM 框架,还提供了 Hibernate Search 来支持全文搜索,提供 validation 来进行数据校验,提供 Hibernate OGM 来支持 NoSQL 解决方案。

这里我们要重点讲解的是 Hibernate ORM 的相关内容,截至 2020 年底,Hibernate ORM 的最新版本是 5.4 版本,6.0 版本还正在开发中。作为一个老牌的 ORM 框架,Hibernate 经受住了 Java EE 企业级应用的考验,一度成为 Java ORM 领域的首选框架。

在使用 Hibernate 的时候,Java 开发可以使用映射文件或是注解定义 Java 语言中的类与数据库中的表之间的各种映射关系,这里使用到的映射文件后缀为“.hbm.xml”。hbm.xml 映射文件将一张数据库表与一个 Java 类进行关联之后,该数据库表中的每一行记录都可以被转换成对应的一个 Java 对象。正是由于 Hibernate 映射的存在,Java 开发只需要使用面向对象思维就可以完成数据库表的设计。

在 Java 这种纯面向对象的语言中,两个 Java 对象之间可能存在一对一、一对多或多对多等复杂关联关系。Hibernate 中的映射文件也必须要能够表达这种复杂关联关系才能够满足我们的需求,同时,还要能够将这种关联关系与数据库中的关联表、外键等一系列关系模型中的概念进行映射,这也就是 ORM 框架中常提到的“关联映射”。

下面我们就来结合示例介绍“一对多”关联关系。例如,一个顾客(Customer)可以创建多个订单(Order),而一个订单(Order)只属于一个顾客(Customer),两者之间存在一对多的关系。在 Java 程序中,可以在 Customer 类中添加一个 List 类型的字段来维护这种一对多的关系;在数据库中,可以在订单表(t_order)中添加一个 customer_id 列作为外键,指向顾客表(t_customer)的主键 id,从而维护这种一对多的关系,如下图所示:

https://cdn.nlark.com/yuque/0/2021/png/576791/1635439916898-1e594dd7-99e2-4d71-b580-5349fb0152fa.png

关系模型中的一对多和对象模型中的一对多

在 Hibernate 中,可以通过如下 Customer.hbm.xml 配置文件将这两种关系进行映射:

<hibernate-mapping>
    <!-- 这里指定了Order类与t_order表之间的映射 -->
    <class name="com.mybatis.test.Order" table="t_order">
        <!-- Order类中的id属性与t_order表中主键id之间的映射 -->
        <id name="id" column="id"/>
        <!-- Order类中的address属性与t_order表中address列之间的映射 -->
        <property name="address" column="address"/>
        <!-- Order类中的tele属性与t_order表中tele列之间的映射 -->
        <property name="tele" column="tele"/>
        <!-- Order类中customer属性与t_order表中customer_id之间的映射,
             同时也指定Order与Customer之间的多对一的关系 -->
        <many-to-one name="customer" column="customer_id"></many-to-one>
    </class>
</hibernate-mapping>

如果是双向关联,则在 Java 代码中,可以直接在 Order 类中添加 Customer 类型的字段指向关联的 Customer 对象,并在相应的 Order.hbm.xml 配置文件中进行如下配置:

<hibernate-mapping>
    <!-- 这里指定了Order类与t_order表之间的映射 -->
    <class name="com.mybatis.test.Order" table="t_order">
        <!-- Order类中的id属性与t_order表中主键id之间的映射 -->
        <id name="id" column="id"/>
        <!-- Order类中的address属性与t_order表中address列之间的映射 -->
        <property name="address" column="address"/>
        <!-- Order类中的tele属性与t_order表中tele列之间的映射 -->
        <property name="tele" column="tele"/>
        <!-- Order类中customer属性与t_order表中customer_id之间的映射,
             同时也指定Order与Customer之间的多对一的关系 -->
        <many-to-one name="customer" column="customer_id"></many-to-one>
    </class>
</hibernate-mapping>

一对一、多对多等关联映射在 Hibernate 映射文件中,都定义了相应的 XML 标签,原理与“一对多”基本一致,只是使用方式和场景略有不同,这里就不再展开介绍,你若感兴趣的话可以参考 Hibernate 的官方文档进行学习。

https://cdn.nlark.com/yuque/0/2021/png/576791/1635439687053-031ff9d4-556c-4c94-8412-c567f8926250.png

除了能够完成面向对象模型与数据库中关系模型的映射,Hibernate 还可以帮助我们屏蔽不同数据库产品中 SQL 语句的差异。

我们知道,虽然目前有 SQL 标准,但是不同的关系型数据库产品对 SQL 标准的支持有细微不同,这就会出现一些非常尴尬的情况,例如,一条 SQL 语句在 MySQL 上可以正常执行,而在 Oracle 数据库上执行会报错。

Hibernate封装了数据库层面的全部操作,Java 程序员不再需要直接编写 SQL 语句,只需要使用 Hibernate 提供的 API 即可完成数据库操作。

https://cdn.nlark.com/yuque/0/2021/png/576791/1635439710864-178ab06e-0443-4b7a-bf75-89b9475c733d.png

例如,Hibernate 为用户提供的 Criteria 是一套灵活的、可扩展的数据操纵 API,最重要的是 Criteria 是一套面向对象的 API,使用它操作数据库的时候,Java 开发者只需要关注 Criteria 这套 API 以及返回的 Java 对象,不需要考虑数据库底层如何实现、SQL 语句如何编写,等等。

下面是 Criteria API 的一个简单示例: