{"id":18024069,"url":"https://github.com/thomasnield/rxkotlin-jdbc","last_synced_at":"2026-02-20T19:32:25.519Z","repository":{"id":144612205,"uuid":"86127964","full_name":"thomasnield/rxkotlin-jdbc","owner":"thomasnield","description":"Fluent RxJava JDBC extension functions for Kotlin","archived":false,"fork":false,"pushed_at":"2019-02-13T21:03:03.000Z","size":74,"stargazers_count":28,"open_issues_count":3,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-15T19:50:33.801Z","etag":null,"topics":["jdbc","kotlin","reactivex","rx","rxjava","rxjava-jdbc","rxkotlin"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thomasnield.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":"2017-03-25T02:57:01.000Z","updated_at":"2022-08-23T06:33:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"c1b6a814-ab55-4bea-bcd5-fe39ecbb8710","html_url":"https://github.com/thomasnield/rxkotlin-jdbc","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/thomasnield/rxkotlin-jdbc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasnield%2Frxkotlin-jdbc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasnield%2Frxkotlin-jdbc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasnield%2Frxkotlin-jdbc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasnield%2Frxkotlin-jdbc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thomasnield","download_url":"https://codeload.github.com/thomasnield/rxkotlin-jdbc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasnield%2Frxkotlin-jdbc/sbom","scorecard":{"id":882510,"data":{"date":"2025-08-11","repo":{"name":"github.com/thomasnield/rxkotlin-jdbc","commit":"596332118a63e29a85b78697092afe5de68cce77"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-24T08:45:06.897Z","repository_id":144612205,"created_at":"2025-08-24T08:45:06.897Z","updated_at":"2025-08-24T08:45:06.897Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29661599,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T16:33:43.953Z","status":"ssl_error","status_checked_at":"2026-02-20T16:33:43.598Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["jdbc","kotlin","reactivex","rx","rxjava","rxjava-jdbc","rxkotlin"],"created_at":"2024-10-30T07:11:49.605Z","updated_at":"2026-02-20T19:32:25.482Z","avatar_url":"https://github.com/thomasnield.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RxKotlin-JDBC\n\nFluent, concise, and easy-to-use extension functions targeting JDBC in the Kotlin language with [RxJava 2.0](https://github.com/ReactiveX/RxJava).\n\nThis library is inspired by Dave Moten's [RxJava-JDBC](https://github.com/davidmoten/rxjava-jdbc) but seeks to be much more lightweight by leveraging Kotlin functions. This works with threadpool `DataSource` implementations such as [HikariCP](https://github.com/brettwooldridge/HikariCP), but can also be used with vanilla JDBC `Connection`s.\n\nExtension functions like `select()`, `insert()`, and `execute()` will target both `DataSource` and JDBC `Connection` types. There is also support for **safe** blocking `Sequence` operations.\n\n## Binaries\n\n**Maven**\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.nield\u003c/groupId\u003e\n  \u003cartifactId\u003erxkotlin-jdbc\u003c/artifactId\u003e\n  \u003cversion\u003e0.4.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n**Gradle**\n\n```groovy\nrepositories {\n    mavenCentral()\n}\ndependencies {\n    compile 'org.nield:rxkotlin-jdbc:0.4.1'\n}\n```\n\n\n## Managing JDBC with Observables and Flowables\n\n### Using DataSources\n\nWhen you use a `DataSource`, a `Connection` will automatically be pulled from the pool upon subscription and given back when `onComplete` is called.\n\n\n```kotlin\nval config = HikariConfig()\nconfig.jdbcUrl = \"jdbc:sqlite::memory:\"\nconfig.minimumIdle = 3\nconfig.maximumPoolSize = 10\n\nval ds = HikariDataSource(config)\n\n//initialize\n\nwith(ds) {\n    execute(\"CREATE TABLE USER (ID INTEGER PRIMARY KEY, USERNAME VARCHAR(30) NOT NULL, PASSWORD VARCHAR(30) NOT NULL)\")\n    execute(\"INSERT INTO USER (USERNAME,PASSWORD) VALUES (?,?)\", \"thomasnield\", \"password123\")\n    execute(\"INSERT INTO USER (USERNAME,PASSWORD) VALUES (?,?)\", \"bobmarshal\",\"batman43\")\n}\n\n// Retrieve all users\nds.select(\"SELECT * FROM USER\")\n        .toObservable { it.getInt(\"ID\") to it.getString(\"USERNAME\") }\n        .subscribe(::println)\n\n\n// Retrieve user with specific ID\nds.select(\"SELECT * FROM USER WHERE ID = :id\")\n        .parameter(\"id\", 2)\n        .toSingle { it.getInt(\"ID\") to it.getString(\"USERNAME\") }\n        .subscribeBy(::println)\n\n// Execute insert which return generated keys, and re-select the inserted record with that key\nds.insert(\"INSERT INTO USER (USERNAME, PASSWORD) VALUES (:username,:password)\")\n        .parameter(\"username\",\"josephmarlon\")\n        .parameter(\"password\",\"coffeesnob43\")\n        .toFlowable { it.getInt(1) }\n        .flatMapSingle {\n            conn.select(\"SELECT * FROM USER WHERE ID = :id\")\n                    .parameter(\"id\", it)\n                    .toSingle { \"${it.getInt(\"ID\")} ${it.getString(\"USERNAME\")} ${it.getString(\"PASSWORD\")}\" }\n        }\n        .subscribe(::println)\n\n// Run deletion\n\nconn.execute(\"DELETE FROM USER WHERE ID = :id\")\n        .parameter(\"id\",2)\n        .toSingle()\n        .subscribeBy(::println)\n```\n\n### Using Connections\n\nYou can also use a standard `Connection` with these extension functions, and closing will not happen automatically so you can micromanage the life of that connection. It is also helpful to execute transactions against an individual connection. \n\n```kotlin\nval connection = DriverManager.getConnection(\"jdbc:sqlite::memory:\")\n\nconnection.select(\"SELECT * FROM USER\")\n        .toObservable { it.getInt(\"ID\") to it.getString(\"USERNAME\") }\n        .subscribe(::println)\n        \nconnection.close()\n```\n\n\n## Blocking Sequences\n\nIt can be convenient to work with JDBC fluently in a blocking manner, so you don't always have to return everything as an `Observable` or `Flowable`. While you should strive to not break the monad, sometimes it is easier to not have items returned in reactive fashion. This is especially the case when you have reactively built `T` objects, but you want to retrieve metadata `U` and `V` objects when `T` is constructed.\n\nYou could try to use RxJava's native blocking operators, but these can have undesired side effects of interruption exceptions especially when the blocking operation is happening under a parent reactive operation that can be disposed. Therefore, these `Sequence` parts of the API do not rely on RxJava at all and are implemented at the `Iterable` level. These may be candidate to be moved to a separate library (and be used as a dependency in this one) before rxkotlin-jdbc reaches a 1.0 release. This transition is not planned to be very breaking, other than perhaps a few import changes. \n\nFor instance, we can call `toSequence()` to iterate a `ResultSet` as a `Sequence`.\n\n```kotlin\ndata class User(val id: Int, val userName: String, val password: String)\n\nconn.select(\"SELECT * FROM USER\").toSequence {\n    User(it.getInt(\"ID\"), it.getString(\"USERNAME\"), it.getString(\"PASSWORD\"))\n}.forEach(::println)\n```\n\nThe `toSequence()` actually returns a `ResultSetSequence`, which is important because if you use Sequence operators like `take()`, the iteration will need to manually be terminated early (completion events are not communicated like in RxJava). The returned `ResultSetSequence` should provide a handle to do this. \n\n```kotlin \nval conn = connectionFactory()\n\nconn.select(\"SELECT * FROM USER\").toSequence { it.getInt(\"ID\") }.apply {\n\n        take(1).forEach(::println)\n        close() // must manually close for operators that terminate iteration early\n}\n```\n\nTypically, if you simply want to return one item from a `ResultSet`, just call `blockingFirst()` or `blockingFirstOrNull()` which will handle the `close()` operation for you. \n\n```kotlin\nval conn = connectionFactory()\n\nval id = conn.select(\"SELECT * FROM USER WHERE ID = 1\").blockingFirst { it.getInt(\"ID\") }\n\nprintln(id)\n```\n\n## Allowing Choice of Sequence, Observable, or Flowable\n\nThis library supports emitting `T` items built off a `ResultSet` in the form of:\n\n* `Sequence\u003cT\u003e`\n* `Observable\u003cT\u003e`\n* `Flowable\u003cT\u003e`\n* `Single\u003cT\u003e`\n* `Maybe\u003cT\u003e`\n\nIf you are building an API, it may be handy to allow the user of the API to choose the means in which to receive the results.\n\nThe `toPipeline()` function will allow mapping the `ResultSet` to `T` items, but defer to the API user how to receive the results.\n\n\n```kotlin\n\ndata class User(val userName: String, val password: String)\n\nfun getUsers() = conn.select(\"SELECT * FROM USER\")\n                .toPipeline {\n                    User(it.getString(\"USERNAME\"), it.getString(\"PASSWORD\"))\n                }\n\nfun main(args: Array\u003cString\u003e) {\n\n    getUsers().toFlowable().subscribe { println(\"Receiving $it via Flowable\") }\n\n    getUsers().toObservable().subscribe { println(\"Receiving $it via Observable\") }\n\n    getUsers().toSequence().forEach { println(\"Receiving $it via Sequence\") }\n}\n```\n## Batching\n\n[Batch operations](http://tutorials.jenkov.com/jdbc/batchupdate.html) are now supported in RxKotlin-JDBC. This allows you to write a large amount of data to a database quickly using JDBC's batching interface.\n\nSimply have an `Observable\u003cT\u003e`, `Flowable\u003cT\u003e`, `Iterable\u003cT\u003e`, or `Sequence\u003cT\u003e` of desired elements to INSERT, UPDATE, or some other action against your database, and pass it to the `batchExecute()` function on `Connection` or `DataSource`.\n\nYou will also need to specify the SQL template, the mapper to turn each `T` element into SQL parameters, and the batch size.\n\nYou can have the result come back as an `Observable\u003cT\u003e`, `Flowable\u003cT\u003e`, `Sequence\u003cT\u003e`, or a `Compeletable`.\n\n```kotlin\nval conn = connectionFactory()\n\nclass User(val username: String, val password: String)\n\nval insertElements = Flowable.just(\n        User(\"josephmarlon\", \"coffeesnob43\"),\n        User(\"samuelfoley\",\"shiner67\"),\n        User(\"emilyearly\",\"rabbit99\"),\n        User(\"johnlawrey\", \"shiner23\"),\n        User(\"tomstorm\",\"coors44\"),\n        User(\"danpaxy\", \"texas22\"),\n        User(\"heathermorgan\",\"squirrel22\")\n)\n\nconn.batchExecute(\n        sqlTemplate = \"INSERT INTO USER (USERNAME, PASSWORD) VALUES (:username,:password)\",\n        elements = insertElements,\n        batchSize = 3,\n        parameterMapper = {\n            parameter(\"username\", it.username)\n            parameter(\"password\", it.password)\n        }\n).toFlowable().count().subscribeBy { println(\"Inserted $it records\") }\n```\n\n## Building Where Conditions Fluently\n\nAn experimental feature in RxKotlin-JDBC is an API-friendly builder for WHERE conditions, that may or may not use certain parameters to build WHERE conditions.\n\nFor instance, here is a function that will query a table with three possible parameters:\n\n```kotlin\nfun userOf(id: Int? = null, userName: String? = null, password: String? = null) =\n        conn.select(\"SELECT * FROM USER\")\n                .whereOptional(\"ID\", id)\n                .whereOptional(\"USERNAME\", userName)\n                .whereOptional(\"PASSWORD\", password)\n                .toObservable(::User)\n```\n\nWe can then query for any combination of these three parameters (including none of them).\n\n```kotlin\nuserOf().subscribe { println(it) } // prints all users\n\nuserOf(userName = \"thomasnield\", password = \"password123\")\n     .subscribe { println(it) }  // prints user with ID 1\n\n\nuserOf(id = 2).subscribe { println(it) } // prints user with ID 2\n```\n\nInstead of providing a simple field name, we can also provide an entire templated expression for more complex condtions.\n\n```kotlin\nconn.select(\"SELECT * FROM USER\")\n       .whereOptional(\"ID \u003e ?\", 1)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomasnield%2Frxkotlin-jdbc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthomasnield%2Frxkotlin-jdbc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomasnield%2Frxkotlin-jdbc/lists"}