Hibernate基础

Hibernate基础总结

有利的条件和主动的恢复产生于再坚持一下的努力之中!

好久没更新了,今天入门了Hibernate,由于之前学习了MyBatis,初步感觉二者的底层实现思想有很多相似之处,下面让我们以一个入门Demo的形式感受一下Hibernate的配置过程。

1. Maven工程,基于Mysql 8.0+版本 Hibernate的POM文件:

经验证不存在依赖冲突,可直接使用:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yu.hibernate</groupId>
    <artifactId>Hibernate</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-directory-provider</artifactId>
            <version>9.0.1.Final</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.4.0.Final</version>
        </dependency>

        <dependency>
            <groupId>jakarta.xml.bind</groupId>
            <artifactId>jakarta.xml.bind-api</artifactId>
            <version>2.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.3.5</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

2. 创建标准Maven工程目录结构

下面是一个较为标准的mavan工程目录文件结构

3.Hibernate核心实现思想

Hibernate作为一个ORM映射框架,本质就是对传统JDBC操作数据库的繁琐操作进行了优化,引入了封装的特性。主要实现的功能可以从两个角度来看:

① 从数据库的角度来看:

  • 自动生成 CRUD SQL 语句:Hibernate 能够根据实体类的定义自动生成相应的 CRUD(创建、读取、更新、删除)SQL 语句,程序员不需要手动编写这些 SQL 语句。这大大减少了开发中的重复工作。
  • 严格遵循 ORM 原则:Hibernate 通过映射 Java 对象与数据库表之间的关系,确保 SQL 语句中的数据全部来自于 Java 对象的属性。这种方式使得对象与数据库之间的同步变得简单。
  • 自定义 SQL 的局限性:虽然 Hibernate 提供了方便的自动化功能,但对于复杂查询或性能优化,使用自定义 SQL 可能会变得繁琐。相较之下,MyBatis 提供了更灵活的 SQL 编写方式,使得开发者能更容易地对 SQL 进行优化。有资料称MyBatis为半自动ORM框架,而Hibernate为全自动ORM框架。

② 从应用程序的角度来看:

  • 对象封装:Hibernate 能够将从数据库中查询到的数据封装成相应的 Java 对象,并返回给应用程序。这使得应用程序可以直接以对象的形式操作数据,而不需要关注底层的 SQL 细节。
  • 会话管理:Hibernate 提供了会话(Session)管理功能,负责在应用程序与数据库之间进行对象的持久化和状态管理。这种机制简化了数据的读取和写入过程,提高了开发效率。
  • 缓存机制:Hibernate 还内置了一级和二级缓存机制,能够有效地提高数据访问的性能,减少数据库的访问频率。

4. Hibernate核心配置文件(hibernate.cfg.xml)

Hibernate规定其核心配置文件的名称和位置,其中hibernate.cfg.xml文件必须位于应用的根目录下,且名称必须为hibernate.cfg.xml,因为框架底层源码需要读取指定目录下的指定文件,若不遵循框架规范则无法使用框架,这个文件主要是记录了连接数据库的一些必要配置(驱动,url,用户名,密码等)。

【注意】(此处容易踩坑):下面的mysql是8.0以上版本,由于mysql8.0使用的 JDBC 驱动程序(mysql-connector-java)进行了更新,支持新的功能和特性。这可能导致了连接 URL 的一些变化,以适应新的驱动程序特性。下面为5.0版本和8.0版本的url:


mysql 5.0 -URL

jdbc:mysql://localhost:3306/yourdb?user=root&password=abc123

mysql 8.0 -URL

jdbc:mysql://localhost:3306/yourdb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true


另外需要注意:XML(可拓展标记语言)标签中不支持“&”符号,如果使用的是mysql8.0+版本,url中必须使用”&amp;“取代"&"符号,否则该文件不会被解析为合法的XML文件导致运行错误!小编就是在这里踩了坑。。(T_T)

