Last active
August 25, 2021 08:28
-
-
Save asmarcz/acce6afeca510013deca6b6022c5b38b to your computer and use it in GitHub Desktop.
Kotlin JDBC layer in Javalin REST API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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