Article

数据访问与切片 Hibernate ORM

更新于:2026-07-14

第一章:Hibernate 框架概述

1.1 什么是 ORM

概念名称说明注意事项
ORM(Object-Relational Mapping)对象关系映射,是一种将关系型数据库中的表结构映射为 Java 对象的技术。通过 ORM,开发者可以使用面向对象的方式操作数据库,无需编写原生 SQL 语句。ORM 并非完全屏蔽 SQL,复杂查询仍可能需要手动优化或使用原生 SQL。
实体类(Entity)对应数据库中的一张表,类的每个实例对应表中的一行记录。类名通常与表名对应,属性与字段对应。
属性映射将类的字段(Field)或属性(Property)映射到数据库表的列(Column)。支持基本类型、包装类型、日期、大对象等类型映射。
主键映射标识实体类中唯一标识记录的属性,对应数据库表的主键列。必须配置主键,否则无法进行持久化操作。

1.2 Hibernate 核心架构简介

组件名称说明注意事项
Hibernate API提供核心接口如 Session、SessionFactory、Transaction 等,用于与数据库交互。开发者主要通过这些接口进行数据操作。
JDBC / JTAHibernate 底层依赖 JDBC 进行数据库连接,也可通过 JTA 支持分布式事务。在配置文件中指定连接方式和事务策略。
Dialect(方言)数据库方言,用于生成针对特定数据库的 SQL 语句。必须根据使用的数据库选择正确的方言,如 MySQLDialect、OracleDialect。
Connection Pool连接池(如 C3P0、HikariCP)管理数据库连接,提升性能。建议生产环境使用第三方连接池。
Mapping Metadata映射元数据,定义实体类与数据库表之间的映射关系,可通过 XML 或注解配置。元数据是 Hibernate 实现 ORM 的核心依据。

1.3 Hibernate 与 JDBC 的对比

对比项JDBCHibernate注意事项
SQL 编写需手动编写所有 SQL 语句大部分操作无需手写 SQL,由框架自动生成Hibernate 仍支持原生 SQL 查询
结果集处理需手动处理 ResultSet,逐行映射为对象自动将结果映射为实体对象减少样板代码
数据库移植性SQL 语句依赖具体数据库,移植性差通过方言(Dialect)自动适配不同数据库更易实现跨数据库支持
对象关系管理无内置机制管理对象状态提供完整的对象生命周期管理(瞬时、持久、脱管)简化持久化逻辑
开发效率较低,需处理大量底层细节较高,专注于业务逻辑适合中大型项目
性能控制完全可控,可精细优化 SQL自动生成 SQL,复杂场景需调优需注意 N+1 查询等问题

1.4 Hibernate 的核心组件与工作流程

组件/流程说明注意事项
Configuration读取 hibernate.cfg.xmlhibernate.properties 配置文件,构建 SessionFactory是启动 Hibernate 的第一步
SessionFactory线程安全的工厂对象,用于创建 Session 实例,一个应用通常只有一个实例应全局唯一,避免频繁创建销毁
Session单线程对象,代表与数据库的一次会话,用于执行 CRUD 操作非线程安全,使用后应及时关闭
Transaction事务管理对象,控制数据库事务的提交与回滚推荐显式管理事务,尤其是在增删改操作中
工作流程1. 加载配置 → 2. 创建 SessionFactory → 3. 打开 Session → 4. 开启 Transaction → 5. 执行数据库操作 → 6. 提交事务 → 7. 关闭 Session典型的”会话-事务”编程模型

第二章:开发环境搭建与第一个 Hibernate 程序

2.1 所需依赖与 JAR 包说明

依赖名称/作用说明注意事项
hibernate-coreHibernate 核心库,包含所有核心 API 和功能必需
hibernate-c3p0(可选)集成 C3P0 连接池生产环境推荐使用连接池
mysql-connector-javaMySQL 数据库驱动根据实际数据库选择对应驱动
javassist 或 byte-buddy动态代理和字节码操作库,用于延迟加载等功能通常由 hibernate-core 传递依赖引入
slf4j-api + 日志实现(如 logback)日志接口与实现,用于输出 Hibernate 执行日志推荐配置日志以便调试
jboss-loggingHibernate 使用的日志门面一般自动引入

示例(Maven 依赖片段):

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.6.15.Final</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
</dependency>

2.2 配置文件 hibernate.cfg.xml 详解

