Johnny Alpha
Johnny Alpha

Reputation: 970

Postgres JSONB not working with Upgrade to SpringBoot Version 3.0

I have a Kotlin SpringBoot project. It's a relatively simple API that persists data to a Postgres JsonB database. I am using the @TypeDef annotation on my entity class, but after upgrading to SpringBoot Version 3.0 with hibernate-core:6.1.7.Final this annotation is no longer available.

Here is my entity class::

import com.vladmihalcea.hibernate.type.json.JsonBinaryType
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import org.hibernate.annotations.Type
import org.hibernate.annotations.TypeDef // not available with latest hibernate-core

import java.io.Serializable
import java.security.SecureRandom
import kotlin.math.abs

@Entity
@Table(name = "myTable")
@TypeDef(name = "jsonb", typeClass = JsonBinaryType::class) // not available :(
data class RecommendationEntity(
  @Id
  open var id: Long = abs(SecureRandom().nextInt().toLong()),
  @Type(type = "jsonb")
  @Column(columnDefinition = "jsonb")
  var data: MyModel
)

data class MyModel(
  // nothing special just a POJO
) : Serializable

And here is my build.gradle.kts::

plugins {
  id("XXXXXXXXXX.gradle-spring-boot") version "5.0.1" // in house plugin thatcopies 3.0.2
  kotlin("jvm") version "1.8.10"
  kotlin("plugin.jpa") version "1.8.20-Beta"
  kotlin("plugin.spring") version "1.8.20-Beta"
}

jacoco.toolVersion = "0.8.8"

configurations {
  testImplementation { exclude(group = "org.junit.vintage") }
}

testSets {
  "testSmoke"()
}

// allOpen {
//  annotations("javax.persistence.Entity")
// }

val springDocVersion = "1.6.14"

dependencies {

  implementation("org.springframework.boot:spring-boot-starter-web")
  implementation("org.springframework.boot:spring-boot-starter-webflux")
  implementation("org.springframework.boot:spring-boot-starter-data-jpa")
  implementation("org.springframework.boot:spring-boot-starter-security")
  implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
  implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
  implementation("org.springframework.boot:spring-boot-starter-actuator:3.0.1")

  implementation("com.deepoove:poi-tl:1.12.1") {
    // exclude apache.xmlgraphics batik due to vulnerabilities when imported with poi-tl
    exclude("org.apache.xmlgraphics", "batik-codec")
    exclude("org.apache.xmlgraphics", "batik-transcoder")
  }
  implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
  implementation("com.fasterxml.jackson.module:jackson-module-kotlin")

  implementation("org.flywaydb:flyway-core:9.11.0")
  implementation("org.postgresql:postgresql:42.5.1")
  
  implementation("org.springdoc:springdoc-openapi-webmvc-core:$springDocVersion")
  implementation("org.springdoc:springdoc-openapi-ui:$springDocVersion")
  implementation("org.springdoc:springdoc-openapi-kotlin:$springDocVersion")
  implementation("org.springdoc:springdoc-openapi-data-rest:$springDocVersion")

  implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
  implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")

  implementation("com.github.doyaaaaaken:kotlin-csv-jvm:1.7.0")
  implementation("com.vladmihalcea:hibernate-types-52:2.21.1")

//  implementation("org.hibernate:hibernate-core:5.6.3.Final")
  // https://mvnrepository.com/artifact/org.hibernate.orm/hibernate-core
  implementation("org.hibernate.orm:hibernate-core:6.1.7.Final")
  testImplementation("org.awaitility:awaitility-kotlin:4.2.0")
  testImplementation("org.mock-server:mockserver-netty:5.15.0")
  testImplementation("io.projectreactor:reactor-test")
  testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test")
  testImplementation("org.junit.jupiter:junit-jupiter-params")

  testImplementation("io.jsonwebtoken:jjwt:0.9.1")
  testImplementation("com.natpryce:hamkrest:1.8.0.1")
  testImplementation("org.flywaydb.flyway-test-extensions:flyway-spring-test:7.0.0")
  
}

java {
  toolchain {
    languageVersion.set(JavaLanguageVersion.of(18))
  }
}

tasks {
  withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
    kotlinOptions {
      jvmTarget = "18"
    }
  }
}

tasks.test {
  finalizedBy(tasks.jacocoTestReport)
}

tasks.jacocoTestReport {
  dependsOn(tasks.test)
  reports {
    xml.required.set(true)
  }
}

val SourceSet.kotlin: SourceDirectorySet
  get() = project.extensions.getByType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension>().sourceSets.getByName(
    name
  ).kotlin

sourceSets {
  create("functional-test") {
    kotlin.srcDirs("src/functional-test")
    compileClasspath += sourceSets["main"].output + configurations["testRuntimeClasspath"]
    runtimeClasspath += output + compileClasspath + sourceSets["test"].runtimeClasspath
  }
}

tasks.withType<Jar>() {
  duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

What I've tried::

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Class org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider does not implement the requested interface jakarta.persistence.spi.PersistenceProvider

I'm quite stumped on this one, and am beginning to think that SpringBoot V3.0 doesn't support Postgres JsonB Nosql :( Can anybody help me with a solution? Or perhaps confirm whether Postgres JsonB Nosql is indeed not supported in the new SpringBoot version? Thanks

Upvotes: 3

Views: 7775

Answers (1)

Johnny Alpha
Johnny Alpha

Reputation: 970

I found the solution here - https://github.com/vladmihalcea/hypersistence-utils/issues/367#issuecomment-1398737096

I bumped the version of my hibernate-types dependency implementation("com.vladmihalcea:hibernate-types-60:2.21.1")

And made changes to my entity class as recommended -

@Entity
@Table(name = "myTable")
// @TypeDef(name = "jsonb", typeClass = JsonBinaryType::class)
data class MyEntity(
  @Id
  open var id: Long = abs(SecureRandom().nextInt().toLong()),
//  @Type(type = "jsonb")
//  @Column(columnDefinition = "jsonb")
  @Type(JsonType::class)
  @Column(columnDefinition = "jsonb")
  var data: MyModel
) 

And now the issue is gone!

Upvotes: 12

Related Questions