JPA 增强
JPA 增强基于 spring-boot-starter-data-jpa 与 Hibernate 5 做了一系列扩展,覆盖类型映射、SQL 函数、ID 生成、Envers 审计、Querydsl 仓库、RSQL 查询,以及 Spring Data 分页结果与 Sort 的 JSON 序列化等场景。
主要扩展
- MySQL JSON 字段映射:
JsonType自动注册为 Hibernate 类型json - MySQL 自定义 SQL 函数:
group_concat/json_extract/regexp等常用函数即开即用 - Snowflake / JDK ID 生成器:内置可直接通过
@GenericGenerator引用 - 条件式 Envers 审计:可在事务/线程作用域内动态跳过审计
- Querydsl 仓库扩展:
QuerydslRepositoryCustom+ 自动装配的JPAQueryFactory/HibernateQueryFactory - RSQL → Querydsl 转换:基于 rsql-parser 把 RSQL 字符串转 Querydsl
Predicate - Hibernate5 Jackson 集成:默认的、对懒加载/缺失实体友好的
Hibernate5Module - Page / Slice / QueryResults 序列化:统一为
{content, total | hasNext},并支持Sort反序列化
安装
Maven
<dependency>
<groupId>com.jeeapp.spring.boot</groupId>
<artifactId>jpa-spring-boot-starter</artifactId>
</dependency>
Gradle
implementation 'com.jeeapp.spring.boot:jpa-spring-boot-starter'
可选依赖(按需开启):
cz.jirutka.rsql:rsql-parser:启用 RSQL → Querydsl 转换org.hibernate:hibernate-envers:启用条件式 Envers 审计
配置
# 数据库平台。设置为 MYSQL 后,本 Starter 会自动注册 MySQL 自定义 SQL 函数与 JsonType
spring.jpa.database=MYSQL
spring.jpa.show-sql=false
spring.jpa.open-in-view=false
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
# Pooled-lo 优化,减少 ID 段空洞
spring.jpa.properties.hibernate.id.optimizer.pooled.preferred=pooled-lo
# 允许 merge 时多份持久化副本共存
spring.jpa.properties.hibernate.event.merge.entity_copy_observer=allow
# JDBC 批处理与抓取
spring.jpa.properties.hibernate.jdbc.batch_size=20
spring.jpa.properties.hibernate.jdbc.fetch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.batch_versioned_data=true
# 二级缓存(按需)
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region_prefix=hibernate:
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE
关键说明:
spring.jpa.database=MYSQL:是MySQLFunctionsContributor(自定义函数)与TypesContributor(JsonType)的触发开关,未设置时这两个扩展不会注入。spring.jpa.open-in-view=false:建议显式关闭,避免事务边界外的懒加载。hibernate.id.optimizer.pooled.preferred=pooled-lo:与SequenceStyleGenerator/Snowflake等 ID 策略配合,减少 ID 跳号问题。- 二级缓存项:仅在引入对应的二级缓存实现(如 ehcache、caffeine)时才生效。
扩展能力
MySQL JSON 字段映射
com.jeeapp.jpa.hibernate.type.JsonType 注册名为 json(也以全限定类名 com.jeeapp.jpa.hibernate.type.JsonType 暴露),通过 TypesContributor 仅在 MySQLDialect 下自动注册。底层使用 Jackson 序列化,关闭 FAIL_ON_UNKNOWN_PROPERTIES 与 DEFAULT_VIEW_INCLUSION,并通过 findAndAddModules() 加载已注册的 Jackson 模块(如 JavaTime)。
@Entity
public class Order {
@Id
private Long id;
@Type(type = "json")
@Column(columnDefinition = "json")
private OrderExtra extra;
}
MySQL 自定义 SQL 函数
com.jeeapp.jpa.hibernate.dialect.MySQLFunctionsContributor 在 spring.jpa.database=MYSQL 时自动通过 MetadataBuilderContributor 注册以下函数,可直接在 JPQL / Criteria / Querydsl Expressions.function(...) 中使用:
| 函数 | 返回类型 | 说明 |
|---|---|---|
group_concat | String | 自定义 GroupConcatFunction,支持 DISTINCT、ORDER BY、SEPARATOR |
substring_index | String | MySQL SUBSTRING_INDEX(str, delim, count) |
json_extract | String | MySQL JSON_EXTRACT |
json_unquote | String | MySQL JSON_UNQUOTE |
json_contains | String | MySQL JSON_CONTAINS |
regexp | Integer | ?1 regexp ?2 |
convert | String | convert(?1 using ?2) |
find_in_set | Integer | find_in_set(?1, ?2) |
replace | String | replace(?1, ?2, ?3) |
ifnull | String | ifnull(?1, ?2) |
date_format | String | date_format(?1, ?2) |
Snowflake / JDK ID 生成器
com.jeeapp.jpa.hibernate.id.SnowflakeGenerator:标准 Snowflake(41bit 时间戳 + 5+5bit 数据中心/工作机 + 12bit 序列),起始 epoch=2016-01-01。dataCenterId默认根据本机 MAC 推导,workerId默认根据dataCenterId+ JVM PID 哈希推导,也可通过parameters显式传入。com.jeeapp.jpa.hibernate.id.JdkIdGenerator:基于 JDKUUID/ 时间戳的简单 ID 生成器。
@Entity
public class User {
@Id
@GeneratedValue(generator = "snowflake")
@GenericGenerator(name = "snowflake", strategy = "com.jeeapp.jpa.hibernate.id.SnowflakeGenerator")
private Long id;
}
条件式 Envers 审计
com.jeeapp.jpa.hibernate.envers.ConditionalEnversIntegrator 替换默认 Envers 监听器,在保留全部 Envers 行为的前提下,允许在运行时跳过特定操作的审计写入。
启用方式(替换默认 Envers 监听):
spring.jpa.properties.hibernate.listeners.envers.autoRegister=false
spring.jpa.properties.hibernate.integrator_provider=com.jeeapp.jpa.hibernate.envers.EnversIntegratorProvider
通过 com.jeeapp.jpa.hibernate.envers.AuditContextHolder 控制:
// 在线程内整体跳过审计
AuditContextHolder.runWithoutAudit(() -> {
return userRepository.saveAll(users); // 这些写操作不会产生 _AUD 记录
});
// 在已有事务中按对象粒度跳过
AuditContextHolder.markAsNoAudit(user);
userRepository.save(user);
事务结束时 AuditContextHolder 会通过 TransactionSynchronization#afterCompletion 自动清理标记。
Querydsl 仓库扩展
com.jeeapp.jpa.querydsl.QuerydslRepositoryCustom 继承 QuerydslRepositorySupport,提供常用便捷方法:
getRequiredQuerydsl()/getRequiredEntityManager():在父类 lazy 字段为 null 时给出明确异常findSlice(Pageable, JPQLQuery):基于pageSize + 1探测的Slice实现,避免Page的count开销getSession():直接拿到底层 HibernateSession
同时自动装配(无需手写):
JPAQueryFactoryBeanHibernateQueryFactoryBean(基于Hibernate5Templates)
RSQL → Querydsl 转换
引入 rsql-parser 后,com.jeeapp.jpa.querydsl.rsql.QuerydslRsqlPredicateBuilder(也即 QuerydslRsqlPredicateConverter)会自动注册:
- 支持运算符:
=eq=/=ne=/=gt=/=ge=/=lt=/=le=/=in=/=out=/=null=/=nonnull=/=like=(=contains=) /=notLike=(=notContains=) /=between=/=notBetween=,并且兼容缩写符号==/!=/>/>=/</<= - 支持嵌套属性路径与集合
any() - 通过
ConversionService完成参数类型转换
@Autowired
private QuerydslRsqlPredicateBuilder builder;
Predicate predicate = builder.getPredicate("name=like=tom;age=gt=18", User.class);
List<User> users = (List<User>) userRepository.findAll(predicate);
辅助转换器:
JacksonRsqlConverter:把任意 POJO 转成 RSQL 字符串JsonRsqlConverter:把 JSONMap转成 RSQL 字符串NoOpRsqlConverter:透传
QuerydslRsqlPredicateBuilder/QuerydslRsqlPredicateConverter已标注@Deprecated,未来版本将以新的转换实现替代,但当前仍可正常使用。
Hibernate5 Jackson 模块
自动注册一个适合 Web 场景的 Hibernate5Module:
FORCE_LAZY_LOADING=disabled:不强制触发懒加载USE_TRANSIENT_ANNOTATION=disabled:不把@Transient视为忽略提示WRAP_IDENTIFIER_IN_OBJECT=disabled+SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS=enabled:未加载的关联只输出主键REPLACE_PERSISTENT_COLLECTIONS=enabled:用普通集合替换 PersistentBag 等WRITE_MISSING_ENTITIES_AS_NULL=enabled:找不到的关联实体输出为null
Page / Slice / QueryResults 序列化
com.jeeapp.jpa.support.JpaJacksonModule(@JsonComponent 自动加载):
Page/Slice序列化为{ "content": [...], "total": <long> | "hasNext": <bool> },丢弃pageable/last/size/number等冗余字段- Querydsl 的
QueryResults同样输出{ content, total } Sort反序列化支持数组形式:[ { "property": "name", "direction": "ASC" } ],同时提供对称的序列化器