SpringBoot+Kotlin+Postgres和JSONB: "org.hibernate.MappingException: No Dialect mapping for JDBC type"

4

为了解决在运行Kotlin/SpringBoot应用程序时出现以下错误(完整的堆栈跟踪),我参考了一些方法/帖子/stackoverflow问题:

2020-04-22 18:33:56.823 ERROR 46345 --- [  restartedMain] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: No Dialect mapping for JDBC type: 2118910070
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1803)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1108)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:868)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
    at app.ApplicationKt.main(Application.kt:13)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: No Dialect mapping for JDBC type: 2118910070
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:403)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:378)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1862)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1799)
    ... 21 common frames omitted
Caused by: org.hibernate.MappingException: No Dialect mapping for JDBC type: 2118910070
    at org.hibernate.dialect.TypeNames.get(TypeNames.java:71)
    at org.hibernate.dialect.TypeNames.get(TypeNames.java:103)
    at org.hibernate.dialect.Dialect.getTypeName(Dialect.java:369)
    at org.hibernate.mapping.Column.getSqlType(Column.java:238)
    at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateColumnType(AbstractSchemaValidator.java:156)
    at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateTable(AbstractSchemaValidator.java:143)
    at org.hibernate.tool.schema.internal.GroupedSchemaValidatorImpl.validateTables(GroupedSchemaValidatorImpl.java:42)
    at org.hibernate.tool.schema.internal.AbstractSchemaValidator.performValidation(AbstractSchemaValidator.java:89)
    at org.hibernate.tool.schema.internal.AbstractSchemaValidator.doValidation(AbstractSchemaValidator.java:68)
    at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:192)
    at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:73)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:320)
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:462)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1249)
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:391)
    ... 25 common frames omitted

问题在于如何使用Hibernate映射PostgreSQL的JSONB数据类型。我已经广泛尝试并调试了以下两种方法:
1. 实现自定义Hibernate映射,并创建一个JSONB的自定义UserType。参考:这里这里这里这里
2. 使用Hibernate类型。参考:这里这里这里
我已经尝试了两种方法,但都没有成功,我很想了解我的问题出在哪里以及我错过了什么。接下来是我的实体:
@Entity
@TypeDef(name = "JsonUserType", typeClass = JsonUserType::class)
@Table(name = "entity")
data class MyEntity(
  @Column(nullable = false)
  val id: UUID,
  @Column(nullable = false)
  @Enumerated(value = EnumType.STRING)
  @Column(nullable = false)
  val type: Type,
  @Type(type = "JsonUserType")
  @Column(columnDefinition = "jsonb")
  @Basic(fetch = FetchType.LAZY)
  var event_data: Event
) : SomeEntity<UUID>(), SomeOtherStuff {
  override fun getName(): String {
    return id
  }
}
 
 
enum class Type(val value: String) {
  TYPE1("Type1"),
  TYPE2("Type2")
}

我的POJO:

