Skip to content

Instantly share code, notes, and snippets.

@yat90
Last active February 3, 2023 05:08
Show Gist options
  • Save yat90/78a110e28ab274fae30f53e94031cda1 to your computer and use it in GitHub Desktop.
Save yat90/78a110e28ab274fae30f53e94031cda1 to your computer and use it in GitHub Desktop.
import com.google.gson.Gson
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.Function
import org.jetbrains.exposed.sql.statements.api.PreparedStatementApi
import org.postgresql.util.PGobject
fun <T : Any> Table.jsonb(name: String, klass: Class<T>, gson: Gson, nullable: Boolean): Column<T> {
return registerColumn<T>(name, JsonColumnType(klass, gson, nullable))
}
class JsonColumnType<out T : Any>(private val klass: Class<T>, private val gson: Gson, override var nullable: Boolean) :
IColumnType {
override fun setParameter(stmt: PreparedStatementApi, index: Int, value: Any?) {
val obj = PGobject()
obj.type = "jsonb"
if (value != null)
obj.value = value as String
stmt[index] = obj
}
override fun valueFromDB(value: Any): Any = when (value) {
is HashMap<*, *> -> value
is Map<*, *> -> value
else -> {
value as PGobject
try {
val json = value.value
gson.fromJson(json, klass)
} catch (e: Exception) {
e.printStackTrace()
throw RuntimeException("Can't parse JSON: $value")
}
}
}
override fun notNullValueToDB(value: Any): Any = gson.toJson(value)
override fun nonNullValueToString(value: Any): String = "'${gson.toJson(value)}'"
override fun sqlType() = "jsonb"
}
class JsonKey(val key: String) : Expression<String>() {
init {
if (!key.matches("[a-zA-Z]+".toRegex())) throw IllegalArgumentException("Only simple json key allowed.")
}
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder { append(key) }
}
inline fun <reified T> Column<Map<*, *>>.json(jsonKey: JsonKey): Function<T> {
val columnType = when (T::class) {
Int::class -> IntegerColumnType()
String::class -> VarCharColumnType()
Boolean::class -> BooleanColumnType()
else -> throw java.lang.RuntimeException("Column type ${T::class} not supported for json field.")
}
return json(jsonKey, columnType)
}
fun <T> Column<Map<*, *>>.json(jsonKey: JsonKey, columnType: IColumnType): Function<T> {
return JsonVal<T>(expr = this, jsonKey = jsonKey, columnType = columnType)
}
private class JsonVal<T>(
val expr: Expression<*>,
val jsonKey: JsonKey,
override val columnType: IColumnType
) : Function<T>(columnType) {
override fun toQueryBuilder(queryBuilder: QueryBuilder) =
queryBuilder { append("CAST((${expr.toQueryBuilder(queryBuilder)} ->> '${jsonKey.key}') AS ${columnType.sqlType()})") }
}
@qoomon
Copy link

qoomon commented Apr 20, 2020

why do you need to check for Map and HashMap in fun valueFromDB(value: Any): Any?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment