Problem with storing LinkedHashMaps as json column

jackson serializer maintain order of linkedhash map as in arrays. this is by design assuming you go for json and not jsonb which is what we are trying to do. and i can not really move away from that due to backward competability issues

i was trying to follow your proposal above and wrote the following code, but it doesn’t seems that it is being used by hibernate although it is loaded properly by the service loader. any insights ?

ass JsonJdbcType : JdbcType {

    private val objectMapper = ObjectMapper().apply {
        registerModule(JavaTimeModule())
        registerKotlinModule()
        disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
        configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, false)
        configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
        setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
    }

    override fun getJdbcTypeCode(): Int = Types.OTHER

    override fun <X> getBinder(javaType: JavaType<X>): ValueBinder<X> {
        return object : BasicBinder<X>(javaType, this) {
            override fun doBind(st: PreparedStatement, value: X, index: Int, options: WrapperOptions) {
                val pgObject = PGobject().apply {
                    type = "json"  // Use "json" not "jsonb"
                    this.value = objectMapper.writeValueAsString(value)
                }
                st.setObject(index, pgObject)
            }

            override fun doBind(st: CallableStatement, value: X, name: String, options: WrapperOptions) {
                val pgObject = PGobject().apply {
                    type = "json"
                    this.value = objectMapper.writeValueAsString(value)
                }
                st.setObject(name, pgObject)
            }
        }
    }

    override fun <X> getExtractor(javaType: JavaType<X>): ValueExtractor<X> {
        return object : BasicExtractor<X>(javaType, this) {
            override fun doExtract(rs: ResultSet, paramIndex: Int, options: WrapperOptions): X? {
                val jsonString = rs.getString(paramIndex)
                return if (jsonString == null) null
                else objectMapper.readValue(jsonString, javaType.javaTypeClass)
            }

            override fun doExtract(statement: CallableStatement, index: Int, options: WrapperOptions): X? {
                val jsonString = statement.getString(index)
                return if (jsonString == null) null
                else objectMapper.readValue(jsonString, javaType.javaTypeClass)
            }

            override fun doExtract(statement: CallableStatement, name: String, options: WrapperOptions): X? {
                val jsonString = statement.getString(name)
                return if (jsonString == null) null
                else objectMapper.readValue(jsonString, javaType.javaTypeClass)
            }
        }
    }
}
class JsonJdbcTypeConstructor : JdbcTypeConstructor {

    override fun getDefaultSqlTypeCode(): Int = SqlTypes.JSON


    override fun resolveType(
        typeConfiguration: TypeConfiguration,
        dialect: Dialect,
        elementType: BasicType<*>,
        columnTypeInformation: ColumnTypeInformation?
    ): JdbcType {
        return JsonJdbcType() // Your custom implementation
    }

    override fun resolveType(
        typeConfiguration: TypeConfiguration,
        dialect: Dialect,
        elementType: JdbcType,
        columnTypeInformation: ColumnTypeInformation?
    ): JdbcType {
        return JsonJdbcType()
    }
}
class CustomTypeContributor : TypeContributor {
    override fun contribute(typeContributions: TypeContributions, serviceRegistry: ServiceRegistry?) {
        // Get the JDBC type registry
        val jdbcTypeRegistry = typeContributions
            .getTypeConfiguration()
            .getJdbcTypeRegistry()


        // Register a custom type constructor
        // Example: 
        jdbcTypeRegistry.addTypeConstructor(
            SqlTypes.JSON,  // JDBC type code
            JsonJdbcTypeConstructor() // Your constructor implementation
        )
    }
}