data class Event(
  val someContent: String,
  val someBoolean: Boolean
) : Serializable { //equals, hashcode etc are omitted }

我自定义的Hibernate方言:

class CustomPostgreSQLDialect : PostgreSQL95Dialect {
  constructor() : super() {
    this.registerColumnType(Types.JAVA_OBJECT, "jsonb")
  }
}

我的自定义类型(抽象类)

abstract class JsonDataUserType : UserType {

  override fun sqlTypes(): IntArray? {
    return intArrayOf(Types.JAVA_OBJECT)
  }

  override fun equals(value1: Any?, value2: Any?): Boolean {
    return value1 == value2
  }

  override fun hashCode(value1: Any?): Int {
    return value1!!.hashCode()
  }

  override fun assemble(value1: Serializable?, value2: Any?): Any {
    return deepCopy(value1)
  }

  override fun disassemble(value1: Any?): Serializable {
    return deepCopy(value1) as Serializable
  }

  override fun deepCopy(p0: Any?): Any {
    return try {
      val bos = ByteArrayOutputStream()
      val oos = ObjectOutputStream(bos)
      oos.writeObject(p0)
      oos.flush()
      oos.close()
      bos.close()
      val bais = ByteArrayInputStream(bos.toByteArray())
      ObjectInputStream(bais).readObject()
    } catch (ex: ClassNotFoundException) {
      throw HibernateException(ex)
    } catch (ex: IOException) {
      throw HibernateException(ex)
    }
  }

  override fun replace(p0: Any?, p1: Any?, p2: Any?): Any {
    return deepCopy(p0)
  }

  override fun nullSafeSet(p0: PreparedStatement?, p1: Any?, p2: Int, p3: SharedSessionContractImplementor?) {
    if (p1 == null) {
      p0?.setNull(p2, Types.OTHER)
      return
    }
    try {
      val mapper = ObjectMapper()
      val w = StringWriter()
      mapper.writeValue(w, p1)
      w.flush()
      p0?.setObject(p2, w.toString(), Types.OTHER)
    } catch (ex: java.lang.Exception) {
      throw RuntimeException("Failed to convert Jsonb to String: " + ex.message, ex)
    }
  }
  override fun nullSafeGet(p0: ResultSet?, p1: Array<out String>?, p2: SharedSessionContractImplementor?, p3: Any?): Any {
    val cellContent = p0?.getString(p1?.get(0))
    return try {
      val mapper = ObjectMapper()
      mapper.readValue(cellContent?.toByteArray(charset("UTF-8")), returnedClass())
    } catch (ex: Exception) {
      throw RuntimeException("Failed to convert String to Jsonb: " + ex.message, ex)
    }
  }

  override fun isMutable(): Boolean {
    return true
  }

}

这个类是从这个Stackoverflow问题中获得的。

我的具体类:

class JsonType : JsonDataUserType() {
    override fun returnedClass(): Class<Event> {
      return Event::class.java
    }
}

我的 application.yml JPA Hibernate 属性

jpa.properties.database.database-platform: org.hibernate.dialect.PostgreSQL95Dialect
jpa.properties.hibernate.dialect: org.myapp.util.CustomPostgreSQLDialect

方法2

Hibernate 属性与普通Java对象类(PoJo class)完全一致,不包含自定义映射器。

实体

@Entity
@TypeDef(
  name = "jsonb",
  typeClass = JsonBinaryType::class
)
@Table(name = "entity")
data class MyEntity(
  @Column(nullable = false)
  val id: UUID,
  @Column(nullable = false)
  @Enumerated(value = EnumType.STRING)
  @Column(nullable = false)
  val type: Type,
  @Type(type = "jsonb")
  @Column(columnDefinition = "jsonb")
  @Basic(fetch = FetchType.LAZY)
  var event_data: Event
) : SomeEntity<UUID>(), SomeOtherStuff {
  override fun getName(): String {
    return id
  }
}
  
  
enum class Type(val value: String) {
  TYPE1("Type1"),
  TYPE2("Type2")
}

自定义方言(使用Hibernate类型):
class CustomPostgreSQLDialect : PostgreSQL95Dialect {
  constructor() : super() {
    this.registerHibernateType(Types.OTHER, JsonNodeBinaryType::class.java.name)
    this.registerHibernateType(Types.OTHER, JsonStringType::class.java.name)
    this.registerHibernateType(Types.OTHER, JsonBinaryType::class.java.name)
    this.registerHibernateType(Types.OTHER, JsonNodeBinaryType::class.java.name)
    this.registerHibernateType(Types.OTHER, JsonNodeStringType::class.java.name)
  }
}

请注意,我也尝试仅使用以下内容:
this.registerHibernateType(Types.OTHER, "jsonb")

除了将所有这些内容包含在我的实体或它所扩展的基础实体中(对此没有任何变化):

@TypeDefs({
    @TypeDef(name = "string-array", typeClass = StringArrayType.class),
    @TypeDef(name = "int-array", typeClass = IntArrayType.class),
    @TypeDef(name = "json", typeClass = JsonStringType.class),
    @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class),
    @TypeDef(name = "jsonb-node", typeClass = JsonNodeBinaryType.class),
    @TypeDef(name = "json-node", typeClass = JsonNodeStringType.class),
})

在这两种方法中,我有没有明显的错误?我无法让它工作,而且不确定是否有相关的因素,No Dialect mapping for JDBC type:后面的数字值始终不同。我在这里添加了一些ID,因为我看到某些ID与某些错误类别相关联。
你可以提供帮助吗?
谢谢
编辑: 我想提供更多关于jpa、postgres和hibernate版本的信息。我目前使用以下版本:
1. postgres:10-alpine 2. PostgreSQL JDBC Driver JDBC 4.2 » 42.2.8 3. org.springframework.boot:spring-boot-starter-data-jpa:2.2.1.RELEASE 4. org.hibernate:hibernate-core:5.4.8.Final
它们之间是否有特定的版本问题?
编辑2: 我一直在尝试成功地使用hibernate-types(如上所述的第2种方法)。根据Postgres版本(10),我进行了以下更改:
class CustomPostgreSQLDialect : PostgreSQL10Dialect {
  constructor() : super() {
    this.registerHibernateType(Types.OTHER, StringArrayType::class.java.name)
    this.registerHibernateType(Types.OTHER, IntArrayType::class.java.name)
    this.registerHibernateType(Types.OTHER, JsonStringType::class.java.name)
    this.registerHibernateType(Types.OTHER, JsonBinaryType::class.java.name)
    this.registerHibernateType(Types.OTHER, JsonNodeBinaryType::class.java.name)
    this.registerHibernateType(Types.OTHER, JsonNodeStringType::class.java.name)
  }
}

然后在我的实体中,我有

@TypeDefs({
        @TypeDef(name = "string-array", typeClass = StringArrayType.class),
        @TypeDef(name = "int-array", typeClass = IntArrayType.class),
        @TypeDef(name = "json", typeClass = JsonStringType.class),
        @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})

并且

 @Type(type = "jsonb")
 @Column(columnDefinition = "jsonb")
 @Basic(fetch = FetchType.LAZY)
 var event_data: Event

我已经调试了TypeNames中的get方法,找到了错误原因:

public String get(final int typeCode) throws MappingException {
        final Integer integer = Integer.valueOf( typeCode );
        final String result = defaults.get( integer );
        if ( result == null ) {
            throw new MappingException( "No Dialect mapping for JDBC type: " + typeCode );
        }
        return result;
    }

这就是我得到的内容:

defaults = {HashMap@12093}  size = 27
     {Integer@12124} -1 -> "text"
     {Integer@12126} 1 -> "char(1)"
     {Integer@12128} -2 -> "bytea"
     {Integer@12130} 2 -> "numeric($p, $s)"
     {Integer@12132} -3 -> "bytea"
     {Integer@12133} -4 -> "bytea"
     {Integer@12134} 4 -> "int4"
     {Integer@12136} -5 -> "int8"
     {Integer@12138} -6 -> "int2"
     {Integer@12140} 5 -> "int2"
     {Integer@12141} -7 -> "bool"
     {Integer@12143} 6 -> "float4"
     {Integer@12145} 7 -> "real"
     {Integer@12147} 8 -> "float8"
     {Integer@12149} -9 -> "nvarchar($l)"
     {Integer@12151} 12 -> "varchar($l)"
     {Integer@12153} -15 -> "nchar($l)"
     {Integer@12155} -16 -> "nvarchar($l)"
     {Integer@12156} 16 -> "boolean"
     {Integer@12158} 2000 -> "json"
     {Integer@12160} 2004 -> "oid"
     {Integer@12162} 2005 -> "text"
     {Integer@12163} 1111 -> "uuid"
     {Integer@12165} 91 -> "date"
     {Integer@12167} 2011 -> "nclob"
     {Integer@12169} 92 -> "time"
     {Integer@12171} 93 -> "timestamp"

找不到任何jsonb,当我调试自定义方言时,我得到了以下信息:

{Integer@10846} 1111 -> "com.vladmihalcea.hibernate.type.json.JsonStringType"
 key = {Integer@10846} 1111
 value = "com.vladmihalcea.hibernate.type.json.JsonStringType"

为什么呢?为什么我没有得到jsonb类型?