<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/springboot_db?characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=UTC&amp;rewriteBatchedStatements=true</property>

 hibernate.cfg.xml文件(完整):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <!--配置所使用的Hibernate方言-->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property>
        <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>

        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/springboot_db?characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=UTC&amp;rewriteBatchedStatements=true</property>
        <!--配置数据库用户名-->
        <property name="hibernate.connection.username">root</property>
        <!--配置数据库密码-->
        <property name="hibernate.connection.password">abc123</property>

        <!--  执行操作时是否在控制台打印 SQL  -->
        <property name="show_sql">true</property>
        <!--  是否对 SQL 进行格式化  -->
        <property name="format_sql">true</property>
        <!--  指定自动生成数据表的策略
                <property name="hbm2ddl.auto">update</property>
                  -->
        <!--  指定关联的 .hbm.xml 文件  -->
        <mapping resource="com/yu/hibernate/mapper/Customer.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

其中<mapper>标签中需要给出实体类映射文件 (XXX.hbm.xml)的具体位置:

小编将映射文件放到了实体类包下,Maven工程中,项目编译后resources目录下的文件会被放到项目根路径下。

分析Hibernate核心配置文件:

  • <hibernate-configuration>:这是 Hibernate 配置文件的根元素。
  • <session-factory>:该元素包含所有与 SessionFactory 相关的配置项。SessionFactory 是 Hibernate 的核心,它负责创建 Session 对象并管理与数据库的连接。
  • <property name="hibernate.dialect">:指定 Hibernate 使用的 SQL 方言。不同的数据库有不同的 SQL 语法,设置正确的方言可以确保 Hibernate 生成适合目标数据库的 SQL 语句。
  • <property name="hibernate.connection.driver_class">:指定 JDBC 驱动的类名。Hibernate 需要使用这个驱动程序来连接数据库。
  • <property name="hibernate.connection.url">:数据库的连接 URL,包含数据库的地址、端口和数据库名称。可以包含其他参数,例如字符编码和 SSL 配置。(注意上文提到的不同版本mysql之间url的区别)
  • <property name="hibernate.connection.username">:数据库的用户名,用于身份验证。
  • <property name="hibernate.connection.password">:数据库的密码,用于身份验证。
  • <property name="show_sql">:如果设置为 true,Hibernate 会在控制台输出所生成的 SQL 语句。这对于调试非常有帮助。
  • <property name="format_sql">:如果设置为 true,Hibernate 会格式化输出的 SQL 语句,使其更易读。
  • <property name="hbm2ddl.auto">:指定 Hibernate 如何处理数据库模式的创建和更新。常见的值包括:
  • create:每次启动应用程序时创建数据库。
  • update:每次启动应用程序时更新数据库(保留现有数据)。
  • validate:验证数据库与实体类的映射是否一致。
  • none:不进行任何操作。
  • <mapping resource="..."/>:指定 Hibernate 映射文件的位置,通常是 .hbm.xml 文件。这些文件定义了实体类与数据库表之间的映射关系。注意,传统的Hibernate配置文件中所使用XML文件来定义实体类与数据库表之间的关系。从Hibernate3.0开始,支持直接在实体类中添加注解的方式映射而不再需要xml映射文件,但这里需要使用可以使用 <mapping class="..."/> 来指定使用 Java 注解进行映射的实体类。对注解的使用褒贬不一,有人认为注解的使用使得配置非常零散,且耦合度高,不便于集中管理,而xml文件便于集中管理配置文件并且在某种程度上实现了解耦,读者朋友更看好那种方法呢?欢迎评论区留言讨论。当然如果使用Spring框架的话,可以使用SpringBoot整合Hibernate,这里点到为止.....

5.Hibernate映射文件(XXX.hbm.xml)解析