配置项语法用途代码示例注意事项
dialect<property name="hibernate.dialect">指定数据库方言,用于生成适配的 SQLorg.hibernate.dialect.MySQL8Dialect必须正确匹配数据库版本
connection.driver_class<property name="hibernate.connection.driver_class">数据库驱动类com.mysql.cj.jdbc.Driver驱动类名随 JDBC 版本变化
connection.url<property name="hibernate.connection.url">数据库连接 URLjdbc:mysql://localhost:3306/testdb确保数据库已启动且可访问
connection.username<property name="hibernate.connection.username">数据库用户名root建议使用配置文件或环境变量管理敏感信息
connection.password<property name="hibernate.connection.password">数据库密码password同上
hbm2ddl.auto<property name="hibernate.hbm2ddl.auto">自动建表策略update值可为 createcreate-dropupdatevalidate;生产环境建议使用 validate 或手动管理 DDL
show_sql<property name="hibernate.show_sql">是否在控制台输出 SQL 语句true调试时开启,生产环境关闭
format_sql<property name="hibernate.format_sql">是否格式化输出的 SQLtrue提高 SQL 可读性
use_sql_comments<property name="hibernate.use_sql_comments">是否在 SQL 中添加注释true有助于调试和理解生成的 SQL
mapping resource<mapping resource="..."/>注册实体映射文件(.hbm.xml每个实体映射文件需单独注册

2.3 实体类与映射文件(.hbm.xml)编写

概念/元素说明注意事项
实体类(POJO)普通 Java 类,包含属性、getter/setter、无参构造函数建议实现 Serializable 接口
<class> 标签定义一个实体类与数据库表的映射,name 属性为类全名,table 为表名
<id> 标签映射主键属性必须存在,column 可省略(默认同属性名)
<generator> 子标签定义主键生成策略class 属性指定策略,如 nativeuuidassigned
<property> 标签映射普通属性,name 为属性名,column 为列名,type 可选(自动推断)
主键生成策略(generator)常见值:native(自增)、uuidassigned(手动赋值)、increment根据数据库和业务需求选择

示例:User.hbm.xml

<hibernate-mapping>
    <class name="com.example.User" table="t_user">
        <id name="id" column="user_id">
            <generator class="native"/>
        </id>
        <property name="name" column="user_name" length="50"/>
        <property name="email" column="email" not-null="true"/>
    </class>
</hibernate-mapping>

2.4 编写第一个 Hibernate 应用:保存实体对象

步骤说明代码示例注意事项
1. 创建 Configuration加载配置文件Configuration cfg = new Configuration().configure();configure() 默认加载 classpath 下的 hibernate.cfg.xml
2. 构建 SessionFactory通过 Configuration 构建SessionFactory sf = cfg.buildSessionFactory();应使用单例模式管理
3. 打开 Session获取数据库会话Session session = sf.openSession();每次操作使用新 Session,用后关闭
4. 开启事务开始事务Transaction tx = session.beginTransaction();写操作必须在事务中
5. 创建并保存实体构造对象并保存User user = new User(); user.setName("张三"); user.setEmail("zhangsan@example.com"); session.save(user);save() 返回主键值(可选接收)
6. 提交事务提交更改tx.commit();必须显式提交,否则不生效
7. 关闭 Session释放资源session.close();防止内存泄漏

完整代码示例:

Session session = sf.openSession();
Transaction tx = null;
try {
    tx = session.beginTransaction();
    User user = new User();
    user.setName("李四");
    user.setEmail("lisi@example.com");
    session.save(user);
    tx.commit();
} catch (Exception e) {
    if (tx != null) tx.rollback();
    e.printStackTrace();
} finally {
    session.close();
}

2.5 SessionFactory 与 Session 的基本使用

方法名语法用途代码示例注意事项
buildSessionFactory()configuration.buildSessionFactory()从 Configuration 构建 SessionFactorySessionFactory sf = cfg.buildSessionFactory();重量级对象,应用启动时创建一次
openSession()sessionFactory.openSession()打开一个新的 SessionSession session = sf.openSession();每次调用返回新实例,需手动管理关闭
getCurrentSession()sessionFactory.getCurrentSession()获取当前线程绑定的 SessionSession session = sf.getCurrentSession();需在配置中启用 hibernate.current_session_context_class=thread
close()(SessionFactory)sessionFactory.close()关闭 SessionFactory,释放资源sf.close();应用关闭时调用
save()session.save(entity)保存实体,返回主键Long id = (Long) session.save(user);瞬时对象变为持久化状态
get()session.get(Class, id)根据主键立即加载对象User user = session.get(User.class, 1L);立即发送 SQL 查询,查不到返回 null
load()session.load(Class, id)根据主键返回代理对象(延迟加载)User user = session.load(User.class, 1L);返回代理,真正使用时才查询,查不到抛异常
update()session.update(entity)更新脱管状态的对象session.update(user);对象必须存在主键
delete()session.delete(entity)删除实体session.delete(user);支持传入实体或 HQL
close()(Session)session.close()关闭 Session,释放连接session.close();必须调用,避免资源泄漏
isOpen()session.isOpen()检查 Session 是否打开if (session.isOpen()) { ... }用于状态判断
getTransaction()session.getTransaction()获取当前事务对象Transaction tx = session.getTransaction();用于事务控制

第三章:核心接口与生命周期管理

3.1 Configuration 接口

方法名语法用途代码示例注意事项
configure()new Configuration().configure()加载默认配置文件 hibernate.cfg.xmlConfiguration cfg = new Configuration().configure();默认加载 classpath 根路径下的 hibernate.cfg.xml
configure(String)new Configuration().configure(String configFileName)加载指定名称的配置文件Configuration cfg = new Configuration().configure("hibernate-test.cfg.xml");文件需在 classpath 中
addResource()configuration.addResource(String resourceName)手动添加 .hbm.xml 映射文件cfg.addResource("com/example/User.hbm.xml");适用于未在 cfg.xml 中注册的映射文件
setProperty()configuration.setProperty(String key, String value)动态设置配置属性cfg.setProperty("hibernate.show_sql", "true");优先级高于配置文件中的设置
buildSessionFactory()configuration.buildSessionFactory()构建 SessionFactory 实例SessionFactory sf = cfg.buildSessionFactory();触发元数据解析和连接池初始化

3.2 SessionFactory 接口

方法名语法用途代码示例注意事项
openSession()sessionFactory.openSession()打开一个新 SessionSession session = sf.openSession();返回的 Session 不属于任何事务上下文,需手动管理事务
getCurrentSession()sessionFactory.getCurrentSession()获取当前线程绑定的 SessionSession session = sf.getCurrentSession();需配置 hibernate.current_session_context_class=thread,事务提交后自动关闭
close()sessionFactory.close()关闭 SessionFactory,释放所有资源sf.close();应用关闭时调用,避免资源泄漏
isOpen()sessionFactory.isOpen()检查 SessionFactory 是否处于打开状态if (sf.isOpen()) { ... }关闭后不可再使用
getStatistics()sessionFactory.getStatistics()获取会话工厂的运行时统计信息Statistics stats = sf.getStatistics();用于性能监控和调试

3.3 Session 接口

方法名语法用途代码示例注意事项
save()session.save(entity)保存瞬时对象,返回主键Long id = (Long) session.save(user);实体进入持久化状态,事务提交后写入数据库
persist()session.persist(entity)持久化对象,不保证立即返回主键session.persist(user);JPA 标准方法,与 save 行为略有不同
get()session.get(Class, id)立即加载对象,查不到返回 nullUser user = session.get(User.class, 1L);立即执行 SQL 查询,可能返回 null
load()session.load(Class, id)延迟加载对象,返回代理User user = session.load(User.class, 1L);仅当访问属性时才查询,查不到抛 ObjectNotFoundException
update()session.update(entity)更新脱管状态的对象session.update(user);对象必须存在主键,且数据库中存在对应记录
saveOrUpdate()session.saveOrUpdate(entity)根据主键判断执行 save 或 updatesession.saveOrUpdate(user);若主键为 null 执行 save,否则执行 update
merge()session.merge(entity)将脱管对象状态合并到持久化对象User merged = session.merge(user);返回新的持久化实例,原对象仍为脱管
delete()session.delete(entity)删除持久化或脱管对象session.delete(user);支持传入实体或 HQL 条件
flush()session.flush()强制同步持久化上下文到数据库session.flush();触发 SQL 执行,但不提交事务
clear()session.clear()清除 Session 缓存中所有对象session.clear();所有持久化对象变为脱管状态
evict()session.evict(entity)从缓存中移除指定对象session.evict(user);该对象变为脱管状态
contains()session.contains(entity)判断对象是否在当前 Session 缓存中boolean isIn = session.contains(user);用于判断对象的持久化状态
close()session.close()关闭 Session,释放连接session.close();必须调用,否则连接不释放

3.4 Transaction 接口

方法名语法用途代码示例注意事项
begin()transaction.begin()开始事务tx.begin();通常由 session.beginTransaction() 返回
commit()transaction.commit()提交事务tx.commit();成功完成操作后调用
rollback()transaction.rollback()回滚事务tx.rollback();异常时调用,撤销所有更改
isActive()transaction.isActive()检查事务是否处于活动状态if (tx.isActive()) { ... }提交或回滚后返回 false
setRollbackOnly()transaction.setRollbackOnly()标记事务为只回滚tx.setRollbackOnly();用于嵌套事务或跨操作回滚控制
getRollbackOnly()transaction.getRollbackOnly()检查事务是否被标记为回滚boolean only = tx.getRollbackOnly();配合 setRollbackOnly 使用

3.5 对象的持久化生命周期(瞬时、持久、脱管、删除)

状态名称说明注意事项
瞬时状态(Transient)对象未与 Session 关联,无主键值或主键为 null此时对对象的修改不会影响数据库
持久状态(Persistent)对象与 Session 关联,存在于数据库中(可能未提交)在 Session 范围内,任何属性修改在 flush 或 commit 时自动同步到数据库
脱管状态(Detached)对象曾与 Session 关联,但 Session 已关闭需通过 update()saveOrUpdate()merge() 重新关联
删除状态(Removed)对象已被 delete() 方法标记删除,但尚未提交在事务提交前仍可回滚,提交后从数据库移除

状态转换规则:

  • 瞬时 → 持久:save()persist()saveOrUpdate()
  • 持久 → 瞬时:close() Session 后对象未被引用
  • 持久 → 脱管:evict()clear()close() Session
  • 脱管 → 持久:update()saveOrUpdate()merge()
  • 任意 → 删除:delete()

第四章:映射基础

4.1 基本类型属性映射

Java 类型对应数据库类型(常见)映射方式(.hbm.xml)注解方式注意事项
StringVARCHAR / TEXT<property name="name" type="string"/>@Column(name = "name")默认映射为 VARCHAR(255)
Integer / intINTEGER<property name="age" type="integer"/>@Column(name = "age")包装类型可为 null
Long / longBIGINT<property name="id" type="long"/>@Column(name = "user_id")常用于主键
Boolean / booleanBIT / TINYINT(1)<property name="isActive" type="boolean"/>@Column(name = "is_active")MySQL 中常用 TINYINT(1) 表示
Date / java.util.DateDATETIME / TIMESTAMP<property name="createTime" type="timestamp"/>@Temporal(TemporalType.TIMESTAMP)需指定 TemporalType
java.sql.DateDATE<property name="birthDate" type="date"/>@Temporal(TemporalType.DATE)只包含日期部分
float / FloatFLOAT<property name="salary" type="float"/>@Column(name = "salary")精度较低
double / DoubleDOUBLE<property name="price" type="double"/>@Column(name = "price")默认浮点类型
BigDecimalDECIMAL / NUMERIC<property name="amount" type="big_decimal"/>@Column(precision=10, scale=2)高精度数值,适合金额

注意: type 属性可省略,Hibernate 可自动推断;注解方式中 @Temporal 用于日期类型。

4.2 主键映射策略(@Id 与 generator)

生成策略(generator)说明.hbm.xml 配置注解配置注意事项
assigned主键由程序手动赋值<generator class="assigned"/>@GeneratedValue(strategy = GenerationType.IDENTITY)需在保存前设置主键值
native根据数据库选择 identity 或 sequence<generator class="native"/>@GeneratedValue(strategy = GenerationType.AUTO)通用策略,推荐使用
identity主键由数据库自增(如 MySQL AUTO_INCREMENT)<generator class="identity"/>@GeneratedValue(strategy = GenerationType.IDENTITY)仅支持支持自增的数据库
sequence使用数据库序列(如 Oracle)<generator class="sequence"><param name="sequence">seq_name</param></generator>@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_gen") @SequenceGenerator(name = "seq_gen", sequenceName = "seq_name")Oracle 推荐方式
uuid生成 128 位 UUID 字符串<generator class="uuid"/>@GeneratedValue(strategy = GenerationType.UUID)全局唯一,适合分布式系统
incrementHibernate 内部递增(不推荐)<generator class="increment"/>不常用多实例环境下不安全

4.3 单表实体映射实践

映射元素说明代码示例(.hbm.xml)代码示例(注解)注意事项
<class> / @Entity实体类与表映射<class name="User" table="t_user">@Entity @Table(name = "t_user")必须定义
<id> / @Id主键映射<id name="id"><generator class="native"/></id>@Id @GeneratedValue必须存在
<property> / @Column普通属性映射<property name="name" column="user_name"/>@Column(name = "user_name")可省略 column 默认同名
length字段长度<property name="name" length="50"/>@Column(length = 50)用于字符串类型
not-null非空约束<property name="email" not-null="true"/>@Column(nullable = false)生成 DDL 时生效
unique唯一约束<property name="email" unique="true"/>@Column(unique = true)用于唯一索引

实践建议: 使用注解更简洁,避免 XML 配置冗余。

4.4 属性访问策略(字段 vs 属性)

访问策略说明配置方式代码示例注意事项
字段访问(field)直接访问类的字段(成员变量)默认策略,或使用 @Access(AccessType.FIELD)private String name; // 直接访问无需 getter/setter,但破坏封装
属性访问(property)通过 getter/setter 方法访问使用 @Access(AccessType.PROPERTY)private String name; public String getName(){...}符合 JavaBean 规范,推荐使用
混合访问部分字段、部分属性在不同属性上分别标注@Access(AccessType.FIELD) private Long id; @Access(AccessType.PROPERTY) private String getName()复杂场景使用,需谨慎

注意: 若使用属性访问,必须提供符合规范的 getter/setter;Hibernate 通过反射调用。


第五章:关联映射

5.1 一对一关联映射(单向与双向)

映射方式说明.hbm.xml 配置注解配置注意事项
单向一对一(基于外键)主表持有从表的外键,从表无引用<many-to-one unique="true"/>@OneToOne @JoinColumn(name = "profile_id")外键字段加唯一约束
单向一对一(主键关联)从表主键同时作为外键引用主表主键<generator class="foreign"/>@OneToOne @PrimaryKeyJoinColumn两表主键值相同
双向一对一双方互相持有对方引用主方:<one-to-one name="profile" cascade="all"/> 从方:<one-to-one name="user" constrained="true"/>主方:@OneToOne(mappedBy = "...") 从方:@OneToOnemappedBy 指定关系维护端
mappedBy 属性指定由哪一方维护外键不适用(XML 中通过 key 指定)@OneToOne(mappedBy = "user")被维护方必须使用 mappedBy

示例(注解):

// User.java
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "profile_id")
private Profile profile;

// Profile.java
@OneToOne(mappedBy = "profile")
private User user;

5.2 一对多与多对一关联映射

映射方式说明.hbm.xml 配置注解配置注意事项
单向一对多一的一方持有集合引用多的一方<set name="orders"><key column="user_id"/><one-to-many class="Order"/></set>@OneToMany @JoinColumn(name = "user_id")外键在”多”表中
双向一对多双方互持引用,“多”方维护外键一的一方:<set name="orders" inverse="true"><key column="user_id"/><one-to-many class="Order"/></set> 多的一方:<many-to-one name="user" column="user_id"/>一的一方:@OneToMany(mappedBy = "user") 多的一方:@ManyToOnemappedBy@OneToMany 端指定
@JoinColumn指定外键列名<key column="user_id"/>@JoinColumn(name = "user_id")默认命名规则为 关联类名_主键
cascade级联操作cascade="all"@OneToMany(cascade = CascadeType.ALL)常用于级联保存/删除
fetch type加载策略lazy="true" / lazy="false"@OneToMany(fetch = FetchType.LAZY)推荐一对多使用 LAZY

示例(双向一对多):

// User.java
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<Order> orders = new HashSet<>();

// Order.java
@ManyToOne
@JoinColumn(name = "user_id")
private User user;

5.3 多对多关联映射

映射方式说明.hbm.xml 配置注解配置注意事项
单向多对多一方持有集合引用另一方<set name="roles" table="user_role"><key column="user_id"/><many-to-many class="Role" column="role_id"/></set>@ManyToMany @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))必须指定中间表
双向多对多双方互持集合引用同上,反向也配置一端:@ManyToMany(mappedBy = "users") 另一端:@ManyToManymappedBy 指定非维护端
@JoinTable指定中间表信息使用 <set table="..."/><key/> / <many-to-many/>@JoinTable(name = "middle_table", joinColumns = ..., inverseJoinColumns = ...)joinColumns 是本方外键,inverseJoinColumns 是对方外键
中间表主键通常为联合主键(user_id, role_id)自动由外键构成无需额外配置建议添加索引提升查询性能

示例(注解):

// User.java
@ManyToMany
@JoinTable(name = "user_role",
    joinColumns = @JoinColumn(name = "user_id"),
    inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();

5.4 级联操作(cascade)配置

级联类型说明注解值代码示例注意事项
save-update级联保存或更新CascadeType.SAVE_UPDATEcascade = {CascadeType.SAVE_UPDATE}当父对象 save/update 时,子对象也执行相同操作
persist级联持久化CascadeType.PERSISTcascade = CascadeType.PERSISTJPA 标准,仅 save 时触发
merge级联合并CascadeType.MERGEcascade = CascadeType.MERGE将脱管对象状态合并到持久化对象
delete级联删除CascadeType.DELETEcascade = CascadeType.DELETE删除父对象时删除所有子对象
delete-orphan删除孤儿记录CascadeType.DELETE_ORPHANcascade = CascadeType.DELETE_ORPHAN子对象从集合移除后自动删除(仅集合关系)
all包含所有级联操作CascadeType.ALLcascade = CascadeType.ALL常用于强聚合关系

注意: 级联操作应谨慎使用,避免误删数据;DELETE_ORPHAN 仅适用于 @OneToMany@ManyToMany

5.5 懒加载与急加载(fetch type)

加载策略说明注解配置.hbm.xml 配置注意事项
FetchType.LAZY懒加载,访问时才查询@OneToMany(fetch = FetchType.LAZY)lazy="true"默认策略,避免 N+1 查询问题
FetchType.EAGER急加载,立即关联查询@ManyToOne(fetch = FetchType.EAGER)lazy="false"可能导致性能问题,慎用
一对一默认策略默认 EAGER@OneToOne 默认 EAGER无显式 lazy 属性时为 eager可能造成过度加载
一对多/多对多默认策略默认 LAZY@OneToMany, @ManyToMany 默认 LAZYlazy="true"推荐保持懒加载
多对一默认策略默认 EAGER@ManyToOne 默认 EAGERmany-to-one 默认立即加载建议改为 LAZY 优化性能

注意事项:

  • 懒加载需保证 Session 未关闭,否则抛 LazyInitializationException
  • 可通过 JOIN FETCH 在 HQL 中显式预加载关联数据。
  • 急加载适用于数据量小、必用的关联。

第六章:Hibernate 查询语言(HQL)

6.1 HQL 基本语法与特点

特性说明注意事项
面向对象操作实体类和属性,而非数据库表和字段from User 等价于 select * from t_user
大小写敏感实体类名和属性名区分大小写Useruser 不同
别名使用可使用 as 或空格定义别名from User as ufrom User u
支持继承可查询父类,返回所有子类实例多态查询
不支持 INSERTHQL 不支持 INSERT INTO ... SELECT ... 形式但支持 INSERT INTO ... VALUES ...

6.2 SELECT 查询

方法/语法用途代码示例注意事项
select *查询所有字段from User返回 Object[] 或实体对象
select 属性查询指定属性select u.name, u.email from User u返回 Object[] 数组
select new 构造器投影到 DTOselect new com.example.UserDTO(u.name, u.email) from User uDTO 必须有对应构造函数
distinct去重查询select distinct u.department from User u避免重复结果

6.3 WHERE 条件查询

运算符语法用途代码示例注意事项
=property = value等值比较from User u where u.age = 25字符串需加引号
<>!=property <> value不等比较where u.status != 'inactive'
>, <, >=, <=数值比较范围判断where u.salary >= 5000
betweenproperty between x and y范围查询where u.age between 18 and 60闭区间
inproperty in (list)集合匹配where u.role in ('admin','user')支持子查询
not inproperty not in (list)集合排除where u.id not in (1,2,3)
is null / is not null判断空值空值检查where u.email is not null不能用 = null
likeproperty like pattern模糊匹配where u.name like '张%'支持 %_
and / or / not逻辑运算组合条件where u.age > 20 and u.active = true注意优先级

6.4 参数绑定(命名参数、位置参数)

参数类型语法用途代码示例注意事项
命名参数:paramName使用名称绑定参数Query q = session.createQuery("from User u where u.name = :name"); q.setParameter("name", "张三");推荐使用,可读性强
位置参数?index按位置绑定参数Query q = session.createQuery("from User u where u.name = ?1"); q.setParameter(1, "张三");从 1 开始编号
setParameter()通用方法设置任意类型参数q.setParameter("id", 100L);自动处理类型转换
setString(), setLong() 等类型专用方法设置特定类型参数q.setLong("id", 100L);已过时,推荐使用 setParameter

注意: 命名参数更安全,支持重复使用;避免字符串拼接防止 SQL 注入。

6.5 聚合函数与分组(GROUP BY, HAVING)

函数/子句语法用途代码示例注意事项
count()count(property)计数select count(*) from User支持 count(*), count(prop)
max(), min()max(property)最大最小值select max(salary) from Employee
avg()avg(property)平均值select avg(age) from User返回 Double
sum()sum(property)求和select sum(price) from Order
group bygroup by property分组统计select u.dept, count(*) from User u group by u.deptselect 中非聚合字段必须出现在 group by
havinghaving condition分组后过滤having count(*) > 5类似 SQL,用于过滤分组结果

6.6 分页查询(setFirstResult, setMaxResults)

方法语法用途代码示例注意事项
setFirstResult()query.setFirstResult(int)设置起始行索引(从 0 开始)query.setFirstResult(10);第 11 条开始
setMaxResults()query.setMaxResults(int)设置最大返回行数query.setMaxResults(20);最多 20 条
组合使用共同实现分页获取第 2 页,每页 10 条query.setFirstResult(10).setMaxResults(10);常用于大数据集分页

示例:

Query query = session.createQuery("from User");
query.setFirstResult((pageNo - 1) * pageSize);
query.setMaxResults(pageSize);
List<User> list = query.list();

6.7 多表查询与连接(JOIN)

连接类型HQL 语法用途代码示例注意事项
inner joinjoin内连接,只返回匹配行from User u join u.orders o默认行为
left outer joinleft joinleft outer join左外连接,返回左表全部from User u left join u.profile p常用于可选关联
right outer joinright joinright outer join右外连接,返回右表全部from Profile p right join p.user u较少使用
full joinfull join全连接from A a full join B b on a.id = b.a_id支持有限
join fetchjoin fetch连接并立即加载(解决懒加载)from User u left join fetch u.orders避免 N+1 查询问题

注意:

  • 使用关联属性名进行连接,如 u.orders
  • fetch join 会将关联数据一同加载,Session 关闭后仍可访问。

6.8 投影查询与构造器返回

投影方式语法用途代码示例注意事项
多列投影select prop1, prop2返回 Object[]select u.name, u.email from User u结果为 Object[] 列表
单列投影select prop返回单一类型列表select u.name from User u返回 List<String>
构造器投影select new Class(...)返回自定义对象select new UserDTO(u.name, u.email) from User uDTO 必须有对应构造函数
别名使用使用 as 定义别名提高可读性select new UserDTO(u.name as name, ...)构造函数参数顺序必须匹配

示例:

public class UserDTO {
    public UserDTO(String name, String email) { ... }
}

Query q = session.createQuery(
    "select new com.example.UserDTO(u.name, u.email) from User u");
List<UserDTO> dtos = q.list();

第七章:原生 SQL 查询

7.1 使用 SQLQuery 执行原生 SQL

方法/语法用途代码示例注意事项
createSQLQuery()创建原生 SQL 查询对象Query query = session.createSQLQuery("SELECT * FROM t_user");方法已过时,在新版本中推荐使用 createNativeQuery()
createNativeQuery()JPA 标准方式创建原生查询Query query = session.createNativeQuery("SELECT * FROM t_user");推荐使用,符合 JPA 规范
list()执行查询并返回结果列表List<Object[]> results = query.list();原生 SQL 默认返回 Object[] 数组
uniqueResult()返回唯一结果,若无结果返回 null,多于一个抛异常Object result = query.uniqueResult();适用于主键查询或唯一约束查询
executeUpdate()执行更新/删除语句,返回影响行数int rows = session.createNativeQuery("UPDATE t_user SET name = ?").executeUpdate();用于 INSERT, UPDATE, DELETE

注意: createSQLQuery() 在 Hibernate 5+ 中已被标记为过时,应优先使用 createNativeQuery()

7.2 结果集映射(addEntity, addScalar)

方法语法用途代码示例注意事项
addEntity(Class)query.addEntity(User.class)将结果映射为指定实体类SQLQuery query = session.createSQLQuery("SELECT * FROM t_user"); query.addEntity(User.class);要求字段名与实体属性匹配(或通过映射配置)
addEntity(String, Class)query.addEntity("alias", EntityClass.class)为带别名的表指定实体映射query.addEntity("u", User.class);用于多表连接查询
addScalar(String)query.addScalar("columnName")指定查询的标量字段(基本类型)query.addScalar("name"); query.addScalar("age");明确字段类型,避免类型推断错误
addScalar(String, Type)query.addScalar("price", StandardBasicTypes.DOUBLE)指定字段及其 Hibernate 类型query.addScalar("salary", StandardBasicTypes.BIG_DECIMAL);控制类型映射精度,如金额使用 BigDecimal
setResultTransformer()query.setResultTransformer(Transformers.aliasToBean(DTO.class))将结果转换为 DTO 对象query.setResultTransformer(new AliasToEntityMapResultTransformer());需引入 org.hibernate.transform

示例:混合映射

SQLQuery query = session.createSQLQuery(
    "SELECT u.id, u.name, r.role_name FROM t_user u JOIN t_role r ON u.role_id = r.id");
query.addScalar("id").addScalar("name").addScalar("role_name");
query.setResultTransformer(Transformers.aliasToBean(UserRoleDTO.class));
List<UserRoleDTO> dtos = query.list();

7.3 命名 SQL 查询(@NamedNativeQuery)

注解用途代码示例注意事项
@NamedNativeQuery定义命名的原生 SQL 查询@NamedNativeQuery(name = "findUserByEmail", query = "SELECT * FROM t_user WHERE email = ?1", resultClass = User.class)必须定义在实体类上
name查询的唯一标识符name = "findActiveUsers"用于在代码中引用该查询
query原生 SQL 语句query = "SELECT * FROM t_user WHERE status = 'ACTIVE'"可包含参数占位符 ?1, ?2:param
resultClass映射结果类型(实体类)resultClass = User.class返回实体对象列表
resultSetMapping引用自定义结果集映射resultSetMapping = "UserSummaryMapping"用于复杂映射场景
@SqlResultSetMapping定义自定义结果集结构@SqlResultSetMapping(name = "UserSummaryMapping", entities = @EntityResult(entityClass = User.class), columns = @ColumnResult(name = "total"))支持实体和标量混合返回

使用命名查询:

Query query = session.getNamedQuery("findUserByEmail");
query.setParameter(1, "user@example.com");
List<User> users = query.list();

优点: 将 SQL 与代码分离,便于维护;支持缓存;可在启动时验证 SQL 语法。


第八章:Criteria API 查询

8.1 Criteria API 概述(旧版 Criteria)

概念说明注意事项
Criteria API面向对象的查询 API,用于构建类型安全的查询旧版(Hibernate 5.2 之前)基于 org.hibernate.Criteria
创建方式通过 Session 创建 Criteria 实例Criteria criteria = session.createCriteria(User.class);
类型安全相比 HQL 字符串拼接,更安全,编译期检查但旧版 Criteria 并非完全类型安全(使用字符串属性名)
可读性条件构建清晰,适合动态查询代码较冗长
过时状态自 Hibernate 5.2 起被标记为 deprecated官方推荐使用 JPA 2.1+ 的 javax.persistence.criteria.CriteriaQuery

替代方案: 新项目应使用 JPA Criteria API 或 QueryDSL。

8.2 Restrictions 条件构建

方法语法用途代码示例注意事项
Restrictions.eq()eq("property", value)等值比较criteria.add(Restrictions.eq("name", "张三"));
Restrictions.ne()ne("property", value)不等比较criteria.add(Restrictions.ne("status", "INACTIVE"));
Restrictions.gt(), ge()gt("age", 18)大于、大于等于criteria.add(Restrictions.ge("age", 18));
Restrictions.lt(), le()lt("salary", 5000)小于、小于等于
Restrictions.between()between("age", 18, 60)范围查询criteria.add(Restrictions.between("age", 18, 60));闭区间
Restrictions.like()like("name", "李%")模糊匹配criteria.add(Restrictions.like("name", "李%"));支持 %_
Restrictions.in()in("role", list)集合匹配criteria.add(Restrictions.in("role", Arrays.asList("admin","user")));
Restrictions.isNull(), isNotNull()isNull("email")空值判断criteria.add(Restrictions.isNotNull("email"));不能使用 == null
Restrictions.and(), or(), not()and(crit1, crit2)逻辑组合criteria.add(Restrictions.and(Restrictions.eq("a",1), Restrictions.eq("b",2)));
复合条件多个 add() 调用默认为 AND 关系criteria.add(...).add(...);多个条件自动组合为 AND

8.3 Projections 聚合操作

方法语法用途代码示例注意事项
Projections.rowCount()Projections.rowCount()计数criteria.setProjection(Projections.rowCount());返回 Long 类型
Projections.max(), min()Projections.max("salary")最大最小值criteria.setProjection(Projections.max("age"));
Projections.avg()Projections.avg("price")平均值criteria.setProjection(Projections.avg("score"));
Projections.sum()Projections.sum("amount")求和criteria.setProjection(Projections.sum("total"));
Projections.groupProperty()Projections.groupProperty("dept")分组字段criteria.setProjection(Projections.projectionList() .add(Projections.groupProperty("dept")) .add(Projections.count("id")));必须与聚合函数结合使用
ProjectionList组合多个投影ProjectionList list = Projections.projectionList();使用 Projections.projectionList() 创建可组合多个聚合与分组字段

示例:分组统计

Criteria criteria = session.createCriteria(User.class);
ProjectionList projList = Projections.projectionList();
projList.add(Projections.groupProperty("department"));
projList.add(Projections.count("id"));
projList.add(Projections.avg("salary"));
criteria.setProjection(projList);
List<Object[]> results = criteria.list();

8.4 Order 排序控制

方法语法用途代码示例注意事项
Order.asc()Order.asc("property")升序排列criteria.addOrder(Order.asc("name"));
Order.desc()Order.desc("property")降序排列criteria.addOrder(Order.desc("createTime"));
多重排序连续添加 Order先按 A 排,再按 B 排criteria.addOrder(Order.asc("dept")).addOrder(Order.desc("salary"));执行顺序即优先级

8.5 关联查询与别名(createAlias)

方法语法用途代码示例注意事项
createAlias()createAlias("association", "alias")创建关联别名,用于连接查询criteria.createAlias("orders", "o"); criteria.add(Restrictions.gt("o.amount", 1000));执行 INNER JOIN
createCriteria()createCriteria("association")创建子 Criteria(已过时)criteria.createCriteria("orders").add(...);不推荐,应使用 createAlias + Restrictions
别名用途在 Restrictions 中引用关联属性Restrictions.eq("o.status", "PAID")必须先 createAlias 才能使用别名
外连接支持createAlias("prop", "alias", JoinType.LEFT_OUTER_JOIN)指定连接类型criteria.createAlias("profile", "p", JoinType.LEFT_OUTER_JOIN);需导入 org.hibernate.sql.JoinType

示例:查询有高价值订单的用户

Criteria criteria = session.createCriteria(User.class);
criteria.createAlias("orders", "o");
criteria.add(Restrictions.gt("o.total", 5000));
criteria.addOrder(Order.desc("o.total"));
List<User> users = criteria.list();

注意: createAlias 是实现关联查询的关键,避免了 N+1 查询问题(当 fetch 策略为 LAZY 时)。


第九章:注解映射(Annotation Mapping)

9.1 @Entity 与 @Table

注解用途语法代码示例注意事项
@Entity标记一个类为持久化实体,对应数据库表@Entity@Entity public class User { ... }必须标注在类上;需有无参构造函数
@Table指定实体映射的数据库表名及表级配置@Table(name="table_name")@Entity @Table(name = "t_user") public class User { ... }可设置表名、schema、catalog、唯一约束等;若不指定,默认表名为类名
name 属性指定数据库表名name = "custom_table"@Table(name = "users")区分大小写,取决于数据库
schema 属性指定数据库 schemaschema = "public"@Table(schema = "admin")用于多 schema 环境
uniqueConstraints定义表级唯一约束uniqueConstraints = @UniqueConstraint(columnNames={"col1","col2"})@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"email"}))生成 DDL 时生效

9.2 @Id 与主键生成策略(@GeneratedValue)

注解用途语法代码示例注意事项
@Id标识主键属性@Id@Id private Long id;每个实体必须有一个 @Id
@GeneratedValue定义主键生成策略@GeneratedValue(strategy = GenerationType.XXX)@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;需与 @Id 一起使用
strategy 属性指定生成策略strategy = GenerationType.IDENTITY见下表
generator 属性引用自定义生成器generator = "my_gen"配合 @GenericGenerator 使用

主键生成策略:

策略说明适用场景
GenerationType.IDENTITY数据库自增(如 MySQL AUTO_INCREMENT)MySQL, SQL Server
GenerationType.SEQUENCE使用数据库序列(如 Oracle)Oracle, PostgreSQL
GenerationType.TABLE使用单独表模拟序列跨数据库兼容
GenerationType.AUTO由 Hibernate 自动选择(IDENTITY/SEQUENCE)通用,推荐
GenerationType.UUID生成 UUID 字符串分布式系统

9.3 @Column 映射

注解属性用途语法代码示例注意事项
name指定数据库列名name = "col_name"@Column(name = "user_name")默认为属性名
length字符串字段长度length = 50@Column(length = 100)默认 255
nullable是否可为空nullable = false@Column(nullable = false)对应 NOT NULL 约束
unique是否唯一unique = true@Column(unique = true)创建唯一索引
insertable是否参与 INSERTinsertable = false用于只读字段(如创建时间)
updatable是否参与 UPDATEupdatable = false用于不可修改字段(如创建时间)
precision, scale数值精度与小数位precision=10, scale=2@Column(precision = 10, scale = 2)用于 BigDecimal,金额字段

9.4 @OneToOne 映射

注解用途语法代码示例注意事项
@OneToOne定义一对一关联@OneToOne@OneToOne private Profile profile;默认使用主键关联
mappedBy指定关系由对方维护(双向)mappedBy = "user"@OneToOne(mappedBy = "profile") private User user;用于被维护端
cascade级联操作cascade = CascadeType.ALL@OneToOne(cascade = CascadeType.ALL)常用于聚合关系
fetch加载策略fetch = FetchType.LAZY@OneToOne(fetch = FetchType.LAZY)注意:@OneToOne 默认 EAGER,建议显式设为 LAZY
optional关联是否可为空optional = false@OneToOne(optional = false)对应外键是否允许 NULL

外键位置: 在未使用 mappedBy 的一方添加外键。

9.5 @OneToMany 与 @ManyToOne 映射

注解用途语法代码示例注意事项
@OneToMany一的一方持有多个@OneToMany@OneToMany(mappedBy = "user") private Set<Order> orders;必须使用 mappedBy 指向 @ManyToOne 端
mappedBy指定由 @ManyToOne 端维护mappedBy = "user"同上@OneToMany 端为只读
@ManyToOne多的一方引用一的一方@ManyToOne@ManyToOne private User user;关系维护端,外键在此方
fetch加载策略fetch = FetchType.LAZY@OneToMany(fetch = FetchType.LAZY)@OneToMany 默认 LAZY,推荐
cascade级联操作cascade = CascadeType.ALL@OneToMany(cascade = CascadeType.ALL, mappedBy = "user")常用于级联保存/删除子对象

最佳实践:

  • 使用 Set 而非 List 避免性能问题。
  • 初始化集合:private Set<Order> orders = new HashSet<>();

9.6 @ManyToMany 映射

注解用途语法代码示例注意事项
@ManyToMany定义多对多关联@ManyToMany@ManyToMany private Set<Role> roles;需配合 @JoinTable
mappedBy指定非维护端mappedBy = "users"@ManyToMany(mappedBy = "roles") private Set<User> users;维护端定义中间表
@JoinTable指定中间表信息@JoinTable(...)见 9.7 节维护端必须使用

9.7 @JoinColumn 与 @JoinTable

注解用途语法代码示例注意事项
@JoinColumn指定外键列@JoinColumn(name = "user_id")@ManyToOne @JoinColumn(name = "user_id")用于 @OneToOne, @ManyToOne, @OneToMany(单向)
name外键列名name = "fk_col"@JoinColumn(name = "author_id")默认命名:关联实体名_主键
@JoinTable定义中间表@JoinTable(name="user_role", ...)@ManyToMany @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))用于 @ManyToMany
joinColumns当前实体在中间表的外键joinColumns = @JoinColumn(...)指向本方主键
inverseJoinColumns关联实体在中间表的外键inverseJoinColumns = @JoinColumn(...)指向对方主键

9.8 其他常用注解(@Transient, @Temporal 等)

注解用途语法代码示例注意事项
@Transient标记非持久化字段@Transient@Transient private String tempData;不映射到数据库,如临时计算字段
@Temporal指定日期时间类型映射@Temporal(TemporalType.DATE/TIME/TIMESTAMP)@Temporal(TemporalType.TIMESTAMP) private Date createTime;Java 8 之后推荐使用 java.time 类型(如 LocalDateTime),无需此注解
@Lob大对象类型(CLOB/BLOB)@Lob@Lob private String content;用于长文本或二进制数据
@Enumerated枚举类型映射@Enumerated(EnumType.STRING/ORDINAL)@Enumerated(EnumType.STRING) private Status status;STRING 更安全,ORDINAL 为序号
@CreationTimestamp自动填充创建时间@CreationTimestamp@CreationTimestamp private LocalDateTime createTime;需启用 hibernate.jpa.compliance.entity-graph 或使用监听器
@UpdateTimestamp自动填充更新时间@UpdateTimestamp@UpdateTimestamp private LocalDateTime updateTime;同上

注意: @CreationTimestamp@UpdateTimestamp 是 Hibernate 特有注解,非 JPA 标准。


第十章:事务管理与并发控制

10.1 事务的基本概念(ACID)

特性说明Hibernate 中的体现
A - 原子性 (Atomicity)事务是不可分割的工作单位,要么全部成功,要么全部失败Hibernate 通过 Transaction 确保所有操作在 commit 前不会持久化
C - 一致性 (Consistency)事务执行前后,数据库从一个一致状态转换到另一个一致状态Hibernate 映射约束(如非空、唯一)帮助维护一致性
I - 隔离性 (Isolation)多个事务并发执行时,彼此隔离,互不干扰Hibernate 通过数据库隔离级别和锁机制实现
D - 持久性 (Durability)事务一旦提交,其结果永久保存在数据库中提交后数据写入磁盘,即使系统崩溃也不会丢失

10.2 Hibernate 中的事务 API(Transaction)

方法用途代码示例注意事项
beginTransaction()开始新事务Transaction tx = session.beginTransaction();返回 Transaction 对象
commit()提交事务tx.commit();成功完成操作后调用,数据持久化
rollback()回滚事务tx.rollback();异常时调用,撤销所有更改
isActive()检查事务是否活跃if (tx.isActive()) { ... }提交或回滚后返回 false
setRollbackOnly()标记事务为只回滚tx.setRollbackOnly();用于跨方法调用时通知回滚

基本模式:

Session session = sessionFactory.openSession();
Transaction tx = null;
try {
    tx = session.beginTransaction();
    // 执行数据库操作
    session.save(user);
    tx.commit();
} catch (Exception e) {
    if (tx != null) tx.rollback();
    throw e;
} finally {
    session.close();
}

10.3 事务边界控制(begin/commit/rollback)

原则说明实践建议
短事务事务应尽可能短,减少锁持有时间避免在事务中执行耗时操作(如网络调用)
原子性操作一个事务应包含一组逻辑上不可分割的操作如转账:扣款与入账应在同一事务
异常处理必须捕获异常并回滚try-catch-finally 中确保 rollback() 调用
资源管理及时关闭 Session使用 try-with-resources(若 Session 实现 AutoCloseable)或 finally 块
传播行为在 Spring 等框架中需配置事务传播如 REQUIRED, REQUIRES_NEW

10.4 并发策略与锁机制(乐观锁 @Version)

机制说明配置方式注意事项
乐观锁 (Optimistic Locking)假设并发冲突少,通过版本号检测冲突@Version private Integer version;字段类型可为 int, long, Date;更新时检查版本
悲观锁 (Pessimistic Locking)假设冲突频繁,直接加锁阻止并发访问session.get(User.class, id, LockMode.PESSIMISTIC_WRITE);性能开销大,慎用
@Version 注解实现乐观锁@Version private Long version;字段在每次更新时自动递增
工作流程1. 读取数据(含版本号)2. 修改数据 3. 更新时检查版本是否匹配UPDATE t_user SET name=?, version=version+1 WHERE id=? AND version=?若版本不匹配,抛 StaleObjectStateException
适用场景读多写少,冲突概率低Web 应用、高并发系统推荐作为默认并发控制策略

示例:

@Entity
public class User {
    @Id
    private Long id;
    private String name;
    @Version
    private Integer version; // 乐观锁字段
    // getters and setters
}

当两个事务同时读取同一用户并尝试更新时,后提交的事务会因版本号不匹配而失败,需由应用层处理重试或提示用户。


第十一章:缓存机制

11.1 一级缓存(Session 级缓存)

特性说明注意事项
作用范围绑定到 Session 实例,生命周期与 Session 一致每个 Session 拥有独立的一级缓存
自动启用默认开启,无需配置Hibernate 自动管理
缓存内容持久化状态的对象(通过 get(), load(), HQL, Criteria 查询加载的对象)包括实体、集合、代理对象
主要作用- 避免重复 SQL 查询 - 保证事务内对象一致性 - 支持脏检查(Dirty Checking)在同一个 Session 中多次查询同一主键对象,仅第一次访问数据库
清除时机- session.clear() - session.evict(entity) - session.close() - 事务提交后(Session 通常关闭)flush() 会将更改同步到数据库,但不清理缓存
示例User u1 = session.get(User.class, 1L); User u2 = session.get(User.class, 1L); // u1 == u2(同一实例)第二次 get() 不会发送 SQL

注意: 一级缓存是强制性的,无法关闭。

11.2 二级缓存(SessionFactory 级缓存)

特性说明配置方式注意事项
作用范围跨 Session,由 SessionFactory 管理所有 Session 共享二级缓存
可选启用需手动配置并开启hibernate.cache.use_second_level_cache=true默认关闭
缓存粒度按实体类或集合缓存需为每个实体显式声明可缓存
缓存提供者需集成第三方缓存实现(如 Ehcache, Redis)通过 hibernate.cache.region.factory_class 指定见 11.4
配置实体可缓存使用 @Cacheable@Cache 注解@Entity @Cacheable @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)或在 .hbm.xml 中使用 <cache usage="read-write"/>
并发策略控制并发访问行为READ_ONLY:只读,适合静态数据 / READ_WRITE:读写,基于时间戳 / NONSTRICT_READ_WRITE:非严格读写,可能脏读 / TRANSACTIONAL:支持事务,开销大推荐 READ_WRITE
命中条件仅对通过主键查询(get(), load())有效HQL/SQL 查询默认不使用二级缓存需配合查询缓存(11.3)

示例配置(pom.xml 引入 Ehcache):

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
    <version>5.6.15.Final</version>
</dependency>

hibernate.cfg.xml:

<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

11.3 查询缓存

特性说明配置方式注意事项
作用缓存 HQL 或 Criteria 查询的结果集 ID 列表配合二级缓存使用,避免重复执行查询
启用需开启并配置hibernate.cache.use_query_cache=true默认关闭
使用方式在查询对象上调用 setCacheable(true)query.setCacheable(true);可选设置缓存区域 setCacheRegion("query.user")
工作原理1. 缓存查询条件 + 参数 → 结果集 ID 列表 2. 根据 ID 从二级缓存获取实体对象若二级缓存中无实体,仍需访问数据库
失效机制当查询涉及的任何表发生增删改时,所有相关查询缓存失效高频更新的表会使查询缓存失效频繁,降低效率
适用场景查询条件固定、结果集不大、数据更新不频繁如分页查询、字典查询
不适用场景频繁更新的表、大结果集、使用 ORDER BY RAND() 等非确定性查询

示例:

Query query = session.createQuery("FROM User u WHERE u.status = :status");
query.setParameter("status", "ACTIVE");
query.setCacheable(true); // 启用查询缓存
query.setCacheRegion("userQueries"); // 指定缓存区域
List<User> users = query.list();

11.4 常用缓存提供者(Ehcache, Redis 集成)

缓存提供者特点集成方式注意事项
Ehcache本地内存缓存、轻量级,部署简单、支持磁盘溢出1. 添加 hibernate-ehcache 依赖 2. 配置 ehcache.xml 3. 在 hibernate.cfg.xml 中指定 EhCacheRegionFactory适合单机应用;集群环境下需使用 Terracotta 实现分布式
Redis分布式内存数据库、支持持久化、高可用、适合集群环境1. 添加 hibernate-redis 依赖(如 hibernate-redis:2.7.0)2. 配置 hibernate.cache.region.factory_class=org.hibernate.cache.redis.hibernate52.SingletonRedisRegionFactory 3. 设置 Redis 连接信息(host, port)需独立部署 Redis 服务;网络延迟可能影响性能;确保序列化兼容
InfinispanJBoss 出品,功能强大、支持本地/分布式模式、与 WildFly 集成好配置 InfinispanRegionFactory适合企业级应用
Caffeine高性能本地缓存、替代 Guava Cache需通过自定义 RegionFactory 集成Hibernate 官方不直接支持,需额外开发

Redis 配置示例(hibernate.cfg.xml):

<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.cache.region.factory_class">
    org.hibernate.cache.redis.hibernate52.SingletonRedisRegionFactory
</property>
<property name="hibernate.cache.redis.host">127.0.0.1</property>
<property name="hibernate.cache.redis.port">6379</property>
<property name="hibernate.cache.redis.expiration">3600</property>

第十二章:性能优化与最佳实践

12.1 N+1 查询问题与解决方案

问题描述解决方案实现方式示例
N+1 问题:执行 1 次主查询 + N 次关联查询(如查用户列表,每用户查订单)1. JOIN FETCHHQL: from User u left join fetch u.ordersfrom User u join fetch u.profile
2. 批量抓取 (Batch Fetching)注解: @BatchSize(size=10)@Entity @BatchSize(size=20) class User {...}
3. 子查询抓取 (Subselect Fetching)注解: @Fetch(FetchMode.SUBSELECT)
后果性能急剧下降,数据库交互次数过多
检测启用 SQL 日志,观察重复的 SELECT 语句hibernate.show_sql=true hibernate.format_sql=true

最佳实践:

  • 优先使用 JOIN FETCH 解决已知的 N+1 问题。
  • 对集合关联使用 @BatchSize 减少查询次数。
  • 避免在循环中调用 session.get()

12.2 批量操作(batch processing)

