sql >> データベース >  >> RDS >> PostgreSQL

SpringBoot + Kotlin + PostgresおよびJSONB:org.hibernate.MappingException:JDBCタイプのダイアレクトマッピングがありません

    プルリクエストでソリューションを提案します

    アイデアは、エンティティを次のように変更することです:

    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://stackoverflow.com/questions/37873995/how-to-create-empty-constructor-for-data-class-in-kotlin-android
         */
        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)
        }
    }
    

    これはかなり回避策ですが、任意の深度レベルの構造で作業することができます。

    P.S. 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://stackoverflow.com/questions/37873995/how-to-create-empty-constructor-for-data-class-in-kotlin-android
         */
        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)
        }
    
    }
    


    1. 新しいOracleユーザーを作成して権限を付与する:構文と例

    2. FROM句で更新対象のテーブルを指定することはできません

    3. Golangで*DB.exec()またはプリペアドステートメントを使用するのはなぜですか?

    4. OracleSQLクエリのログ