映射文件定义了实体类数据库表之间的映射关系:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.yu.hibernate.mapper.Customer" table="customers" catalog="springboot_db">
        <id name="cid" type="java.lang.String">
            <column name="customerID" length="20" />
            <generator class="assigned"></generator>
        </id>
        <property name="name" type="java.lang.String">
            <column name="name" length="20" not-null="true" />
        </property>
        <property name="phone" type="java.lang.String">
            <column name="phone" />
        </property>
    </class>
</hibernate-mapping>
  • 根元素 <hibernate-mapping>:这是映射文件的根元素,定义了文件的命名空间和版本信息。通常还会包含 XML Schema 的引用。
  • <class> 元素:该元素用于定义一个 Java 类与数据库表之间的映射关系。
    • name:指定要映射的 Java 类的全限定名。
    • table:指定要映射的数据库表的名称。
    • catalog:指定要映射的数据库名称
  • <id> 元素
    • 用于定义实体的主键属性。
    • name:指定主键属性的名称。
    • column:指定数据库表中对应的列名。
    • <generator>:定义主键生成策略,常见的生成策略有:
    • native:使用数据库自带的主键生成策略。
    • identity:使用自增长主键(在某些数据库中)。
    • sequence:使用序列生成主键(在支持序列的数据库中)。
  • <property> 元素:用于定义普通属性的映射。
    • name:指定要映射的属性名称。
    • column:指定数据库表中对应的列名。
  • <many-to-one> 元素
    • 用于定义多对一关系。
    • name:指定关联的属性名称。
    • class:指定关联的实体类。
    • column:指定数据库表中对应的外键列名。
  • <set>、<list>、<bag>、<map> 元素:用于定义一对多或多对多关系的集合映射。
    • name:指定集合属性的名称。
    • table:指定集合关联的数据库表(如果需要)。
    • cascade:定义级联操作的策略(如 all、save-update 等。)
  • <key>:指定集合的外键列名。
  • <one-to-many>:指定集合中元素的类。

测试类:

package test;
import com.yu.hibernate.mapper.Customer;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import java.util.List;

/**
 * ClassName: com.yu.hibernate.test.Htest
 *
 * @Author 雨
 * @Create 2024/9/14  10:24
 * @Version 1.0
 */

public class Htest {
    public static void main(String[] args) {
        Configuration cfg = new Configuration().configure("hibernate.cfg.xml");
        SessionFactory sessionFactory = cfg.buildSessionFactory();
        Session s = sessionFactory.openSession();
        Query q = s.createQuery("from Customer");
        List<Customer> list = q.list();
        for (Customer c : list) {
            System.out.println(c.getCid());
        }
    }
}

6. Hibernate实现多表联立查询的操作

在 Hibernate 中,多对多关系的映射可以通过中间表来实现。中间表包含两个外键,分别指向两个实体的主键。

① 相关标签

<hibernate-mapping>
    <class name="com.example.entity.Customer" table="customers">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>
        <property name="email" column="email"/>
        <many-to-one name="address" class="com.example.entity.Address" column="address_id"/>


        <set name="orders" table="orders" cascade="all">
            <key column="customer_id"/>
            <one-to-many class="com.example.entity.Order"/>
        </set>


    </class>
</hibernate-mapping>

<set> 元素:这个元素定义了与 Order 实体之间的一对多关系。

        <set name="orders" table="orders" cascade="all">:

  • name: 指定集合属性的名称,这里是 orders,表示一个客户可以有多个订单。
  • table: 指定订单集合所关联的数据库表名,这里是 orders。
  • cascade: 定义级联操作的策略,这里设置为 all,表示对该集合的操作(如保存、更新、删除)会级联到相关的 Order 实体。saveupdate表示级联增添和修改,但不级联删除。

<key> 元素:这个元素指定了集合的外键列名

        <key column="customer_id"/>:

  • customer_id 列在 orders 表中用作外键,指向 customers 表中的主键。

<one-to-many> 元素:

        <one-to-many class="com.example.entity.Order"/>:

  • 这个元素指定集合中元素的类。在这里,集合 orders 中的每个元素都是 Order 类型的实体

② 其他常用标签

除了上述元素外,Hibernate 映射文件还支持其他一些常用元素,下面是一些示例:

<bag>、<list>、<map> 元素

用于定义不同类型的集合映射。
<bag>: 定义一个无序集合,允许重复元素。
<list>: 定义一个有序集合,允许重复元素,并支持索引。
<map>: 定义一个键值对映射。

<composite-id> 元素

用于定义复合主键(即由多个字段组成的主键)。

<composite-id name="id" class="com.example.entity.OrderId">
    <key-property name="orderNumber" column="order_number"/>
    <key-property name="customerId" column="customer_id"/>
</composite-id>

<component> 元素

用于定义复合属性(即一个属性由多个字段组成)

<property name="address" column="address_id">
    <component class="com.example.entity.Address">
        <property name="street" column="street"/>
        <property name="city" column="city"/>
    </component>
</property>

③ 多表连接实例

以下是一个多对多关系的 Hibernate 映射文件实例

有两个实体:StudentCourse,一个学生可以选修多门课程,而一门课程可以被多个学生选修。

// Student.java
package com.example.entity;

import java.util.Set;

public class Student {
    private Long id;
    private String name;
    private Set<Course> courses; // 多对多关系

    // Getters and Setters
}

// Course.java
package com.example.entity;

import java.util.Set;

public class Course {
    private Long id;
    private String title;
    private Set<Student> students; // 多对多关系

    // Getters and Setters
}

创建两个映射文件:Student.hbm.xmlCourse.hbm.xml

Student.hbm.xml:

<hibernate-mapping xmlns="http://www.hibernate.org/xsd/hibernate-mapping"
                   xsi:schemaLocation="http://www.hibernate.org/xsd/hibernate-mapping 
                   http://www.hibernate.org/xsd/hibernate-mapping-3.0.xsd"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <class name="com.example.entity.Student" table="students">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        
        <property name="name" column="name"/>
        
        <set name="courses" table="student_courses" cascade="all">
            <key column="student_id"/>
            <many-to-many class="com.example.entity.Course" column="course_id"/>
        </set>
    </class>
</hibernate-mapping>

  • 在 Student 映射文件中,<set> 元素定义了学生和课程之间的多对多关系。
  • name="courses": 表示 Student 实体中的 courses 属性。
  • table="student_courses": 指定中间表的名称。
  • <key column="student_id"/>: 指定中间表中指向 Student 实体的外键列。
  • <many-to-many class="com.example.entity.Course" column="course_id"/>: 指定关联的实体类为 Course,并指定中间表中指向 Course 的外键列。

Course.hbm.xml:

<hibernate-mapping xmlns="http://www.hibernate.org/xsd/hibernate-mapping"
                   xsi:schemaLocation="http://www.hibernate.org/xsd/hibernate-mapping 
                   http://www.hibernate.org/xsd/hibernate-mapping-3.0.xsd"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <class name="com.example.entity.Course" table="courses">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        
        <property name="title" column="title"/>
        
        <set name="students" table="student_courses" cascade="all" lazy="true">
            <key column="course_id"/>
            <many-to-many class="com.example.entity.Student" column="student_id"/>
        </set>
    </class>
</hibernate-mapping>

  • 在 Course 映射文件中,<set> 元素定义课程和学生之间的多对多关系。
  • name="students": 表示 Course 实体中的 students 属性。
  • table="student_courses": 指定中间表的名称,与 Student 映射文件中的相同。
  • <key column="course_id"/>: 指定中间表中指向 Course 实体的外键列。
  • <many-to-many class="com.example.entity.Student" column="student_id"/>: 指定关联的实体类为 Student,并指定中间表中指向 Student 的外键列。

④ 级联操作相关名词解释

中间表:

  • student_courses 表用于存储学生和课程之间的关系,包含两个外键:student_id 和 course_id

映射文件结构:

  • 每个实体类都有一个对应的映射文件,使用 <class> 元素定义类与数据库表的映射。
  • 使用 <set> 元素来定义多对多关系,同时指定中间表及其外键。

级联操作:

  • 在映射中使用 cascade="all",表示对 Student 或 Course 实体的操作会级联到关联的实体。这意味着当你对一个学生进行操作时,相关的课程也会受到影响。saveupdate表示级联增添和修改,但不级联删除。

懒加载:

在每个 <set> 元素中,设置了 lazy="true",这表示在访问集合时才会加载相关的数据。懒加载可以提高性能,因为它避免了在初始查询时加载所有相关数据,特别是在关系比较复杂的情况下。

⑤ 示例(级联Demo)

保存学生和课程:

import org.hibernate.Session;
import org.hibernate.Transaction;

import java.util.HashSet;

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction transaction = session.beginTransaction();

        // 创建学生和课程
        Student student1 = new Student();
        student1.setName("Alice");

        Course course1 = new Course();
        course1.setTitle("Mathematics");

        // 设置多对多关系
        student1.getCourses().add(course1);
        course1.getStudents().add(student1);

        // 保存实体
        session.save(student1); // 这将级联保存 course1
        // 或者可以先保存 course1 然后再保存 student1,效果是一样的

        transaction.commit();
        session.close();
    }
}

更新学生及其课程

import org.hibernate.Session;
import org.hibernate.Transaction;

public class UpdateExample {
    public static void main(String[] args) {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction transaction = session.beginTransaction();

        // 查询学生
        Student student = session.get(Student.class, 1L); // 假设学生ID为1

        // 创建新课程并添加到学生的课程集合中
        Course course2 = new Course();
        course2.setTitle("Physics");
        
        student.getCourses().add(course2);
        course2.getStudents().add(student);

        // 更新学生
        session.update(student); // 更新学生及其课程

        transaction.commit();
        session.close();
    }
}

7. Hibernate框架执行SQL实现流程

Hibernate 的底层实现流程可以概括为以下几个步骤:

  1. 配置和初始化:读取配置文件,创建 SessionFactory
  2. 会话管理:打开 Session,开始事务。
  3. 持久化操作:执行添加、更新、删除和获取操作。
  4. 查询操作:通过 HQL 或 Criteria 执行查询。
  5. 事务提交:提交事务,执行所有 SQL 操作。
  6. 清理和关闭:关闭 Session 和 SessionFactory
  7. 延迟加载与缓存:利用延迟加载和二级缓存优化性能。Hibernate 默认支持延迟加载,这意味着关联的对象(如集合)在第一次访问时才会加载。Hibernate 提供了二级缓存机制,可以在 SessionFactory 级别缓存对象,以减少数据库访问。

8. Hibernate框架SQL生成原理

所有基于Java的所有持久层框架本质都是对JDBC操作的简化,不需要程序员写太多繁琐的JDBC代码,Hibernate也是一样,HQL 和 Criteria API最终都会通过反射机制,填充Java对象,生成相应SQL语句。编程界有句话非常好,送给大家:麻烦不能被消除,只能被转移。

  • HQL 和 Criteria API 在底层都依赖于 JDBC 进行数据库操作。
  • Hibernate 通过解析 HQL 或 Criteria 查询,生成相应的 SQL 语句,并使用 JDBC 执行。
  • 在结果映射过程中,Hibernate 使用反射来创建和填充 Java 对象。

这种设计使得 Hibernate 能够将对象关系映射过程抽象化,开发者可以专注于对象模型而不必直接处理 SQL 和 JDBC 代码。

大道至简,学到现在回头看所有学过的框架、中间件、设计模式,底层实现思想都有微妙的关联。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值