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中必须使用”&“取代"&"符号,否则该文件不会被解析为合法的XML文件导致运行错误!小编就是在这里踩了坑。。(T_T)
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/springboot_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&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&useSSL=false&serverTimezone=UTC&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 映射文件实例
有两个实体:Student
和 Course
,一个学生可以选修多门课程,而一门课程可以被多个学生选修。
// 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.xml
和 Course.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 的底层实现流程可以概括为以下几个步骤:
- 配置和初始化:读取配置文件,创建
SessionFactory
。 - 会话管理:打开
Session
,开始事务。 - 持久化操作:执行添加、更新、删除和获取操作。
- 查询操作:通过 HQL 或 Criteria 执行查询。
- 事务提交:提交事务,执行所有 SQL 操作。
- 清理和关闭:关闭
Session
和SessionFactory
。 - 延迟加载与缓存:利用延迟加载和二级缓存优化性能。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 代码。
大道至简,学到现在回头看所有学过的框架、中间件、设计模式,底层实现思想都有微妙的关联。