Skip to content

Instantly share code, notes, and snippets.

@asmarcz
Last active August 25, 2021 08:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save asmarcz/acce6afeca510013deca6b6022c5b38b to your computer and use it in GitHub Desktop.
Save asmarcz/acce6afeca510013deca6b6022c5b38b to your computer and use it in GitHub Desktop.
Kotlin JDBC layer in Javalin REST API
import com.zaxxer.hikari.HikariDataSource
import io.javalin.Javalin
import org.slf4j.LoggerFactory
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
val ds = HikariDataSource().apply {
jdbcUrl = "jdbc:pgsql://localhost/test"
username = "api"
password = "dummy123"
}
fun ResultSet.toList(): List<Map<String, Any>> {
val metadata = this.metaData
val rows = mutableListOf<Map<String, Any>>()
while (this.next()) {
val row = mutableMapOf<String, Any>()
for (i in 1..metadata.columnCount) {
row[metadata.getColumnLabel(i)] = this.getObject(i)
}
rows.add(row)
}
return rows
}
class SQL(private val query: String) {
private var generated: Array<out String>? = null
interface UpdateResult {
val affected: Int
val generatedKeys: List<Map<String, Any>>
}
fun asList(vararg params: Any): List<Map<String, Any>> {
return prepare(params) { stmt ->
val rows: List<Map<String, Any>>
stmt.executeQuery().use { rs ->
rows = rs.toList()
}
rows
}
}
fun generatedKeys(vararg keys: String): SQL {
generated = keys
return this
}
fun update(vararg params: Any): UpdateResult {
return prepare(params, generated) { stmt ->
val affected = stmt.executeUpdate()
object : UpdateResult {
override val affected = affected
override val generatedKeys = stmt.generatedKeys.toList()
}
}
}
private fun <T> prepare(
params: Array<out Any>,
generatedKeys: Array<out String>? = null,
callback: (stmt: PreparedStatement) -> T
): T {
ds.connection.use { con ->
when (generatedKeys) {
null -> con.prepareStatement(query)
else -> con.prepareStatement(query, generatedKeys)
}.use { stmt ->
var idx = 1
for (param in params) {
stmt.setObject(idx++, param)
}
return callback(stmt)
}
}
}
}
fun main() {
val app = Javalin.create().start(8100)
val logger = LoggerFactory.getLogger("test-api")
app.exception(SQLException::class.java) { e, ctx ->
logger.error("An SQLException occurred.", e)
ctx.result("Sorry, it's on us.")
ctx.status(500)
}
app.get("/") { ctx ->
ctx.json(
SQL("SELECT name FROM people").asList()
)
}
app.get("/:length") { ctx ->
ctx.json(
SQL("SELECT name FROM people WHERE LENGTH(name) = ?")
.asList(ctx.pathParam<Int>("length").get())
)
}
app.post("/add") { ctx ->
val result = SQL("INSERT INTO people (name) VALUES (?)")
.generatedKeys("id")
.update(ctx.formParam<String>("name").get())
ctx.result(
"Affected ${result.affected} row with id=${result.generatedKeys[0]["id"]} and name=${ctx.formParam("name")}."
)
}
app.delete("/delete") { ctx ->
ctx.json(
SQL("DELETE FROM people WHERE id = ?")
.update(ctx.formParam<Int>("id").get())
.affected
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment