1. 概述

在某些项目中,我们可能需要将 JSON 对象存储到关系型数据库中。

本篇文章将介绍几种方式,帮助我们将 JSON 对象 持久化到关系型数据库中

虽然有很多框架支持这一功能,但本文将聚焦于一些简单、通用的方案,使用 HibernateJackson 以及 Hypersistence Utils 库来实现。

2. 依赖项

本教程使用 Hibernate Core 作为基础依赖:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.4.24.Final</version>
</dependency>

同时使用 Jackson 作为 JSON 处理库:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.3</version>
</dependency>

⚠️ 注意:这些方法并不局限于这两个库,你可以自由替换为其他 JPA 实现或 JSON 库。

3. 序列化与反序列化方法

最基础的做法是:在持久化前将 JSON 对象转换为字符串,读取时再将其转换回对象。

我们可以通过以下方式实现:

  • 自定义序列化和反序列化方法

我们以一个 Customer 实体为例,包含姓名和一些客户属性(用 HashMap 表示):

@Entity
@Table(name = "Customers")
public class Customer {

    @Id
    private int id;

    private String firstName;

    private String lastName;

    private String customerAttributeJSON;

    @Convert(converter = HashMapConverter.class)
    private Map<String, Object> customerAttributes;
}

我们选择将客户属性以 JSON 字符串的形式存储在 customerAttributeJSON 字段中,而不是使用关联表。✅ 这样可以简化数据库结构并提升查询性能。

3.1 序列化方法

public void serializeCustomerAttributes() throws JsonProcessingException {
    this.customerAttributeJSON = objectMapper.writeValueAsString(customerAttributes);
}

你可以在每次设置属性时自动调用该方法,或手动在持久化前调用。

3.2 反序列化方法

public void deserializeCustomerAttributes() throws IOException {
    this.customerAttributes = objectMapper.readValue(customerAttributeJSON, 
        new TypeReference<Map<String, Object>>() {});
}

3.3 使用示例

@Test
public void whenStoringAJsonColumn_thenDeserializedVersionMatches() {
    Customer customer = new Customer();
    customer.setFirstName("first name");
    customer.setLastName("last name");

    Map<String, Object> attributes = new HashMap<>();
    attributes.put("address", "123 Main Street");
    attributes.put("zipcode", 12345);

    customer.setCustomerAttributes(attributes);
    customer.serializeCustomerAttributes();

    String serialized = customer.getCustomerAttributeJSON();

    customer.setCustomerAttributeJSON(serialized);
    customer.deserializeCustomerAttributes();

    assertEquals(attributes, customer.getCustomerAttributes());
}

4. 属性转换器(Attribute Converter)

如果你使用的是 JPA 2.1 或更高版本,可以使用 AttributeConverter 来简化这个过程。

4.1 创建转换器

public class HashMapConverter implements AttributeConverter<Map<String, Object>, String> {

    @Override
    public String convertToDatabaseColumn(Map<String, Object> customerInfo) {
        try {
            return objectMapper.writeValueAsString(customerInfo);
        } catch (final JsonProcessingException e) {
            logger.error("JSON 写入失败", e);
            return null;
        }
    }

    @Override
    public Map<String, Object> convertToEntityAttribute(String customerInfoJSON) {
        try {
            return objectMapper.readValue(customerInfoJSON, 
                new TypeReference<HashMap<String, Object>>() {});
        } catch (final IOException e) {
            logger.error("JSON 读取失败", e);
            return null;
        }
    }
}

4.2 应用转换器

@Convert(converter = HashMapConverter.class)
private Map<String, Object> customerAttributes;

✅ 使用这种方式,Hibernate 会自动完成序列化与反序列化,无需手动调用方法。

5. 使用 Hypersistence Utils 库

Hypersistence Utils 是一个轻量级库,支持将 JSON 映射到多种数据库(如 H2、MySQL、Oracle 等)。

5.1 添加依赖

<dependency>
    <groupId>io.hypersistence</groupId>
    <artifactId>hypersistence-utils-hibernate-55</artifactId>
    <version>3.8.1</version>
</dependency>

⚠️ 注意:Hibernate 版本不同需使用对应的依赖,如 Hibernate 6 使用 hypersistence-utils-hibernate-60

5.2 实体示例

@Entity
@TypeDef(name = "json", typeClass = JsonType.class)
public class Warehouse {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    private String location;

    @Type(type = "json")
    @Column(columnDefinition = "json")
    private Map<String, String> attributes = new HashMap<>();
}

✅ 使用 @TypeDef 定义类型别名,@Type 引用该类型,即可实现自动映射。

6. 使用 @JdbcTypeCode 注解(Hibernate 6+)

Hibernate 6 引入了 @JdbcTypeCode 注解,用于指定字段的 JDBC 类型。

6.1 示例

@Entity
public class Store {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

    private String location;

    @JdbcTypeCode(SqlTypes.JSON)
    private Map<String, String> attributes = new HashMap<>();
}

✅ 无需自定义转换器,也无需外部库,直接使用该注解即可实现 JSON 映射。

7. 将 JSON 映射到 @Embeddable 类(Hibernate 6+)

从 Hibernate 6 开始,我们可以将 JSON 映射到嵌入类(@Embeddable)中。

⚠️ 目前仅支持 Oracle 和 PostgreSQL。

7.1 嵌入类定义

@Embeddable
public class Specification implements Serializable  {
    private int ram;
    private int internalMemory;
    private String processor;
    // 构造器、getter、setter 省略
}

7.2 实体类中使用

@Entity
public class Phone {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @Embedded
    @JdbcTypeCode(SqlTypes.JSON)
    private Specification specification;
}

✅ 使用 @Embedded + @JdbcTypeCode(SqlTypes.JSON) 实现嵌入类的 JSON 映射。

8. 总结

本文介绍了多种使用 Hibernate 持久化 JSON 对象的方法:

  • 手动序列化与反序列化
  • 使用 AttributeConverter 简化流程
  • 利用 Hypersistence Utils 库
  • Hibernate 6 的 @JdbcTypeCode 注解
  • 嵌入类的 JSON 映射(仅部分数据库支持)

✅ 推荐使用 @JdbcTypeCode 或 Hypersistence 库,代码更简洁,维护成本更低。

如需源码,请访问 GitHub 项目地址


原始标题:Persist a JSON Object Using Hibernate | Baeldung