{"id":13524494,"url":"https://github.com/seratch/kotliquery","last_synced_at":"2025-04-05T13:06:06.069Z","repository":{"id":2618295,"uuid":"47000280","full_name":"seratch/kotliquery","owner":"seratch","description":"A handy Database access library in Kotlin","archived":false,"fork":false,"pushed_at":"2024-04-17T14:20:04.000Z","size":317,"stargazers_count":207,"open_issues_count":13,"forks_count":35,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-10-13T17:45:46.116Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seratch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-11-27T21:37:58.000Z","updated_at":"2024-09-04T10:15:53.000Z","dependencies_parsed_at":"2024-01-13T22:55:11.783Z","dependency_job_id":"6a33f91b-d9e6-41a8-90f4-681b5b6eac83","html_url":"https://github.com/seratch/kotliquery","commit_stats":{"total_commits":108,"total_committers":19,"mean_commits":5.684210526315789,"dds":0.2777777777777778,"last_synced_commit":"575ab680d5285005eb4cec87303cd3ff93804a3a"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seratch%2Fkotliquery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seratch%2Fkotliquery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seratch%2Fkotliquery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seratch%2Fkotliquery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seratch","download_url":"https://codeload.github.com/seratch/kotliquery/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247339155,"owners_count":20923014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T06:01:10.634Z","updated_at":"2025-04-05T13:06:06.052Z","avatar_url":"https://github.com/seratch.png","language":"Kotlin","funding_links":[],"categories":["开源库和框架","others"],"sub_categories":["数据库"],"readme":"## KotliQuery\n\n[![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin/blob/readme/README.md#libraries-frameworks-database)\n[![CI Builds](https://github.com/seratch/kotliquery/actions/workflows/ci-builds.yml/badge.svg)](https://github.com/seratch/kotliquery/actions/workflows/ci-builds.yml)\n[![Maven Central](https://img.shields.io/maven-central/v/com.github.seratch/kotliquery.svg?label=Maven%20Central)](https://central.sonatype.com/search?q=kotliquery\u0026smo=true)\n\nKotliQuery is a handy RDB client library for Kotlin developers! The design is highly inspired by [ScalikeJDBC](http://scalikejdbc.org/), which is a proven database library in Scala. The priorities in this project are:\n\n* Less learning time\n* No breaking changes in releases\n* No additional complexity on top of JDBC\n\nThis library simply mitigates some pain points of the JDBC but our goal is not to completely encapsulate it.\n\n### Getting Started\n\nThe quickest way to try this library out would be to start with a simple Gradle project. You can find some examples [here](https://github.com/seratch/kotliquery/tree/master/sample).\n\n#### build.gradle\n\n```groovy\napply plugin: 'kotlin'\n\nbuildscript {\n    ext.kotlin_version = '1.7.20'\n    repositories {\n        mavenCentral()\n    }\n    dependencies {\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n    }\n}\nrepositories {\n    mavenCentral()\n}\ndependencies {\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version\"\n    implementation 'com.github.seratch:kotliquery:1.9.0'\n    implementation 'com.h2database:h2:2.1.214'\n}\n```\n\n### Example\n\nKotliQuery is much more easy-to-use than you expect. After just reading this short section, you will have learnt enough.\n\n#### Creating DB Session\n\nFirst thing you do is to create a `Session` object, which is a thin wrapper of `java.sql.Connection` instance. With this object, you can run queries using an established database connection.\n\n```kotlin\nimport kotliquery.*\n\nval session = sessionOf(\"jdbc:h2:mem:hello\", \"user\", \"pass\") \n```\n\n#### HikariCP\n\nFor production-grade applications, utilizing a connection pool library for better performance and resource management is highly recommended. KotliQuery provides an out-of-the-box solution that leverages [HikariCP](https://github.com/brettwooldridge/HikariCP), which is a widely accepted connection pool library.\n\n```kotlin\nHikariCP.default(\"jdbc:h2:mem:hello\", \"user\", \"pass\")\n\nsessionOf(HikariCP.dataSource()).use { session -\u003e\n   // working with the session\n}\n```\n\n#### DDL Execution\n\nYou can use a session for executing both DDLs and DMLs. The `asExecute` method if a query object sets the underlying JDBC Statement method to [`execute`](https://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html#execute-java.lang.String-).\n\n```kotlin\nsession.run(queryOf(\"\"\"\n  create table members (\n    id serial not null primary key,\n    name varchar(64),\n    created_at timestamp not null\n  )\n\"\"\").asExecute) // returns Boolean\n```\n\n#### Update Operations\n\nUsing `asUpdate` is an appropriate way to perform insert/update/delete statements. This method sets the underlying JDBC Statement method to [`executeUpdate`](https://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html#executeUpdate-java.lang.String-). \n\n```kotlin\nval insertQuery: String = \"insert into members (name,  created_at) values (?, ?)\"\n\nsession.run(queryOf(insertQuery, \"Alice\", Date()).asUpdate) // returns effected row count\nsession.run(queryOf(insertQuery, \"Bob\", Date()).asUpdate)\n```\n\n#### Select Queries\n\nNow that you've got a database table named `members`, it's time to run your first SQL statement with this library! To build a callable SQL executor, your code follows the three steps for it:\n\n- Use `queryOf` factory method with a query statement and its parameters to create a new `Query` object\n- Use `#map` method to attache a result extracting function (`(Row) -\u003e A`) to the `Query` object\n- Specify the response type (`asList`/`asSingle`) for the result\n\nThe following query returns a list of all member's IDs. In this line, the SQL statement is not yet executed. Also, this object `allIdsQuery` does not have any state. This means that you can reuse th object multiple times.\n\n```kotlin\nval allIdsQuery = queryOf(\"select id from members\").map { row -\u003e row.int(\"id\") }.asList\n```\n\nWith a valid session object, you can perform the SQL statement. The type of returned `ids` would be safely determined by Kotlin compiler.\n\n```kotlin\nval allIds: List\u003cInt\u003e = session.run(allIdsQuery)\n```\n\nAs you see, the extractor function is greatly flexible. You can define functions with any return type. All you need to do is to implement a function that extracts values from JDBC `ResultSet` interator and map them into a single expected type value. Here is a complete example:\n\n```kotlin\ndata class Member(\n  val id: Int,\n  val name: String?,\n  val createdAt: java.time.ZonedDateTime)\n\nval toMember: (Row) -\u003e Member = { row -\u003e \n  Member(\n    row.int(\"id\"), \n    row.stringOrNull(\"name\"), \n    row.zonedDateTime(\"created_at\")\n  )\n}\n\nval allMembersQuery = queryOf(\"select id, name, created_at from members\").map(toMember).asList\nval allMembers: List\u003cMember\u003e = session.run(allMembersQuery)\n```\n\nIf you are sure that a query can return zero or one row, `asSingle` returns an optional single value as below:\n\n```kotlin\nval aliceQuery = queryOf(\"select id, name, created_at from members where name = ?\", \"Alice\").map(toMember).asSingle\nval alice: Member? = session.run(aliceQuery)\n```\n\nTechnically, it's also possible to use `asSingle` along with an SQL statement returning multiple rows. With the default setting, the result data extraction returns only the first row in the results and skips the rest. In other words, KotliQuery silently ignores the inefficiency and the potential misbehavior. If you prefer detection by an error in this scenario, you can pass `strict` flag to `Session` initializer. With strict set to true, the query execution throws an exception if it detects multiple rows for `asSingle`.\n\n```kotlin\n// Session object constructor\nval session = Session(HikariCP.dataSource(), strict = true)\n\n// an auto-closing code block for session\nsessionOf(HikariCP.dataSource(), strict = true).use { session -\u003e\n\n}\n```\n\n\n#### Named query parameters\n\nAn alternative way to bind parameters is to use named parameters that start with `:` in the statement string. Note that, with this feature, KotliQuery still uses a prepared statement internally and your query execution is safe from SQL injection. The parameter parts like `:name` and `:age` in the following example query won't be just replaced as string values.\n\n```kotlin\nqueryOf(\n  \"\"\"\n  select id, name, created_at \n  from members \n  where (name = :name) and (age = :age)\n  \"\"\", \n  mapOf(\"name\" to \"Alice\", \"age\" to 20)\n)\n```\n\nPerformance-wise, the named parameter syntax can be slightly slower for parsing the statement plus a tiny bit more memory-consuming. But for most use case, the overhead should be ignorable. If you would like to make your SQL statements more readable and/or if your query has to repeat the same parameter in a query, using named query parameters should improve your productivity and the maintainability of the query a lot.\n\n#### Typed params\n\nYou can specify the Java type for each parameter in the following way. Passing the class `Parameter` helps KotliQuery properly determine the type to bind for each parameter in queries.\n\n```kotlin\nval param = Parameter(param, String::class.java)\nqueryOf(\n  \"\"\"\n  select id, name\n  from members \n  where ? is null or ? = name\n  \"\"\",\n  param,\n  param\n)\n``` \n\nAs a handier way, you can use the following helper method.\n\n```kotlin\nqueryOf(\n  \"\"\"\n  select id, name \n  from members \n  where ? is null or ? = name\n  \"\"\", \n  null.param\u003cString\u003e(),\n  null.param\u003cString\u003e()\n)\n```\n\nThis functionality is particularly useful in the situations like [the ones dsecribed here](https://www.postgresql.org/message-id/6ekbd7dm4d6su5b9i4hsf92ibv4j76n51f@4ax.com).\n\n#### Working with Large Dataset\n\nThe `#forEach` allows you to work with each row with less memory consumption. With this way, your application code does not need to load all the query result data in memory at once. This feature is greatly useful when you load a large number of rows from a database table by a single query.\n\n```kotlin\nsession.forEach(queryOf(\"select id from members\")) { row -\u003e\n  // working with large data set\n})\n```\n\n#### Transaction\n\nRunning queries in a transaction is of course supported! The `Session` object provides a way to start a transaction in a certain code block.\n\n```kotlin\nsession.transaction { tx -\u003e\n  // begin\n  tx.run(queryOf(\"insert into members (name,  created_at) values (?, ?)\", \"Alice\", Date()).asUpdate)\n}\n// commit\n\nsession.transaction { tx -\u003e\n  // begin\n  tx.run(queryOf(\"update members set name = ? where id = ?\", \"Chris\", 1).asUpdate)\n  throw RuntimeException() // rollback\n}\n```\n\nAs this library is a bit opinionated, transactions are available only with a code block. We intentionally do not support `begin` / `commit` methods. If you would like to manually manage the state of a transaction for some reason, you can use `session.connection.commit()` / `session.connection.rollback()` for it.\n\n## References\n\n* [Pro Kotlin Web Apps from Scratch](https://www.amazon.com/Pro-Kotlin-Apps-Scratch-Production-Ready-ebook/dp/B0BT1LSDFJ/) by August Lilleaas: uses KotliQuery to demonstrate how to efficiently run SQL queries\n\n## License\n\nThe MIT License\nCopyright (c) 2015 - Kazuhiro Sera\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseratch%2Fkotliquery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseratch%2Fkotliquery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseratch%2Fkotliquery/lists"}