Hibernate学习记录
Hibernate 学习指南
1. Hibernate 核心概念与价值
1.1 什么是 Hibernate?
Hibernate 是一个全自动 ORM(对象-关系映射)框架,它负责 Java 对象与关系型数据库表之间的映射,允许开发者通过操作对象来实现数据库 CRUD,无需手动编写 SQL 语句。
它是 JPA(Java Persistence API)规范的主要实现者,同时提供了远超 JPA 规范的扩展功能。
1.2 为什么选择 Hibernate?
- 彻底屏蔽 SQL:通过对象操作自动生成 SQL,减少重复劳动
- 跨数据库兼容:支持 MySQL、Oracle、SQL Server 等,切换数据库无需修改代码
- 强大的关联映射:轻松处理复杂的对象关系(如订单-商品-用户的多对多关联)
- 内置缓存机制:一级缓存(Session 级别)、二级缓存(SessionFactory 级别)提升性能
- 与 Spring 生态无缝集成:在 Spring 项目中可通过
@Repository轻松整合
1.3 Hibernate 与其他 ORM 框架的区别
| 框架 | 特点对比 | 适用场景 |
|---|---|---|
| Hibernate | 全自动 ORM,无需写 SQL,学习曲线较陡 | 复杂关系、快速开发、跨库需求 |
| MyBatis | 半自动化 ORM,需手动写 SQL,灵活性高 | 需优化 SQL、互联网高并发场景 |
| Spring Data JPA | 基于 JPA 的封装,简化 CRUD 接口 | 快速开发简单业务 |
2. 环境搭建与第一个程序
2.1 核心依赖(Maven)
<!-- Hibernate核心包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.6.14.Final</version>
</dependency>
<!-- 数据库驱动(以MySQL为例) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- 日志框架(可选,用于调试) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
</dependencies>
2.2 核心配置文件(hibernate.cfg.xml)
放置在src/main/resources目录下,配置数据库连接和Hibernate基础属性:<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE hibernate-configuration PUBLIC
“-//Hibernate/Hibernate Configuration DTD 3.0//EN”
“http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- 方言:指定数据库类型(Hibernate根据方言生成对应SQL) -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property>
<!-- 可选配置 -->
<property name="hibernate.show_sql">true</property> <!-- 显示生成的SQL -->
<property name="hibernate.format_sql">true</property> <!-- 格式化SQL -->
<property name="hibernate.hbm2ddl.auto">update</property> <!-- 自动建表策略 -->
<!-- 注册实体类(包扫描或具体类) -->
<mapping class="com.example.entity.User"/>
</session-factory>
</hibernate-configuration>hbm2ddl.auto参数说明:
create:每次启动删除旧表,创建新表(测试用)create-drop:启动创建表,关闭时删除(测试用)update:根据实体类自动更新表结构(开发用)validate:校验表结构与实体类是否一致,不一致报错(生产用)
2.3 第一个 Hibernate 程序:保存用户
步骤 1:创建实体类(User.java)package com.example.entity;
1 | import javax.persistence.*; |
步骤 2:编写测试类 package com.example.test;
1 | import com.example.entity.User; |
执行结果
- 控制台输出自动生成的 SQL:
1
insert into t_user (create_time, username) values (?, ?)
- 数据库表
t_user中新增一条记录,id自动递增。
3. 实体映射基础(注解与 XML)
Hibernate 支持两种映射方式:注解映射(推荐)和XML 映射(传统方式),核心是定义”类 → 表”、”属性 → 字段”的对应关系。
3.1 常用注解详解
| 注解 | 作用 | 常用属性 |
|---|---|---|
@Entity |
标记类为 Hibernate 实体 | - |
@Table |
指定映射的表名 | name:表名;schema:数据库名 |
@Id |
标记属性为主键 | - |
@GeneratedValue |
主键生成策略 | strategy:生成策略(如 IDENTITY) |
@Column |
映射属性到表字段 | name:字段名;length:长度;nullable:是否允许为空 |
@Transient |
标记属性不映射到数据库(临时属性) | - |
@Temporal |
映射日期时间类型(JPA 规范) | value:TemporalType.DATE/TIME/TIMESTAMP |
主键生成策略(@GeneratedValue):
GenerationType.IDENTITY:依赖数据库自增(MySQL 的 AUTO_INCREMENT)GenerationType.SEQUENCE:使用数据库序列(Oracle 的 SEQUENCE)GenerationType.TABLE:通过中间表生成主键(跨数据库通用)GenerationType.AUTO:由 Hibernate 自动选择(默认)
3.2 XML 映射方式(备用方案)
当需要避免在实体类中混入注解时,可使用 XML 映射文件(如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">
<!-- 普通属性映射 -->
<property name="username" column="username" length="50" not-null="true"/>
<property name="createTime" column="create_time"/>
</class>
</hibernate-mapping>需在hibernate.cfg.xml中注册映射文件:
4. 核心 API 详解(Session、Transaction 等)
Hibernate 的核心操作围绕以下 API 展开,理解它们的生命周期和用法是关键。
4.1 SessionFactory
- 作用:创建 Session 对象,维护数据库连接信息和 Hibernate 配置
- 特点:重量级对象,线程安全,全局只需要一个实例
- 创建方式:
1
2
3SessionFactory sessionFactory = new Configuration()
.configure() // 默认加载hibernate.cfg.xml
.buildSessionFactory();
4.2 Session
- 作用:类似数据库连接,负责执行 CRUD 操作,管理实体对象的生命周期
- 特点:轻量级对象,线程不安全,每次操作需创建新实例
- 核心方法:
save(obj):保存对象(生成 INSERT)get(clazz, id):根据 ID 查询(立即加载)load(clazz, id):根据 ID 查询(懒加载,返回代理对象)update(obj):更新对象(生成 UPDATE)delete(obj):删除对象(生成 DELETE)saveOrUpdate(obj):保存或更新(根据 ID 是否为 null 自动判断)createQuery(hql):创建 HQL 查询对象
4.3 Transaction
- 作用:管理事务,保证操作的原子性
- 核心方法:
commit():提交事务rollback():回滚事务
- 使用规范:所有数据库操作必须在事务中执行(即使是查询,也建议开启事务以保证一致性)
4.4 实体对象的三种状态
Hibernate 中实体对象有三种状态,状态转换是理解缓存和持久化的关键:
瞬时态(Transient):
- 特点:未与 Session 关联,数据库中无对应记录,无主键 ID
- 例:
new User("张三")创建的对象 - 转换:调用
session.save()→ 持久态
持久态(Persistent):
- 特点:与 Session 关联,数据库中存在对应记录,有主键 ID
- 例:
session.get(User.class, 1L)查询到的对象 - 特性:对对象的修改会自动同步到数据库(脏检查机制)
脱管态(Detached):
- 特点:曾是持久态,但 Session 已关闭,数据库中存在对应记录
- 转换:
session.close()后持久态 → 脱管态;调用session.update()→ 重新变为持久态
5. 查询方式全解析(HQL、Criteria、SQL)
Hibernate 提供多种查询方式,满足不同场景需求。
5.1 HQL(Hibernate Query Language)
特点:面向对象的查询语言,语法类似 SQL,但操作的是实体类和属性(而非表和字段)。
基本查询// 查询所有用户
1 | Query<User> query = session.createQuery("from User", User.class); |
5.3 原生 SQL 查询
特点:直接执行原生 SQL 语句,适合复杂查询场景(如多表关联、数据库特有函数)。
1 | // 原生SQL查询 |
6. 关联映射(一对一、一对多、多对多)
关联映射是 Hibernate 的核心优势,用于处理对象之间的关系(对应数据库的外键关联)。
6.1 一对多关联(最常用)
场景:一个用户(User)有多个订单(Order),一个订单属于一个用户。
步骤 1:定义实体类// User.java(一的一方)
1 |
|
关键属性说明:
mappedBy = "user":指定关联关系由 Order 类的 user 属性维护(外键在 t_order 表)cascade = CascadeType.ALL:级联操作(保存用户时自动保存订单,删除用户时自动删除订单)orphanRemoval = true:当订单从用户的 orders 列表中移除时,自动删除该订单fetch = FetchType.LAZY:懒加载(查询订单时不加载用户,访问 user 属性时才查询)
步骤 2:测试一对多关联// 保存用户及关联的订单
1 | Session session = sessionFactory.openSession(); |
6.2 一对一关联
1 | **场景**:一个用户(User)对应一个用户详情(UserDetail)。 |
6.3 多对多关联
1 | **场景**:一个学生(Student)可以选多门课程(Course),一门课程有多个学生。 |
7. 缓存机制(一级、二级、查询缓存)
Hibernate 的缓存机制是提升性能的关键,减少数据库访问次数。
7.1 一级缓存(Session 缓存)
- 作用范围:当前 Session(会话)级别,生命周期与 Session 一致
- 特点:默认开启,无法关闭,线程不安全
- 工作原理:Session 会缓存查询到的持久态对象,同一 Session 内再次查询相同 ID 的对象时,直接从缓存获取,不查询数据库
1 | Session session = sessionFactory.openSession(); |
7.2 二级缓存(SessionFactory 缓存)
- 作用范围:整个应用级别,所有 Session 共享
- 特点:默认关闭,需手动配置,线程安全
- 适用场景:查询频繁、修改少的数据(如字典表、分类表)
步骤 1:添加二级缓存依赖(以 Ehcache 为例)
1 | <groupId>org.hibernate</groupId> |
步骤 2:配置二级缓存
在hibernate.cfg.xml中添加:
步骤 3:标记实体类使用二级缓存@Entity
1 | // 缓存策略 |
缓存策略(CacheConcurrencyStrategy):
READ_ONLY:只读(适合不会修改的数据)READ_WRITE:读写(支持修改,保证一致性)NONSTRICT_READ_WRITE:非严格读写(可能出现脏读,性能高)TRANSACTIONAL:事务型(分布式环境使用)
7.3 查询缓存
- 作用:缓存 HQL/Criteria 查询的结果集
- 特点:依赖二级缓存,需手动开启并指定查询使用缓存
// 开启查询缓存
Queryquery = session.createQuery(“from User”, User.class);
query.setCacheable(true); // 此查询结果会被缓存
// 第一次查询:从数据库加载,结果放入查询缓存
List
// 第二次查询:从查询缓存获取
List
8. 事务管理与并发控制
8.1 事务 ACID 特性
Hibernate 事务严格遵循 ACID 特性:
- 原子性(Atomicity):事务中的操作要么全成,要么全败
- 一致性(Consistency):事务执行前后数据状态一致
- 隔离性(Isolation):多个事务并发执行时互不干扰
- 持久性(Durability):事务提交后数据永久保存
8.2 事务隔离级别
在hibernate.cfg.xml中配置:
- 1:
READ_UNCOMMITTED(读未提交,可能脏读) - 2:
READ_COMMITTED(读已提交,避免脏读) - 4:
REPEATABLE_READ(可重复读,避免脏读、不可重复读) - 8:
SERIALIZABLE(串行化,避免所有并发问题,性能低)
8.3 并发控制:乐观锁与悲观锁
乐观锁(推荐)
通过版本号控制并发,适合读多写少场景:@Entity
1 | public class Product { |
- 原理:更新时会检查版本号,
update ... where id=? and version=? - 冲突时:抛出
OptimisticLockingFailureException,需重试
悲观锁
通过数据库锁机制控制,适合写多读少场景:// 使用 HQL 加悲观锁
1 | Query<User> query = session.createQuery("from User where id=:id", User.class); |
9. 高级特性(批量操作、拦截器、事件)
9.1 批量操作(提高大量数据处理效率)
默认情况下,Hibernate 会逐条处理数据,批量操作可优化性能:
批量插入 Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for (int i = 0; i < 1000; i++) {
User user = new User(“用户” + i);
session.save(user);
// 每50条刷新一次并清理缓存(避免内存溢出)
if (i % 50 == 0) {
session.flush(); // 刷入数据库
session.clear(); // 清理一级缓存
}
}
tx.commit();
session.close();
需在hibernate.cfg.xml中添加批量配置:
9.2 拦截器(Interceptor)
用于在实体对象生命周期的特定阶段(如保存、更新前)插入自定义逻辑:
public class MyInterceptor extends EmptyInterceptor {
// 保存前拦截
@Override
public boolean onSave(Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {
if (entity instanceof User) {
// 自动设置创建时间
for (int i = 0; i < propertyNames.length; i++) {
if (“createTime”.equals(propertyNames[i])) {
state[i] = LocalDateTime.now();
return true; // 通知 Hibernate 状态已修改
}
}
}
return false;
}
}
注册拦截器:SessionFactory sessionFactory = new Configuration()
.configure()
.setInterceptor(new MyInterceptor()) // 全局拦截器
.buildSessionFactory();
9.3 事件监听(Event Listener)
类似拦截器,更灵活的生命周期事件处理:
public class UserSaveListener implements PreInsertEventListener {
@Override
public boolean onPreInsert(PreInsertEvent event) {
if (event.getEntity() instanceof User) {
// 设置创建时间
event.getState()[event.getPersister()
.getPropertyIndex(“createTime”)] = LocalDateTime.now();
}
return false; // 不阻止事件继续执行
}
}
注册监听器(在hibernate.cfg.xml中):
</event>
10. 性能优化实践
10.1 减少数据库访问
- 合理使用缓存(一级缓存默认开启,二级缓存用于高频查询数据)
- 批量操作替代逐条操作(设置
hibernate.jdbc.batch_size) - 分页查询避免一次性加载大量数据(
setFirstResult+setMaxResults)
10.2 优化关联查询
- 优先使用懒加载(
FetchType.LAZY),避免不必要的关联数据加载 - 复杂关联查询使用
fetch join(HQL)一次性加载所需数据,避免 N+1 查询问题:1
2
3
4
5// 一次性加载用户及其订单(避免N+1查询)
List<User> users = session.createQuery(
"from User u left join fetch u.orders where u.id = :id", User.class)
.setParameter("id", 1L)
.getResultList();
10.3 其他优化建议
- 关闭
hibernate.hbm2ddl.auto(生产环境),手动管理表结构 - 使用
DTO模式传输数据,避免返回完整实体类 - 避免在循环中执行数据库操作
- 合理设置
Session生命周期(Web 项目中通常与请求生命周期一致)
11. 实战案例:完整 CRUD 应用
11.1 项目结构 src/main/java
├── com.example
│ ├── entity
│ │ └── User.java
│ ├── dao
│ │ ├── UserDao.java
│ │ └── UserDaoImpl.java
│ ├── service
│ │ ├── UserService.java
│ │ └── UserServiceImpl.java
│ └── util
│ └── HibernateUtil.java
src/main/resources
└── hibernate.cfg.xml
11.2 核心代码实现
Hibernate 工具类(HibernateUtil.java)public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// 初始化SessionFactory
sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
getSessionFactory().close();
}
}
DAO 层(UserDaoImpl.java)public class UserDaoImpl implements UserDao {
@Override
public void save(User user) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.save(user);
tx.commit();
} catch (Exception e) {
if (tx != null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
@Override
public User getById(Long id) {
Session session = HibernateUtil.getSessionFactory().openSession();
try {
return session.get(User.class, id);
} finally {
session.close();
}
}
// 其他CRUD方法省略...
}
Service 层与测试(略)
12. 常见问题与解决方案
12.1 N+1 查询问题
现象:查询 N 个主对象时,每个主对象都触发一次关联对象查询,共执行 N+1 次 SQL。
解决:使用fetch join一次性加载关联数据:from User u left join fetch u.orders where u.id in (:ids)
12.2 懒加载异常(LazyInitializationException)
现象:Session 关闭后访问懒加载的关联属性(如order.getUser())。
解决:
- 方法 1:在 Session 关闭前初始化关联对象(
Hibernate.initialize(order.getUser())) - 方法 2:使用
fetch = FetchType.EAGER(不推荐,可能加载过多数据) - 方法 3:延长 Session 生命周期(如 Web 项目中使用 Open Session In View 模式)
12.3 事务提交后数据未更新
原因:实体对象处于脱管态(Session 已关闭),修改后未调用update()。
解决:重新关联 Session 并更新:Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = session.merge(detachedUser); // 合并脱管对象到 Session
tx.commit();
session.close();
12.4 缓存与数据库数据不一致
原因:二级缓存未及时更新。
解决:
- 修改数据后手动清理缓存:
sessionFactory.getCache().evict(User.class, id) - 使用合适的缓存策略(如
READ_WRITE)