{"id":13428858,"url":"https://github.com/andrewoma/kwery","last_synced_at":"2026-01-11T11:55:07.643Z","repository":{"id":27632222,"uuid":"31116728","full_name":"andrewoma/kwery","owner":"andrewoma","description":"Kwery is an SQL library for Kotlin","archived":false,"fork":false,"pushed_at":"2019-10-01T12:34:04.000Z","size":1118,"stargazers_count":203,"open_issues_count":12,"forks_count":13,"subscribers_count":14,"default_branch":"master","last_synced_at":"2024-10-27T06:39:19.035Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/andrewoma.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-02-21T05:47:29.000Z","updated_at":"2024-02-29T07:20:40.000Z","dependencies_parsed_at":"2022-09-01T00:11:18.457Z","dependency_job_id":null,"html_url":"https://github.com/andrewoma/kwery","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewoma%2Fkwery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewoma%2Fkwery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewoma%2Fkwery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewoma%2Fkwery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andrewoma","download_url":"https://codeload.github.com/andrewoma/kwery/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243815605,"owners_count":20352195,"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-07-31T01:01:07.056Z","updated_at":"2026-01-11T11:55:07.592Z","avatar_url":"https://github.com/andrewoma.png","language":"Kotlin","funding_links":[],"categories":["Libraries","\u003ca name=\"Kotlin\"\u003e\u003c/a\u003eKotlin","数据库开发"],"sub_categories":["Database","语音合成"],"readme":"#### Kwery Overview\n\nKwery is an SQL library for Kotlin.\n\nKwery consists of three major modules (core, mapper and fetcher) that when combined provide similar\nfunctionality to a traditional ORM.\n\nKwery's manifesto:\n* **Your domain model is sacred.** No annotations or modifications to your model are required. Immutable models are fully supported.\n* **No implicit fetching.** Joins and graph fetches are explicit for predictable performance.\n* **No magic.** No proxies, interceptors, reflection or implicit saves. Explicit functions with sensible defaults control everything.\n* **Useful logging.** Logged statements are valid SQL with inline parameters for your dialect.\n\n[![Build Status](https://travis-ci.org/andrewoma/kwery.svg?branch=master)](https://travis-ci.org/andrewoma/kwery)\n\n#### Core\n\nThe [core module](core) is a fairly thin wrapper over JDBC, providing support for named parameters, logging\nand transactions.\n```kotlin\nclass Actor(val firstName: String, val lastName: String, val lastUpdate: Timestamp)\n\nval session = DefaultSession(connection, HsqlDialect()) // Standard JDBC connection\n\nval sql = \"select * from actor where first_name = :first_name\"\n\nval actors = session.select(sql, mapOf(\"first_name\" to \"Brad\")) { row -\u003e\n    Actor(row.string(\"first_name\"), row.string(\"last_name\"), row.timestamp(\"last_update\"))\n}\n```\n\n#### Mapper\n\nThe [mapper module](mapper) module builds on core to provide typical DAO (Data Access Object) functionality.\n\nAs Kwery believes your domain model shouldn't be tainted by mapping annotations,\nit uses a ``Table`` object to define the mapping between rows and objects.\n\n```kotlin\n// We'll map to standard immutable classes, grouping name fields into a class\nclass Name(val firstName: String, val lastName: String)\nclass Actor(val id: Int, val name: Name, val lastUpdate: LocalDateTime)\n\n// A table object defines the mapping between columns and models\n// Conversions default to those defined in the configuration but may be overridden\nobject actorTable : Table\u003cActor, Int\u003e(\"actor\"), VersionedWithTimestamp {\n    val ActorId    by col(Actor::id, id = true)\n    val FirstName  by col(Name::firstName, Actor::name)\n    val LastName   by col(Name::lastName, Actor::name)\n    val LastUpdate by col(Actor::lastUpdate, version = true)\n\n    override fun idColumns(id: Int) = setOf(ActorId of id)\n\n    override fun create(value: Value\u003cActor\u003e) = Actor(value of ActorId,\n            Name(value of FirstName, value of LastName), value of LastUpdate)\n}\n\n// Given a table object, a generic dao is a one-liner, including standard CRUD operations\nclass ActorDao(session: Session) : AbstractDao\u003cActor, Int\u003e(session, actorTable, Actor::id)\n\n// Now we can use the DAO\nval dao = ActorDao(session)\nval inserted = dao.insert(Actor(1, Name(\"Kate\", \"Beckinsale\"), LocalDateTime.now()))\nval actors = dao.findAll()\n```\n\nSee [`FilmDao.kt`](/mapper/src/test/kotlin/com/github/andrewoma/kwery/mappertest/example/FilmDao.kt) for\na more comprehensive example.\n\n#### Graph Fetcher\n\nDAOs only fetch data from their linked table by default. To fetch an object graph, using\na [graph fetcher](fetcher) is the recommended method.\n\nGiven a graph specification, the fetcher attempts to fetch the graph in the minimum\nnumber of queries possible. It does this by batching together requests for the same\ntype into a single query. As it fetches by ids, it also provides an ideal\nmechanism to insert a cache layer.\n\n```kotlin\n// Given the following domain model\ndata class Actor(val id: Int, val firstName: String, val lastName: String)\n\ndata class Language(val id: Int, val name: String)\n\ndata class Film(val id: Int, val language: Language, val actors: Set\u003cActor\u003e,\n                val title: String, val releaseYear: Int)\n\n// Define types with functions describing how to fetch a batch by ids\nval language = Type(Language::id, { languageDao.findByIds(it) })\nval actor = Type(Actor::id, { actorDao.findByIds(it) })\n\n// For types that reference other types describe how to apply fetched values\nval film = Type(Film::id, { filmDao.findByIds(it) }, listOf(\n        // 1 to 1\n        Property(Film::language, language, { it.language.id }, { f, l -\u003e f.copy(language = l) }),\n\n        // 1 to many requires a function to describe how to fetch the related objects\n        CollectionProperty(Film::actors, actor, Film::id,\n                { f, a -\u003e f.copy(actors = a.toSet()) },\n                { actorDao.findByFilmIds(it) })\n))\n\nval fetcher = GraphFetcher(setOf(language, actor, film))\n\n// Extension function to fetch the graph for any List using fetcher defined above\nfun \u003cT\u003e Collection\u003cT\u003e.fetch(node: Node) = fetcher.fetch(this, Node(node))\n\n// We can now efficiently fetch various graphs for any list of films\n// The following fetches the films with actors and languages in 3 queries\nval filmsWithAll = filmDao.findFilmsReleasedAfter(2010).fetch(Node.all)\n\n// The graph specification can also be built using properties\nval filmsWithActors = filmDao.findFilmsReleasedAfter(2010).fetch(Film::actors.node())\n```\n\nDAOs and graph fetching aim to cover 95% of a typical application data retrievals. For the\nremaining performance critical sections, use specialised methods on the DAOs using\npartial selects and joins as required.\n\n#### Example\n\nThe [example module](example) demonstrates using Kwery\nto expose a simple model via RESTful web services via [Dropwizard](http://dropwizard.io/).\n\n#### Transactional\n\nThe [transactional module](transactional) adds general purpose transaction interceptors. e.g.\n\n```kotlin\n@Transactional open class MyService(val session: Session) {\n    open fun foo() {}\n}\n\nval session = ManagedThreadLocalSession(dataSource, HsqlDialect())\nval service = transactionalFactory.fromClass(MyService(session), MyService::session)\nservice.foo() // Now calls to service automatically occur within a transaction\n```\n\nSee the [readme](transactional) for more information.\n\n#### Transactional for Jersey\n\nThe [transactional-jersey module](transactional-jersey) adds transaction annotations for Jersey.\n\nRegistering [`TransactionListener`](transactional-jersey/src/main/kotlin/com/github/andrewoma/kwery/transactional/jersey/transactional.kt)\nas a Jersey provider allows the `transactional` attribute to declare resource classes or methods as transactional.  \n\n```kotlin\nPath(\"/films\")\n@Transactional class FilmResource : Resource {\n    GET fun find(): List\u003cFilm\u003e {\n        ...\n    }\n}\n```\n\nSee the [readme](transactional-jersey) for more information.\n\n#### Status\n\nKwery is unstable. It's currently being developed for a side project, so features are added as required.\n\nKwery is available in [Maven Central](http://search.maven.org/#search%7Cga%7C1%7Candrewoma.kwery)\n\n`0.17` Compatible with Kotlin 1.1.3-2.\n* Fix #14 - Incorrect parameter positions for collections\n* Lazily set `Statement.poolable`\n\n`0.16` Compatible with Kotlin 1.1.0.\n\n`0.15` Compatible with Kotlin 1.0.4.\n* Mapper: Support ThreadLocalSessions in Dao by creating implicit transactions (thanks @brianmadden)\n\n`0.14` Compatible with Kotlin 1.0.4.\n\n`0.13` Compatible with Kotlin 1.0.3.\n\n`0.12` Compatible with Kotlin 1.0.2.\n* Core: QueryBuilder\n* Core: Fix collection binding when not first parameter\n* Mapper: Add Dao.findByIdForUpdate\n\n`0.11` Compatible with Kotlin 1.0.2.\n* Core: Fix logging of statements with bound values containing `$`\n* Core: Add experimental sqlite support\n* Mapper: Support generated keys for MySQL in DAOs\n\n`0.10` Compatible with Kotlin 1.0.2.\n\n`0.9` Compatible with Kotlin 1.0.0.\n* Mapper: add `Table.optionalCol` to construct optional types via paths\n\n`0.8` Compatible with Kotlin 1.0.0-rc-1036.\n* Mapper: support PreUpdate and PreInsert events (thanks @davemaple)\n* Remove tomcat pool module as Postgres drivers now support prepared statement caching\n\n`0.7` Compatible with Kotlin 1.0.0-beta-3595.\n* Add MySQL dialect\n\n`0.6` Compatible with Kotlin 1.0.0-beta-1038.\n\n`0.5` Compatible with Kotlin M14.\n\n`0.4` Compatible with Kotlin M13:\n* Provide a consistent set of defaults and converters for mapping standard types\n* Add defaults and converters for OffsetDateTime and ZonedDateTime\n\n`0.3` Compatible with Kotlin M13:\n* Improved docs\n* Simplified transaction listeners\n* Made transactions re-entrant\n* Renamed ThreadLocalSession to ManagedThreadLocalSession and introduced a new ThreadLocalSession for\n  use without interceptors and annotations.\n\n`0.2` Compatible with Kotlin M12, adding transactional interceptors.\n\n`0.1` Compatible with Kotlin M11.\n\n#### Building\n\n```bash\ngit clone https://github.com/andrewoma/kwery.git\ncd kwery\n./gradlew check install\n```\n\nNote: The tests require a local postgres and mysql database named `kwery`. e.g. On OS X\n```\nbrew install postgres\nlaunchctl load ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist\ncreatedb kwery\n\nbrew install mysql\nln -sfv /usr/local/opt/mysql/*.plist ~/Library/LaunchAgents\nlaunchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist\nmysql -uroot -e 'create database kwery'\nmysql -uroot -e \"create user 'kwery'@'localhost' identified by 'kwery'\"\nmysql -uroot -e \"grant all privileges on *.* to 'kwery'@'localhost'\"\n```\n\nTo open in IntelliJ, just open the `build.gradle` file and IntelliJ will generate the project automatically.\n\n#### Roadmap\n\nCore:\n* Support direct execution (currently everything is via a PreparedStatement)\n* Add more robust named parameter replacement (ignore patterns inside comments, strings, etc)\n\nDAO:\n* Documentation\n\nFetcher:\n* Documentation\n* General review - code seems overly complicated for what it does\n\nModules:\n* Dropwizard metrics integration\n* Generator - Generate initial `Table` and domain objects from reading JDBC metadata\n\nRobustness/Performance:\n* Soak test - check for leaking connections/resources over extended usage\n* Profile array based in clauses on large tables\n\nMisc:\n* Better IDE support for highlighting inline SQL. Vote for [KT-6610](https://youtrack.jetbrains.com/issue/KT-6610)\n\n#### License\nThis project is licensed under a MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewoma%2Fkwery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandrewoma%2Fkwery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewoma%2Fkwery/lists"}