方法用途代码示例注意事项
JDBC 批量提高大批量增删改性能hibernate.jdbc.batch_size=20在 hibernate.cfg.xml 中配置
手动分批提交避免内存溢出和长事务for (int i = 0; i < 10000; i++) { session.save(entity); if (i % 50 == 0) { session.flush(); session.clear(); } }flush() 同步到数据库;clear() 清理一级缓存;避免 OutOfMemoryError
StatelessSession无状态批量操作(见 12.4)StatelessSession statelessSession = sessionFactory.openStatelessSession();不使用缓存和持久化上下文

配置:

hibernate.jdbc.batch_size=20
hibernate.order_inserts=true      # 批量优化
hibernate.order_updates=true
hibernate.batch_versioned_data=true # 批量更新版本化数据

12.3 fetch 策略优化

关联类型推荐 Fetch 策略原因
@ManyToOneFetchType.LAZY默认 EAGER,易导致过度加载
@OneToOneFetchType.LAZY默认 EAGER,常造成性能问题
@OneToManyFetchType.LAZY默认 LAZY,保持
@ManyToManyFetchType.LAZY默认 LAZY,保持

优化建议:

  • 将 @ManyToOne 和 @OneToOne 显式设为 LAZY。
  • 在 HQL 中使用 JOIN FETCH 按需预加载。
  • 避免在实体中定义 EAGER 关联,除非 100% 确定每次都需要。

12.4 StatelessSession 的使用场景

特性说明适用场景注意事项
无一级缓存不缓存任何对象批量导入、导出数据无法利用缓存
无脏检查不跟踪对象状态变化高性能 ETL 作业必须显式调用 insert(), update(), delete()
无级联操作不支持 cascade简单的 CRUD 批量操作需手动管理关联
低内存占用不维护持久化上下文处理海量数据(> 10万条)避免 OutOfMemoryError
方法insert(), update(), delete(), get(), query()statelessSession.insert(user);不支持 saveOrUpdate()

示例:

StatelessSession session = sessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();
for (User user : users) {
    session.insert(user); // 必须显式插入
}
tx.commit();
session.close();

12.5 性能监控与日志配置

工具/配置用途配置方式注意事项
SQL 日志查看执行的 SQL 语句hibernate.show_sql=true hibernate.format_sql=true开发环境开启,生产环境关闭
慢查询日志识别执行时间长的查询数据库层面配置(如 MySQL slow_query_log)结合 APM 工具分析
统计信息监控缓存命中率、查询次数等hibernate.generate_statistics=true 通过 SessionFactory.getStatistics() 获取性能开销小,生产可用
APM 工具应用性能监控(如 SkyWalking, Prometheus)集成探针或埋点全链路监控,定位瓶颈
Hibernate Metrics暴露指标到 Micrometer使用 hibernate-micrometer 模块与 Spring Boot Actuator 集成

推荐日志配置(logback.xml):

<logger name="org.hibernate.SQL" level="DEBUG"/>
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/> <!-- 参数绑定 -->
<logger name="org.hibernate.cache" level="DEBUG"/> <!-- 缓存日志 -->

第十三章:Spring 集成(可选)

13.1 Spring 与 Hibernate 整合原理

核心思想说明优势
容器管理Spring 容器负责创建和管理 SessionFactory、Session 和 Transaction解耦配置与使用,便于维护
依赖注入Hibernate 的 SessionFactory 通过 DI 注入到 DAO 或 Service 层符合 IoC 原则,提升可测试性
事务抽象Spring 提供统一的事务管理 API,屏蔽底层差异(Hibernate、JDBC 等)支持声明式事务,简化事务控制
资源管理Spring 自动处理 Session 的开启、关闭和绑定到线程(通过 ThreadLocal)避免资源泄漏,保证线程安全
异常转换将 Hibernate 的 Checked Exception 转换为 Spring 的 DataAccessException(Runtime Exception)简化异常处理,无需显式捕获

整合方式:

  1. XML 配置(传统方式)
  2. Java Config(推荐)
  3. Spring Boot 自动配置(最简便)

13.2 配置 LocalSessionFactoryBean

配置方式说明代码示例
XML 配置在 Spring 配置文件中定义 LocalSessionFactoryBean<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="packagesToScan" value="com.example.entity"/><property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</prop><prop key="hibernate.hbm2ddl.auto">update</prop><prop key="hibernate.show_sql">true</prop></props></property></bean>
Java Config使用 @Configuration 类配置见下方代码示例
关键属性dataSource:数据源;packagesToScan:实体类包路径;hibernateProperties:Hibernate 配置属性

Java Config 示例:

@Configuration
@EnableTransactionManagement
public class HibernateConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setPackagesToScan("com.example.entity");
        Properties hibernateProperties = new Properties();
        hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
        hibernateProperties.put("hibernate.hbm2ddl.auto", "update");
        sessionFactory.setHibernateProperties(hibernateProperties);
        return sessionFactory;
    }
}

注意:

  • LocalSessionFactoryBean 是 Spring 提供的工厂 Bean,用于创建 Hibernate SessionFactory。
  • packagesToScan 替代了传统的 mappingResources,自动扫描带 @Entity 的类。

13.3 声明式事务管理(@Transactional)

注解用途配置方式注意事项
@Transactional声明方法或类具有事务性在方法或类上添加注解需启用 <tx:annotation-driven/>@EnableTransactionManagement
事务传播控制事务的边界行为@Transactional(propagation = Propagation.REQUIRED)默认 REQUIRED
隔离级别设置事务隔离@Transactional(isolation = Isolation.READ_COMMITTED)默认数据库默认级别
只读事务提示数据库优化@Transactional(readOnly = true)用于查询方法,提升性能
回滚规则指定异常时是否回滚@Transactional(rollbackFor = Exception.class)默认仅对 RuntimeException 和 Error 回滚
超时设置事务最大执行时间@Transactional(timeout = 30)单位:秒

工作原理: Spring 使用 AOP 动态代理,在目标方法执行前后开启和提交/回滚事务。

示例:

@Service
public class UserService {

    @Autowired
    private SessionFactory sessionFactory;

    @Transactional
    public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
        Session session = sessionFactory.getCurrentSession();
        User from = session.get(User.class, fromId);
        User to = session.get(User.class, toId);
        from.setBalance(from.getBalance().subtract(amount));
        to.setBalance(to.getBalance().add(amount));
        session.update(from);
        session.update(to);
        // 方法结束,事务自动提交
    }
}

注意:

  • @Transactional 仅对 public 方法有效。
  • 自调用(同一个类中方法调用)不会触发事务代理。

13.4 使用 HibernateTemplate(已过时)或直接使用 SessionFactory

方式说明代码示例状态与建议
HibernateTemplateSpring 提供的模板类,封装了 Session 获取、异常转换等hibernateTemplate.get(User.class, id)已过时(从 Spring 4.3+ 不再推荐)。原因:侵入性代码,限制了 Hibernate 新特性使用
直接使用 SessionFactory通过 getCurrentSession() 获取绑定到事务的 SessionsessionFactory.getCurrentSession().get(User.class, id)推荐方式。更简洁,直接使用原生 API,与 Spring 事务无缝集成

getCurrentSession() 特点:

  • 返回当前线程绑定的 Session。
  • 由 Spring 管理生命周期,无需手动关闭。
  • 要求已配置 hibernate.current_session_context_class=thread(Spring 已自动处理)。

示例:

@Repository
public class UserDao {

    @Autowired
    private SessionFactory sessionFactory;

    public User findById(Long id) {
        return sessionFactory.getCurrentSession().get(User.class, id);
    }

    public void save(User user) {
        sessionFactory.getCurrentSession().save(user);
    }
}

总结: 现代 Spring + Hibernate 开发应:

  1. 使用 LocalSessionFactoryBean 或 Java Config 配置 SessionFactory。
  2. 直接注入 SessionFactory 并调用 getCurrentSession()
  3. 使用 @Transactional 管理事务。
  4. 避免使用 HibernateTemplate。