当项目同时使用 MySQL 和 PostgreSQL 时,从 JsonStringType 切换到 JsonBinaryType

Switch from JsonStringType to JsonBinaryType when the project uses both MySQL and PostgreSQL


当需要从 PostgreSQL 切换到 MariaDB/MySql.
时,列 json 出现问题 我使用 Spring Boot + JPA + Hibernate + hibernate-types-52.
我要映射的table是这样的:

CREATE TABLE atable(
 ...
 acolumn JSON,
 ... 
);

好的,它适用于 PostgreSQL 和 MariaDB/MySql。
问题是当我想部署一个可以轻松从一个应用程序切换到另一个应用程序时,因为 PostgreSQL 和 MySQL/MariaDB 的正确 hibernate-types-52 实现是不同的

这适用于 MySQL/MariaDB

@Entity
@Table(name = "atable")
@TypeDef(name = "json", typeClass = JsonStringType.class)
  public class Atable {
  ...
  @Type(type = "json")
  @Column(name = "acolumn", columnDefinition = "json")
  private JsonNode acolumn;
  ...
}

这适用于 PosgreSQL

@Entity
@Table(name = "atable")
@TypeDef(name = "json", typeClass = JsonBinaryType.class)
public class Atable {
  ...
  @Type(type = "json")
  @Column(name = "acolumn", columnDefinition = "json")
  private JsonNode acolumn;
  ...
}

任何类型的从 JsonBinaryType 切换到 JsonStringType 的解决方案(或解决此问题的任何其他解决方案)都值得赞赏。

从 Hibernate Types 项目的 2.11 版本开始,您可以只使用 JsonType,它适用于 PostgreSQL、MySQL、Oracle、SQL服务器,或H2.

因此,使用 JsonType 而不是 JsonBinaryTypeJsonStringType

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

  @Type(type = "json")
  @Column(name = "acolumn", columnDefinition = "json")
  private JsonNode acolumn;

}

就是这样!

您可以做一些疯狂的事情 - 限制是这仅适用于特定类型和列:

首先,用动态映射替换静态@TypeDef

你可以用一个HibernatePropertiesCustomizer加一个TypeContributorList:

@Configuration
public class HibernateConfig implements HibernatePropertiesCustomizer {

  @Value("${spring.jpa.database-platform:}")
  private Class<? extends Driver> driverClass;

  @Override
  public void customize(Map<String, Object> hibernateProperties) {

    AbstractHibernateType<Object> jsonType;
    if (driverClass != null && PostgreSQL92Dialect.class.isAssignableFrom(driverClass)) {
      jsonType = new JsonBinaryType(Atable.class);
    } else {
      jsonType = new JsonStringType(Atable.class);
    }

    hibernateProperties.put(EntityManagerFactoryBuilderImpl.TYPE_CONTRIBUTORS,
        (TypeContributorList) () -> List.of(
            (TypeContributor) (TypeContributions typeContributions, ServiceRegistry serviceRegistry) ->
                typeContributions.contributeType(jsonType, "myType")));
  }
}

所以这仅限于 Atable.class,我将此自定义命名为 Json-Type 'myType'。即,您用 @Type(type = 'myType').

注释 属性

我在这里使用配置的方言,但在我的应用程序中,我正在检查活动配置文件以获取特定于数据库的配置文件。

另请注意,自 Hibernate 5.3 以来,TypeContributions .contributeType(BasicType, String...) 已弃用。我还没有研究新机制。

所以这涵盖了 @Type 部分,但是如果你想使用 Hibernate Schema 生成,你仍然需要 @Column(columnDefinition = "... 位,所以 Hibernate 知道要使用哪种列类型。

从这里开始感觉有点恶心。我们可以注册一个 Integrator 来操作映射元数据:

hibernateProperties.put(EntityManagerFactoryBuilderImpl.INTEGRATOR_PROVIDER,
            (IntegratorProvider) () -> Collections.singletonList(JsonColumnMappingIntegrator.INSTANCE));

作为演示,我仅检查 PostgreSQL,并且仅将动态 columnDefinition 应用于特定实体中的特定列:

public class JsonColumnMappingIntegrator implements Integrator {

  public static final JsonColumnMappingIntegrator INSTANCE =
      new JsonColumnMappingIntegrator();

  @Override
  public void integrate(
      Metadata metadata,
      SessionFactoryImplementor sessionFactory,
      SessionFactoryServiceRegistry serviceRegistry) {

    Database database = metadata.getDatabase();

    if (PostgreSQL92Dialect.class.isAssignableFrom(database.getDialect().getClass())) {
      Column acolumn=
        ((Column) metadata.getEntityBinding(Atable.class.getName()).getProperty("acolumn").getColumnIterator().next());
      settingsCol.setSqlType("json");
    }
  }

  @Override
  public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
  }
}

metadata.getEntityBindings() 将为您提供所有实体绑定,您可以对其进行迭代,然后对属性进行迭代。虽然这看起来效率很低。 我也不确定你是否可以设置 'IS JSON' 约束等,所以自定义创建脚本会更好。