跳到主要内容

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(自定义函数)与 TypesContributorJsonType)的触发开关,未设置时这两个扩展不会注入。
  • 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_PROPERTIESDEFAULT_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.MySQLFunctionsContributorspring.jpa.database=MYSQL 时自动通过 MetadataBuilderContributor 注册以下函数,可直接在 JPQL / Criteria / Querydsl Expressions.function(...) 中使用:

函数返回类型说明
group_concatString自定义 GroupConcatFunction,支持 DISTINCTORDER BYSEPARATOR
substring_indexStringMySQL SUBSTRING_INDEX(str, delim, count)
json_extractStringMySQL JSON_EXTRACT
json_unquoteStringMySQL JSON_UNQUOTE
json_containsStringMySQL JSON_CONTAINS
regexpInteger?1 regexp ?2
convertStringconvert(?1 using ?2)
find_in_setIntegerfind_in_set(?1, ?2)
replaceStringreplace(?1, ?2, ?3)
ifnullStringifnull(?1, ?2)
date_formatStringdate_format(?1, ?2)

Snowflake / JDK ID 生成器

  • com.jeeapp.jpa.hibernate.id.SnowflakeGenerator:标准 Snowflake(41bit 时间戳 + 5+5bit 数据中心/工作机 + 12bit 序列),起始 epoch=2016-01-01dataCenterId 默认根据本机 MAC 推导,workerId 默认根据 dataCenterId + JVM PID 哈希推导,也可通过 parameters 显式传入。
  • com.jeeapp.jpa.hibernate.id.JdkIdGenerator:基于 JDK UUID / 时间戳的简单 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 实现,避免 Pagecount 开销
  • getSession():直接拿到底层 Hibernate Session

同时自动装配(无需手写):

  • JPAQueryFactory Bean
  • HibernateQueryFactory Bean(基于 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:把 JSON Map 转成 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" } ],同时提供对称的序列化器

相关资源