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
)
}
}