1. 概述

本文将深入介绍 Hibernate Types 这个非常实用的第三方库。它的核心价值在于:✅ 扩展了 Hibernate ORM 原生不支持的数据类型,尤其是对 JSON、Java 8 时间类型等现代数据结构提供了开箱即用的支持。

简单粗暴地说,如果你在项目中遇到“这字段怎么存?”的踩坑场景,比如想把一个 POJO 直接存成数据库的 JSON 字段,这个库能让你少写一堆转换代码,直接搞定。


2. 依赖配置

要使用 Hibernate Types,只需引入对应的 Maven 依赖。根据你项目中 Hibernate 的版本选择合适的 artifact:

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>2.9.7</version>
</dependency>

⚠️ 注意版本匹配:

  • Hibernate 5.4 / 5.3 / 5.2 → hibernate-types-52
  • Hibernate 5.1 / 5.0 → hibernate-types-51
  • Hibernate 4.3 → hibernate-types-43
  • Hibernate 4.2 / 4.1 → hibernate-types-4

本文示例基于 MySQL 数据库。我们使用 Docker 快速启动一个测试环境(别担心,这不是重点):

$ ./create-database.sh

这个脚本会自动拉起一个 MySQL 容器,省去手动部署的麻烦。


3. 支持的数据库

该库支持主流数据库:

  • Oracle
  • SQL Server
  • PostgreSQL
  • MySQL

不同类型在不同数据库中的映射可能略有差异。本文以 MySQL 为例,使用 JSON 列类型存储 JSON 数据。

📌 详细类型映射表可参考官方 GitHub 仓库:https://github.com/vladmihalcea/hibernate-types


4. 数据模型设计

我们以音乐专辑(Album)和歌曲(Song)为例构建数据模型:

  • 一张专辑包含封面(CoverArt)和多首歌曲
  • 一首歌有长度、艺术家信息(Artist)
  • 封面包含前后图 URL 和 UPC 编码
  • 艺术家包含姓名、国家、流派

传统做法是建一堆关联表,但现在我们可以用 JSON 直接内嵌非核心数据,减少表数量和 JOIN 操作。

实体定义

public class Album extends BaseEntity {
    @Type(type = "json")
    @Column(columnDefinition = "json")
    private CoverArt coverArt;

    @OneToMany(fetch = FetchType.EAGER)
    private List<Song> songs;

    // getters and setters
}
public class Song extends BaseEntity {
    private Long length = 0L;

    @Type(type = "json")
    @Column(columnDefinition = "json")
    private Artist artist;

    // getters and setters
}

对应的值对象(非实体):

public class Artist implements Serializable {
    private String name;
    private String country;
    private String genre;
    // getters, setters, constructors
}
public class CoverArt implements Serializable {
    private String frontCoverArtUrl;
    private String backCoverArtUrl;
    private String upcCode;
    // getters, setters, constructors
}

✅ 关键点:

  • ArtistCoverArt 是普通 POJO,不是 JPA Entity
  • 通过 @Type(type = "json") 注解标记为 JSON 映射字段
  • 字段直接嵌入父实体,无需单独建表

4.1 JSON 类型注册

为了让 @Type("json") 生效,必须在实体基类或配置类中声明类型定义:

@TypeDefs({
    @TypeDef(name = "json", typeClass = JsonStringType.class),
    @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
public class BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    // common fields
}

📌 说明:

  • JsonStringType 对应 MySQL 的 JSON 列,底层通过字符串处理
  • JsonBinaryType 更适合 PostgreSQL 的 JSONB 类型
  • MySQL 虽然支持 JSON 列,但 JDBC 层仍以字符串形式传输,因此推荐使用 JsonStringType

4.2 持久化行为验证

当我们保存数据时,Hibernate 会自动将对象序列化为 JSON 字符串插入数据库。例如:

Song happySong = new Song();
happySong.setName("A Happy Song");
happySong.setArtist(new Artist("Superstar", "England", "Pop"));
happySong.setLength(240L);

Album album = new Album();
album.setName("Album 0");
album.setCoverArt(new CoverArt("http://fakeurl-0", "http://fakeurl-1", "b2b9b193-ee04-4cdc-be8f-3a276769ab5b"));
album.setSongs(Arrays.asList(happySong));
albumRepository.save(album);

生成的 SQL 类似:

insert into song (name, artist, length, id) 
values ('A Happy Song', '{"name":"Superstar","country":"England","genre":"Pop"}', 240, 3);

insert into album (name, cover_art, id) 
values ('Album 0', '{"frontCoverArtUrl":"http://fakeurl-0","backCoverArtUrl":"http://fakeurl-1","upcCode":"b2b9b193-ee04-4cdc-be8f-3a276769ab5b"}', 7);

✅ 结果:Java 对象被自动转为合法 JSON 存入数据库,查询时也能自动反序列化回来,完全透明。


5. 泛型类型支持

除了 JSON,该库还提供了对 java.time 包中非标准类型的持久化支持:

  • YearMonth
  • Year
  • Month

这些类型 Hibernate 原生不支持,但业务中很常见(比如“发布年月”)。Hibernate Types 提供了多种存储方式:

  • 存为 Integer(如 202405 表示 2024 年 5 月)
  • 存为 String(如 "2024-05")
  • 存为 Date(取当月第一天)

示例:使用 YearMonthIntegerType

@TypeDef(
    typeClass = YearMonthIntegerType.class,
    defaultForType = YearMonth.class
)
public class Song extends BaseEntity {

    @Column(name = "recorded_on", columnDefinition = "mediumint")
    private YearMonth recordedOn = YearMonth.now();

    // other fields
}

✅ 效果:

  • Java 层使用 YearMonth 类型,类型安全
  • 数据库存为整数(如 202405),节省空间且易索引
  • 无需手动转换逻辑,框架自动完成序列化/反序列化

6. 其他实用工具类

Hibernate Types 还提供了一些提升开发体验的辅助功能:

✅ CamelCaseToSnakeCaseNamingStrategy

自动将 Java 驼峰命名属性映射到数据库下划线命名字段。

例如:

  • coverArtcover_art
  • recordedOnrecorded_on

配置方式:

@NamingStrategy(CamelCaseToSnakeCaseNamingStrategy.class)
public class Album { ... }

✅ ClassImportIntegrator

允许在 JPQL 的 NEW 构造函数中直接使用类名,无需写全限定名。

✅ ListResultTransformer & MapResultTransformer

优化原生 SQL 查询结果的转换:

  • 支持 Lambda 表达式处理结果集
  • 向下兼容老版本 JPA
  • 返回干净的 List<Map<String, Object>> 或自定义 DTO 列表

这些工具类虽然不起眼,但在复杂查询和性能优化时能省不少事。


7. 总结

Hibernate Types 是一个轻量但极其实用的库,特别适合以下场景:

  • 需要存储 JSON 字段(MySQL/PostgreSQL)
  • 使用 YearMonth 等非标准时间类型
  • 想减少实体表数量,采用“宽表 + JSON 扩展字段”设计

✅ 优势:

  • 零侵入,只需加注解
  • 类型安全,自动序列化
  • 减少样板代码,提升开发效率

📌 建议:在微服务或中台项目中,面对灵活多变的业务字段时,搭配 JSON 类型使用效果更佳。

所有示例代码已托管至 GitHub: https://github.com/john-doe/tutorials/tree/master/persistence-modules/hibernate-libraries


原始标题:A Guide to the Hibernate Types Library