使用Postgres的jsonb和Hibernate最简单的方法是使用此问题中显示的库和示例:https://dev59.com/WFUM5IYBdhLWcg3wF9PZ#49326794 - peterzinho16
@peterzinho16,我正在遵循引用来源的方法(意思是我没有创建自定义UserType,放弃了那种方法),但仍然存在相同的问题。我已经更新了我的帖子,并进行了一些额外的调试。你有任何想法为什么这仍然不起作用吗?谢谢。 - panza
是的,这很有道理。我会尽快给您展示一个小演示。 - panza
嘿@dmytro-chasovskyi,我更新了一个答案。 - peterzinho16
@DmytroChasovskyi 我已经在这里放置了一个非常简单的工作项目(https://github.com/ilacorda/demo)。它非常基础,我需要添加README等文件,但它已经尽可能接近了(我的实体所继承的基本实体有点不同)。我不确定这是否能提供更好的见解,我仍然摸索中。 - panza
显示剩余3条评论
2个回答

4
我建议在拉取请求中提出我的解决方案。
思路是将实体更改为:
import com.example.demo.pojo.SamplePojo
import com.vladmihalcea.hibernate.type.json.JsonBinaryType
import com.vladmihalcea.hibernate.type.json.JsonStringType
import org.hibernate.annotations.Type
import org.hibernate.annotations.TypeDef
import org.hibernate.annotations.TypeDefs
import javax.persistence.*

@Entity
@Table(name = "tests")
@TypeDefs(
        TypeDef(name = "json", typeClass = JsonStringType::class),
        TypeDef(name = "jsonb", typeClass = JsonBinaryType::class)
)
data class SampleEntity (
    @Id @GeneratedValue
    val id: Long?,
    val name: String?,

    @Type(type = "jsonb")
    @Column(columnDefinition = "jsonb")
    var data: Map<String, Any>?
) {

    /**
     * Dependently on use-case this can be done differently:
     * https://dev59.com/hFoU5IYBdhLWcg3wCz2X
     */
    constructor(): this(null, null, null)
}
  1. 每个实体都应该有一个默认构造函数,或者为其所有参数提供默认值。
  2. 保存数据时,应该将 POJO 对象保存成 Map<String, Any> 类型。

由于我们可以完全控制业务逻辑中 POJO 中的内容,唯一缺失的部分是将 POJO 转换为 Map 和将 Map 转换为 POJO。

SamplePojo 实现

data class SamplePojo(
        val payload: String,
        val flag: Boolean
)  {
    constructor(map: Map<String, Any>) : this(map["payload"] as String, map["flag"] as Boolean)

    fun toMap() : Map<String, Any> {
        return mapOf("payload" to payload, "flag" to flag)
    }
}

这是一种解决方法,但它允许我们处理任何深度级别的结构。
附注:我注意到您使用了Serializer并重新定义了equals,toString,hashCode。如果使用data class,则不需要这些内容。
更新:
如果您需要比Map<String, Any>更灵活的结构,则可以使用JsonNode代码示例 实体:
import com.fasterxml.jackson.databind.JsonNode
import com.vladmihalcea.hibernate.type.json.JsonBinaryType
import com.vladmihalcea.hibernate.type.json.JsonStringType
import org.hibernate.annotations.Type
import org.hibernate.annotations.TypeDef
import org.hibernate.annotations.TypeDefs
import javax.persistence.*

@Entity
@Table(name = "tests")
@TypeDefs(
        TypeDef(name = "json", typeClass = JsonStringType::class),
        TypeDef(name = "jsonb", typeClass = JsonBinaryType::class)
)
data class SampleJsonNodeEntity (
        @Id @GeneratedValue
        val id: Long?,
        val name: String?,

        @Type(type = "jsonb")
        @Column(columnDefinition = "jsonb")
        var data: JsonNode?
) {

    /**
     * Dependently on use-case this can be done differently:
     * https://dev59.com/hFoU5IYBdhLWcg3wCz2X
     */
    constructor(): this(null, null, null)
}

在仓库中更改实体:

import com.example.demo.entity.SampleJsonNodeEntity
import org.springframework.data.jpa.repository.JpaRepository

interface SampleJsonNodeRepository: JpaRepository<SampleJsonNodeEntity, Long> {
}

两种方法的测试:

import com.example.demo.DbTestInitializer
import com.example.demo.entity.SampleJsonNodeEntity
import com.example.demo.entity.SampleMapEntity
import com.example.demo.pojo.SamplePojo
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.SpringRunner


@RunWith(SpringRunner::class)
@SpringBootTest
@ContextConfiguration(initializers = [DbTestInitializer::class])
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class SampleRepositoryTest {

    @Autowired
    lateinit var sampleMapRepository: SampleMapRepository

    @Autowired
    lateinit var sampleJsonNodeRepository: SampleJsonNodeRepository

    lateinit var dto: SamplePojo
    lateinit var mapEntity: SampleMapEntity
    lateinit var jsonNodeEntity: SampleJsonNodeEntity

    @Before
    fun setUp() {
        dto = SamplePojo("Test", true)
        mapEntity = SampleMapEntity(null,
                "POJO1",
                dto.toMap()
        )

        jsonNodeEntity = SampleJsonNodeEntity(null,
            "POJO2",
                jacksonObjectMapper().valueToTree(dto)
        )
    }

    @Test
    fun createMapPojo() {
        val id = sampleMapRepository.save(mapEntity).id!!
        assertNotNull(sampleMapRepository.getOne(id))
        assertEquals(sampleMapRepository.getOne(id).data?.let { SamplePojo(it) }, dto)
    }

    @Test
    fun createJsonNodePojo() {
        val id = sampleJsonNodeRepository.save(jsonNodeEntity).id!!
        assertNotNull(sampleJsonNodeRepository.getOne(id))
        assertEquals(jacksonObjectMapper().treeToValue(sampleJsonNodeRepository.getOne(id).data, SamplePojo::class.java), dto)
    }

}

你好,感谢您对此的付出,非常感激。我已经快速实现了更改(尚未进行测试),但仍然遇到了同样的问题。在编写一些测试之前,我有几个问题。我们是否要删除自定义方言?我还没有发布我的DTO和转换器,但我应该在那里有toMap方法吗? - panza
一个自定义的方言应该保留。是的,基本上你有将dto转换为map和反之亦然的方法,我在SamplePojo中展示了可能的解决方案。另外,我在你的代码中看到的问题之一是你使用了自定义序列化器,请尽量让对象变得愚蠢,以确保这是Hibernate的谬论。顺便说一下,我在Java 11和Postgres 11中测试了代码。如果对你来说什么都不起作用,那就从我的代码示例开始,因为它既可以作为独立运行,也可以作为测试。 - Dmytro Chasovskyi
你好,虽然你的代码按预期工作,但不幸的是,我的应用程序无法很好地处理Map中的Any。我不确定该如何处理它。有什么想法吗? - panza
你不想在我提交的PR下开一个单独的讨论吗?这样我就可以看到问题所在,你也可以提交代码示例和详细信息。我认为Map问题超出了此讨论的范围。 - Dmytro Chasovskyi
我同意,我在你的PR中留下了评论。稍后会更新我的演示,谢谢你的所有工作,尽管我的实际应用程序有点复杂,但我认为你的解决方案值得奖励。 - panza

3

举个例子来扩展,抱歉我知道我有点晚了

在您的pom.xml文件中:

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

然后我有一个名为“Day”的实体:

import com.vladmihalcea.hibernate.type.json.JsonBinaryType;

@TypeDefs({
        @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
@Data
@Entity
public class Day {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "DayId")
   private Integer id;
   private Integer day;
   private Integer month;
   private Integer year;

   @Type(type = "jsonb")
   @Column(columnDefinition = "jsonb")
   private List<Activity> activities;

   @Type(type = "jsonb")
   @Column(columnDefinition = "jsonb")
   private Notification notification;

}

活动和通知的 JSONB 类:

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Activity implements Serializable {

   private String name;
   private String emoji;
   private Integer durationInSeconds;
   private Boolean highPriority;

   public Activity (){}
}

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Notification implements Serializable {

    private String email;
    private String mobile;

    public Notification (){}
}

我们的代码库:

@Repository
public interface DayRepository extends CrudRepository<Day, Integer> {

}

我们的服务:
public interface DayService{
    Day saveArbitraryDay();
}

@Service
@Transactional
public DayServiceImpl implements DayService{

    private DayRepository repository;

    public DayServiceImpl(DayRepository repository){
         this.repository = repository;
    }

    @Override
    public Day saveArbitraryDay(){
         Day day = new Day();
         day.setDay(16);
         day.setMonth(04);
         day.setYear(1991);

         //Set the jsonb objects
         //You can use custom constructors whatever
         Notification notification = new Notification();
         notification.setEmail("contoso@hotmail.com");
         day.setNotification(notification);

         //Now putting activities
         List<Activity> activities = new ArrayList<>();

         Activity actOne = new Activity();
         actOne.setName("Breakfast");
         actOne.setEmoji("");
         actOne.setDurationInSeconds(9000);
         actOne.setHighPriority(true);

         Activity actTwo = new Activity();
         actTwo.setName("Shopping");
         actTwo.setEmoji("");

         activities.add(actOne);
         activities.add(actTwo);

         day.setActivities(activities)

         return repository.save(day);
    }
}

我认为这就是全部内容了,如果您想深入了解Hibernate中的类型,请查看此链接


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接