{"id":13593524,"url":"https://github.com/davidmoten/rxjava-jdbc","last_synced_at":"2025-05-14T13:08:41.207Z","repository":{"id":13962238,"uuid":"16662619","full_name":"davidmoten/rxjava-jdbc","owner":"davidmoten","description":"Efficient execution and functional composition of database calls using jdbc and RxJava Observables","archived":false,"fork":false,"pushed_at":"2025-02-27T00:59:15.000Z","size":1297,"stargazers_count":804,"open_issues_count":28,"forks_count":114,"subscribers_count":54,"default_branch":"master","last_synced_at":"2025-04-11T18:21:29.611Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","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/davidmoten.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":"2014-02-09T08:07:31.000Z","updated_at":"2025-03-22T04:28:21.000Z","dependencies_parsed_at":"2023-10-02T01:34:04.965Z","dependency_job_id":"264bd0b6-8cba-4134-8df7-17ed74618e4a","html_url":"https://github.com/davidmoten/rxjava-jdbc","commit_stats":{"total_commits":971,"total_committers":16,"mean_commits":60.6875,"dds":0.1575695159629248,"last_synced_commit":"42b611a430c0783975fd96ef4ba238316fa06350"},"previous_names":[],"tags_count":46,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Frxjava-jdbc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Frxjava-jdbc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Frxjava-jdbc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Frxjava-jdbc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidmoten","download_url":"https://codeload.github.com/davidmoten/rxjava-jdbc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254149974,"owners_count":22022852,"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-01T16:01:21.198Z","updated_at":"2025-05-14T13:08:36.187Z","avatar_url":"https://github.com/davidmoten.png","language":"Java","readme":"rxjava-jdbc\n============\n\u003ca href=\"https://github.com/davidmoten/rxjava-jdbc/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/davidmoten/rxjava-jdbc/actions/workflows/ci.yml/badge.svg\"/\u003e\u003c/a\u003e\u003cbr/\u003e\n\u003ca href=\"https://scan.coverity.com/projects/4834\"\u003e\u003cimg src=\"https://scan.coverity.com/projects/4834/badge.svg?flat=1\"/\u003e\u003c/a\u003e\u003cbr/\u003e\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.davidmoten/rxjava-jdbc/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/com.github.davidmoten/rxjava-jdbc)\n\nEfficient execution, concise code, and functional composition of database calls\nusing JDBC and [RxJava](https://github.com/Netflix/RxJava/wiki) [Observable](http://netflix.github.io/RxJava/javadoc/rx/Observable.html). \n\nStatus: Released to Maven Central\n\nSee also [rxjava2-jdbc](https://github.com/davidmoten/rxjava2-jdbc) for RxJava 2.x with non-blocking connection pools!\n\n[Release Notes](https://github.com/davidmoten/rxjava-jdbc/releases)\n\nFeatures\n--------------------------\n* Functionally compose database queries run sequentially or in parallel \n* Queries may be only partially run or indeed never run due to subscription cancellations thus improving efficiency\n* Concise code\n* Queries can depend on completion of other Observables and can be supplied parameters through Observables.\n* Method chaining just leads the way (once you are on top of the RxJava api of course!)\n* All the RxJava goodness!\n* Automatically maps query result rows into typed tuples or your own classes\n* CLOB and BLOB handling is simplified greatly\n\nMaven site reports are [here](http://davidmoten.github.io/rxjava-jdbc/index.html) including [javadoc](http://davidmoten.github.io/rxjava-jdbc/apidocs/index.html).\n\nTable of Contents\n------------------------\n\n- [rxjava-jdbc](#)\n\t- [Features](#)\n\t- [Todo](#todo)\n\t- [Build instructions](#build-instructions)\n\t- [Getting started](#getting-started)\n\t- [Query types](#query-types)\n\t- [Functional composition of JDBC calls](#functional-composition-of-jdbc-calls)\n\t- [About toBlocking](#about-toblocking)\n\t- [Dependencies](#dependencies)\n\t- [Mixing explicit and Observable parameters](#mixing-explicit-and-observable-parameters)\n\t- [Passing multiple parameter sets to a query](#passing-multiple-parameter-sets-to-a-query)\n\t- [Named parameters](#named-parameters)\n\t- [Processing a ResultSet](#processing-a-resultset)\n\t- [Mapping](#mapping)\n\t- [Explicit mapping](#explicit-mapping)\n\t- [Automap](#automap)\n\t\t- [Automap using an interface](#automap-using-an-interface)\n\t\t- [Automap using a concrete class](#automap-using-a-concrete-class)\n\t- [Auto mappings](#auto-mappings)\n\t- [Tuples](#tuples)\n\t\t- [Tuple2](#tuple2)\n\t\t- [TupleN](#tuplen)\n\t- [Returning generated keys](#returning-generated-keys)\n\t- [Large objects support](#large-objects-support)\n\t\t- [Insert a Clob](#insert-a-clob)\n\t\t- [Insert a Null Clob](#insert-a-null-clob)\n\t\t- [Read a Clob](#read-a-clob)\n\t\t- [Insert a Blob](#insert-a-clob)\n\t\t- [Insert a Null Blob](#insert-a-null-blob)\n\t\t- [Read a Blob](#read-a-blob)\n\t- [Compose](#compose)\n\t- [Transactions](#transactions)\n\t\t- [Transactions as dependency](#transactions-as-dependency)\n\t\t- [onNext Transactions](#onNext-transactions)\n\t- [Asynchronous queries](#asynchronous-queries)\n\t- [Backpressure](#backpressure)\n\t- [Logging](#logging)\n\t- [Database Connection Pools](#database-connection-pools)\n\t- [Using a custom connection pool](#using-a-custom-connection-pool)\n\t- [Use a single Connection](#use-a-single-connection)\n\t- [Note for SQLite Users](#note-for-sqlite-users)\n\nTodo\n------------\n* Callable statements\n\nBuild instructions\n-------------------\n```\ngit clone https://github.com/davidmoten/rxjava-jdbc.git\ncd rxjava-jdbc\nmvn clean install\n```\n\nGetting started\n--------------------\nInclude this maven dependency in your pom (available in Maven Central):\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.davidmoten\u003c/groupId\u003e\n    \u003cartifactId\u003erxjava-jdbc\u003c/artifactId\u003e\n    \u003cversion\u003eVERSION_HERE\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nAfter using [RxJava](https://github.com/Netflix/RxJava/wiki) on a work project and being very impressed with \nit (even without Java 8 lambdas!), I wondered what it could offer for JDBC usage. The answer is lots!\n\nHere's a simple example:\n```java\nDatabase db = Database.from(url);\nList\u003cString\u003e names = db\n\t\t.select(\"select name from person where name \u003e ? order by name\")\n\t\t.parameter(\"ALEX\")\n\t\t.getAs(String.class)\n\t\t.toList().toBlocking().single();\nSystem.out.println(names);\n```\noutput:\n```\n[FRED, JOSEPH, MARMADUKE]\n```\nWithout using rxjava-jdbc:\n```java\nString sql = \"select name from person where name \u003e ? order by name\";\ntry (Connection con = nextConnection();\n     PreparedStatement ps = con.prepareStatement(sql);) {\n    ps.setObject(1, \"ALEX\");\n    List\u003cString\u003e list = new ArrayList\u003cString\u003e();\n    try (ResultSet rs = ps.executeQuery()) {\n        while (rs.next()) {\n            list.add(rs.getString(1));\n        }\n    }\n    System.out.println(list);\n} catch (SQLException e) {\n    throw new RuntimeException(e);\n}\n```\n\nQuery types\n------------------\nThe ```Database.select()``` method is used for \n* SQL select queries. \n\nThe ```Database.update()``` method is used for\n* update\n* insert\n* delete\n* DDL (like *create table*, etc)\n\nExamples of all of the above methods are found in the sections below. \n\nFunctional composition of JDBC calls\n-----------------------------------------\nHere's an example, wonderfully brief compared to normal JDBC usage:\n \n```java\nimport com.github.davidmoten.rx.jdbc.Database;\nimport rx.Observable;\n\n// use composition to find the first person alphabetically with\n// a score less than the person with the last name alphabetically\n// whose name is not XAVIER. Two threads and connections will be used.\n\nDatabase db = new Database(connectionProvider);\nObservable\u003cInteger\u003e score = db\n\t\t.select(\"select score from person where name \u003c\u003e ? order by name\")\n\t\t.parameter(\"XAVIER\")\n\t\t.getAs(Integer.class)\n\t\t.last();\nString name = db\n\t\t.select(\"select name from person where score \u003c ? order by name\")\n\t\t.parameters(score)\n\t\t.getAs(String.class)\n\t\t.first()\n\t\t.toBlocking().single();\nassertEquals(\"FRED\", name);\n```\nor alternatively using the ```Observable.compose()``` method to chain everything in one command:\n```java\nString name = db\n    .select(\"select score from person where name \u003c\u003e ? order by name\")\n    .parameter(\"XAVIER\")\n    .getAs(Integer.class)\n    .last()\n    .compose(db.select(\"select name from person where score \u003c ? order by name\")\n            .parameterTransformer()\n            .getAs(String.class))\n    .first()\n    .toBlocking().single();\n```\n\nAbout toBlocking\n----------------------------\nYou'll see ```toBlocking()``` used in the examples in this page and in \nthe unit tests but in your application code you should try to avoid using it. The most benefit \nfrom the reactive style is obtained by *not leaving the monad*. That is, stay in Observable land and make \nthe most of it. Chain everything together and leave ```toBlocking``` to \nan endpoint or better still just subscribe with a ```Subscriber```.\n\nDependencies\n--------------\nYou can setup chains of dependencies that will determine the order of running of queries. \n\nTo indicate that a query cannot be run before one or more other Observables\nhave been completed use the `dependsOn()` method. Here's an example:\n```java\nObservable\u003cInteger\u003e insert = db\n\t\t.update(\"insert into person(name,score) values(?,?)\")\n\t\t.parameters(\"JOHN\", 45)\n\t\t.count()\n\t\t.map(Util.\u003cInteger\u003e delay(500));\nint count = db\n\t\t.select(\"select name from person\")\n\t\t.dependsOn(insert)\n\t\t.get()\n\t\t.count()\n\t\t.toBlocking().single();\nassertEquals(4, count);\n```\n\nNote that when you pass the output of a query as a parameter to another query there is an implicit dependency established.\n\nMixing explicit and Observable parameters\n------------------------------------------\nExample:\n```java\nString name= db\n\t.select(\"select name from person where name \u003e ?  and score \u003c ? order by name\")\n\t.parameter(\"BARRY\")\n\t.parameters(Observable.just(100))\n\t.getAs(String.class)\n\t.first()\n\t.toBlocking().single();\nassertEquals(\"FRED\",name);\n```\n\nPassing multiple parameter sets to a query\n--------------------------------------------\nGiven a sequence of parameters, each chunk of parameters will be run with the query and the results appended. \nIn the example below there is only one parameter in the sql statement yet two parameters are specified.\nThis causes the statement to be run twice.\n\n```java\nList\u003cInteger\u003e list = \n\tdb.select(\"select score from person where name=?\")\n\t    .parameter(\"FRED\").parameter(\"JOSEPH\")\n\t\t.getAs(Integer.class).toList().toBlocking().single();\nassertEquals(Arrays.asList(21,34),list);\n```\n\nNamed parameters\n----------------------------\nExamples:\n\n```java\nObservable\u003cString\u003e names = db\n    .select(\"select name from person where score \u003e= :min and score \u003c=:max\")\n    .parameter(\"min\", 24)\n    .parameter(\"max\", 26)\n    .getAs(String.class);\n```\n\nUsing a map of parameters:\n```java\nMap\u003cString, Integer\u003e map = new HashMap\u003cString, Integer\u003e();\nmap.put(\"min\", 24);\nmap.put(\"max\", 26);\nObservable\u003cString\u003e names = db\n    .select(\"select name from person where score \u003e= :min and score \u003c=:max\")\n    .parameters(map)\n    .getAs(String.class);\n```\n\nUsing an Observable of maps:\n```java\nObservable\u003cString\u003e names = db\n    .select(\"select name from person where score \u003e= :min and score \u003c=:max\")\n    .parameters(Observable.just(map1, map2))\n    .getAs(String.class);\n```\n\nProcessing a ResultSet\n-----------------------------\nMany operators in rxjava process items pushed to them asynchronously. Given this it is important that ```ResultSet``` query results are processed \nbefore being emitted to a consuming operator. This means that the select query needs to be passed a function that converts a ```ResultSet``` to\na result that does not depend on an open ```java.sql.Connection```. Use the ```get()```, ```getAs()```, ```getTuple?()```, and ```autoMap()``` methods \nto specify this function as below.\n\n```java\nObservable\u003cInteger\u003e scores = db.select(\"select score from person where name=?\")\n\t    .parameter(\"FRED\")\n\t\t.getAs(Integer.class);\n```\n\nMapping\n-----------------\nA common requirement is to map the rows of a ResultSet to an object. There are two main options: explicit mapping and automap.\n\nExplicit mapping\n----------------------\nUsing `get` you can map the `ResultSet` as you wish:\n\n```java\ndb.select(\"select name, score from person\")\n  .get( rs -\u003e new Person(rs.getString(1), rs.getInt(2)));\n```\n\nAutomap\n------------------------------\n`automap` does more for you than explicit mapping. You can provide just an annotated interface and objects will be created that implement that interface and types will be converted for you (See *Auto mappings* section below).\n\nThere is some reflection overhead with using auto mapping. Use your own benchmarks to determine if its important to you (the reflection overhead may not be significant compared to the network latencies involved in database calls).\n\nThe `autoMap` method maps result set rows to instances of the class you nominate. \n\nIf you nominate an interface then dynamic proxies (a java reflection feature) are used to build instances. \n\nIf you nominate a concrete class then the columns of the result set are mapped to parameters in the constructor (again using reflection).\n\n### Automap using an interface\n\nCreate an annotated interface (introduced in *rxjava-jdbc* 0.5.8):\n\n```java\npublic interface Person {\n\n    @Column(\"name\")\n    String name();\n\n    @Column(\"score\")\n    int score();\n}\n``` \n\nThen run\n```java\nObservable\u003cPerson\u003e persons = db\n                 .select(\"select name, score from person order by name\")\n                 .autoMap(Person.class);\n```\nEasy eh!\n\nAn alternative is to annotate the interface with the indexes of the columns in the result set row:\n\n```java\npublic interface Person {\n\n    @Index(1)\n    String name();\n\n    @Index(2)\n    int score();\n}\n```\n\nCamel cased method names will be converted to underscore by default (since 0.5.11):\n\n```java\npublic interface Address {\n\n    @Column // maps to address_id \n    int addressId();\n\n    @Column // maps to full_address\n    String fullAddress();\n}\n```\n \nYou can also specify the sql to be run in the annotation:\n\n```java\n@Query(\"select name, score from person order by name\")\npublic interface Person {\n\n    @Column\n    String name();\n\n    @Column\n    int score();\n}\n``` \nThen run like this:\n```java\nObservable\u003cPerson\u003e persons = db\n                 .select().autoMap(Person.class);\n```\n\n\n### Automap using a concrete class \n\nGiven this class:\n```java\nstatic class Person {\n\tprivate final String name;\n\tprivate final double score;\n\tprivate final Long dateOfBirth;\n\tprivate final Long registered;\n\n\tPerson(String name, Double score, Long dateOfBirth,\n\t\t\tLong registered) {\n\t\t\t...\n```\nThen run\n```java\nObservable\u003cPerson\u003e persons = db\n\t\t\t\t.select(\"select name,score,dob,registered from person order by name\")\n\t\t\t\t.autoMap(Person.class);\n```\nThe main requirement is that the number of columns in the select statement must match \nthe number of columns in a constructor of ```Person``` and that the column types can be \nautomatically mapped to the types in the constructor.\n\nAuto mappings\n------------------\nThe automatic mappings below of objects are used in the ```autoMap()``` method and for typed ```getAs()``` calls.\n* ```java.sql.Date```,```java.sql.Time```,```java.sql.Timestamp``` \u003c==\u003e ```java.util.Date```\n* ```java.sql.Date```,```java.sql.Time```,```java.sql.Timestamp```  ==\u003e ```java.lang.Long```\n* ```java.sql.Blob``` \u003c==\u003e ```java.io.InputStream```, ```byte[]```\n* ```java.sql.Clob``` \u003c==\u003e ```java.io.Reader```, ```String```\n* ```java.math.BigInteger``` ==\u003e ```Long```, ```Integer```, ```Decimal```, ```Float```, ```Short```, ```java.math.BigDecimal```\n* ```java.math.BigDecimal``` ==\u003e ```Long```, ```Integer```, ```Decimal```, ```Float```, ```Short```, ```java.math.BigInteger```\n\nNote that automappings do not occur to primitives so use ```Long``` instead of ```long```.\n\nTuples\n---------------\nTyped tuples can be returned in an ```Observable```:\n### Tuple2\n```java\nTuple2\u003cString, Integer\u003e tuple = db\n\t\t.select(\"select name,score from person where name \u003e? order by name\")\n\t\t.parameter(\"ALEX\").create()\n\t\t.getAs(String.class, Integer.class).last()\n\t\t.toBlocking().single();\nassertEquals(\"MARMADUKE\", tuple.value1());\nassertEquals(25, (int) tuple.value2());\n```\nSimilarly for ```Tuple3```, ```Tuple4```, ```Tuple5```, ```Tuple6```, ```Tuple7```, and finally \n### TupleN\n```java\nTupleN\u003cString\u003e tuple = db\n\t\t.select(\"select name, lower(name) from person order by name\")\n\t\t.create()\n\t\t.getTupleN(String.class).first()\n\t\t.toBlocking().single();\nassertEquals(\"FRED\", tuple.values().get(0));\nassertEquals(\"fred\", tuple.values().get(1));\n```\n\nReturning generated keys\n-------------------------\nIf you insert into a table that say in h2 is of type `auto_increment` then you don't need to specify a value but you may want to know what value was inserted in the generated key field.\n\nGiven a table like this\n```\ncreate table note(\n    id bigint auto_increment primary key,\n    text varchar(255)\n)\n```\nThis code inserts two rows into the *note* table and returns the two generated keys:\n\n```java\nObservable\u003cInteger\u003e keys = \n    db.update(\"insert into note(text) values(?)\")\n      .parameter(\"hello\", \"there\")\n      .returnGeneratedKeys()\n      .getAs(Integer.class);\n```\n\nThe `returnGeneratedKeys` method also supports returning multiple keys per row so the builder offers methods just like `select` to do explicit mapping or auto mapping.\n\nLarge objects support\n------------------------------\nBlob and Clobs are straightforward to handle.\n\n### Insert a Clob\nHere's how to insert a String value into a Clob (*document* column below is of type ```CLOB```):\n```java\nString document = ...\nObservable\u003cInteger\u003e count = db\n\t\t.update(\"insert into person_clob(name,document) values(?,?)\")\n\t\t.parameter(\"FRED\")\n\t\t.parameter(Database.toSentinelIfNull(document)).count();\n```\n(Note the use of the ```Database.toSentinelIfNull(String)``` method to handle the null case correctly)\n\nor using a ```java.io.Reader```:\n```java\nReader reader = ...;\nObservable\u003cInteger\u003e count = db\n\t\t.update(\"insert into person_clob(name,document) values(?,?)\")\n\t\t.parameter(\"FRED\")\n\t\t.parameter(reader).count();\n```\n### Insert a Null Clob\nThis requires *either* a special call (```parameterClob(String)```) to identify the parameter as a CLOB:\n```java\nObservable\u003cInteger\u003e count = db\n\t\t.update(\"insert into person_clob(name,document) values(?,?)\")\n\t\t.parameter(\"FRED\")\n\t\t.parameterClob(null).count();\n```\nor use the null Sentinel object for Clobs:\n```java\nObservable\u003cInteger\u003e count = db\n\t\t.update(\"insert into person_clob(name,document) values(?,?)\")\n\t\t.parameter(\"FRED\")\n\t\t.parameter(Database.NULL_CLOB).count();\n```\nor wrap the String parameter with ```Database.toSentinelIfNull(String)``` as above in the Insert a Clob section.\n\n### Read a Clob\n```java\nObservable\u003cString\u003e document = db.select(\"select document from person_clob\")\n\t\t\t\t.getAs(String.class);\n```\nor\n```java\nObservable\u003cReader\u003e document = db.select(\"select document from person_clob\")\n\t\t\t\t.getAs(Reader.class);\n```\n### Insert a Blob\nSimilarly for Blobs (*document* column below is of type ```BLOB```):\n```java\nbyte[] bytes = ...\nObservable\u003cInteger\u003e count = db\n\t\t.update(\"insert into person_blob(name,document) values(?,?)\")\n\t\t.parameter(\"FRED\")\n\t\t.parameter(Database.toSentinelIfNull(bytes)).count();\n```\n### Insert a Null Blob\nThis requires *either* a special call (```parameterBlob(String)``` to identify the parameter as a CLOB:\n```java\nObservable\u003cInteger\u003e count = db\n\t\t.update(\"insert into person_blob(name,document) values(?,?)\")\n\t\t.parameter(\"FRED\")\n\t\t.parameterBlob(null).count();\n```\nor use the null Sentinel object for Blobs:\n```java\nObservable\u003cInteger\u003e count = db\n\t\t.update(\"insert into person_clob(name,document) values(?,?)\")\n\t\t.parameter(\"FRED\")\n\t\t.parameter(Database.NULL_BLOB).count();\n```\nor wrap the byte[] parameter with ```Database.toSentinelIfNull(byte[])``` as above in the Insert a Blob section.\n\n### Read a Blob\n```java\nObservable\u003cbyte[]\u003e document = db.select(\"select document from person_clob\")\n\t\t\t\t.getAs(byte[].class);\n```\nor\n```java\nObservable\u003cInputStream\u003e document = db.select(\"select document from person_clob\")\n\t\t\t\t.getAs(InputStream.class);\n```\n\nCompose\n-----------------------------------\n\nUsing the ```Observable.compose()``` method you can perform multiple queries without breaking method chaining. ```Observable.compose()``` \nrequires a ```Transformer``` parameter which are available via \n\n* ```db.select(sql).parameterTransformer().getXXX()```\n* ```db.select(sql).parameterListTransformer().getXXX()```\n* ```db.select(sql).dependsOnTransformer().getXXX()```\n* ```db.update(sql).parameterTransformer()```\n* ```db.update(sql).parameterListTransformer()```\n* ```db.update(sql).dependsOnTransformer()```\n\nExample:   \n```java\nObservable\u003cInteger\u003e score = Observable\n    // parameters for coming update\n    .just(4, \"FRED\")\n    // update Fred's score to 4\n    .compose(db.update(\"update person set score=? where name=?\")\n            //parameters are pushed\n            .parameterTransformer())\n    // update everyone with score of 4 to 14\n    .compose(db.update(\"update person set score=? where score=?\")\n            .parameters(14, 4)\n            //wait for completion of previous observable\n            .dependsOnTransformer())\n    // get Fred's score\n    .compose(db.select(\"select score from person where name=?\")\n            .parameters(\"FRED\")\n            //wait for completion of previous observable\n            .dependsOnTransformer()\n\t\t\t.getAs(Integer.class));\n```\n\nNote that conditional evaluation of a query is obtained using \nthe ```parameterTransformer()``` method (no parameters means no query run) \nwhereas using ```dependsOnTransformer()``` just waits for the \ndependency to complete and ignores how many items the dependency emits.  \n\nIf the query does not require parameters you can push it an empty list \nand use the ```parameterListTransformer()``` to force execution.\n\nExample:\n```java\nObservable\u003cInteger\u003e rowsAffected = Observable\n    //generate two integers\n    .range(1,2)\n    //replace the integers with empty observables\n    .map(toEmpty())\n    //execute the update twice with an empty list\n    .compose(db.update(\"update person set score = score + 1\")\n            .parameterListTransformer())\n    // flatten\n    .compose(RxUtil.\u003cInteger\u003e flatten())\n    // total the affected records\n    .compose(SUM_INTEGER);\n```\n\nTransactions\n------------------\nWhen you want a statement to participate in a transaction then either it should\n* depend on ```db.beginTransaction()``` \n* be passed parameters or dependencies through ```db.beginTransactionOnNext()```\n\n### Transactions as dependency\n```java\nObservable\u003cBoolean\u003e begin = db.beginTransaction();\nObservable\u003cInteger\u003e updateCount = db\n    // set everyones score to 99\n    .update(\"update person set score=?\")\n    // is within transaction\n    .dependsOn(begin)\n    // new score\n    .parameter(99)\n    // execute\n    .count();\nObservable\u003cBoolean\u003e commit = db.commit(updateCount);\nlong count = db\n    .select(\"select count(*) from person where score=?\")\n\t// set score\n\t.parameter(99)\n\t// depends on\n\t.dependsOn(commit)\n\t// return as Long\n\t.getAs(Long.class)\n\t// log\n\t.doOnEach(RxUtil.log())\n\t// get answer\n\t.toBlocking().single();\nassertEquals(3, count);\n```\n\n### onNext Transactions\n```java\nList\u003cInteger\u003e mins = Observable\n    // do 3 times\n    .just(11, 12, 13)\n    // begin transaction for each item\n    .compose(db.beginTransactionOnNext_())\n    // update all scores to the item\n    .compose(db.update(\"update person set score=?\").parameterTransformer())\n    // to empty parameter list\n    .map(toEmpty())\n    // increase score\n    .compose(db.update(\"update person set score=score + 5\").parameterListTransformer())\n    //only expect one result so can flatten\n    .compose(RxUtil.\u003cInteger\u003eflatten())\n    // commit transaction\n    .compose(db.commitOnNext_())\n    // to empty lists\n    .map(toEmpty())\n    // return count\n    .compose(db.select(\"select min(score) from person\").parameterListTransformer()\n            .getAs(Integer.class))\n    // list the results\n    .toList()\n    // block and get\n    .toBlocking().single();\nassertEquals(Arrays.asList(16, 17, 18), mins);\n```\n\nNote that for each ```commit*``` method there is an corresponding ```rollback``` method as well.\n\nAsynchronous queries\n--------------------------\nUnless run within a transaction all queries are synchronous by default. However, if you request an asynchronous \nversion of the database using ```Database.asynchronous()``` or if you use asynchronous Transformers then watch out because this means that \nsomething like the code below could produce unpredictable results:\n\n```java\nDatabase adb = db.asynchronous();\nObservable\n    .just(1, 2, 3)\n    .compose(adb.update(\"update person set score = ?\")\n            .parameterTransformer());\n```\nAfter running this code you have no guarantee that the *update person set score=1* ran before the *update person set score=2*. \nTo run those queries synchronously either use a transaction:\n\n```java\nDatabase adb = db.asynchronous();\nObservable\n   .just(1, 2, 3)\n   .compose(adb.update(\"update person set score = ?\")\n           .dependsOn(db.beginTransaction())\n           .parameterTransformer())\n    .compose(adb.commitOnComplete_());\n```\n\nor use the default version of the ```Database``` object that schedules queries using ```Schedulers.trampoline()```.\n\n```java\nObservable.just(1, 2, 3)\n          .compose(db.update(\"update person set score = ?\")\n                  .parameterTransformer());\n```\n\nBackpressure\n-----------------\n```Database.select``` supports reactive pull backpressure as introduced in RxJava 0.20.0. This means that the pushing of items from the results of a query can be optionally slowed down by the operators downstream to assist in preventing out of memory exceptions or thread starvation. \n\nLogging\n-----------------\nLogging is handled by slf4j which bridges to the logging framework of your choice. Add\nthe dependency for your logging framework as a maven dependency and you are sorted. See the test scoped log4j example in [rxjava-jdbc/pom.xml](https://github.com/davidmoten/rxjava-jdbc/blob/master/pom.xml).\n\nDatabase Connection Pools\n----------------------------\nInclude the dependency below:\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.zaxxer\u003c/groupId\u003e\n    \u003cartifactId\u003eHikariCP-java6\u003c/artifactId\u003e\n    \u003cversion\u003e2.3.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\nand you can use a [Hikari](https://github.com/brettwooldridge/HikariCP) database connection pool like so:\n```java\nDatabase db = Database.builder().url(url).pool(minPoolSize,maxPoolSize).build();\n```\nOnce finished with a ``Database`` that has used a connection pool you should call \n```java\ndb.close();\n```\nThis will close the connection pool and  release its resources.\n\nUsing a custom connection pool\n---------------------------------\nIf Hikari doesn't suit you or you have container imposed constraints this is how you can use a different connection pool. \n\nWrite an implmentation of the ```ConnectionProvider``` interface (two methods, ```getConnection()``` and ```close()```) and use it like so:\n\n```java\nConnectionProvider cp = new CustomConnectionProvider();\nDatabase db = Database.builder().connectionProvider(cp).build();\n```\n\nThis method could be used to supply a JNDI datasource for example.\n\nUse a single Connection\n----------------------------\nA ```Database``` can be instantiated from a single ```java.sql.Connection``` which will \nbe used for all queries in companion with the current thread ```Scheduler``` (```Schedulers.trampoline()```).\nThe connection is wrapped in a ```ConnectionNonClosing``` which suppresses close calls so that the connection will\n still be open for all queries and will remain open after use of the ```Database``` object.\n \n Example:\n ```java\n Database db = Database.from(con);\n ```\n\nFetch Size\n----------------------------\nThe `fetch size` setting in [statements](https://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html#setFetchSize(int))\nallows to specify how many rows should be fetched from the database at once. In other words, instead of fetching all\ndata in the `ResultSet` at once, potentially consuming a lot of memory in the heap, the `fetch size` setting\nallows to trade time, due to multiple round-trips to the database, in exchange for lower memory consumption.\n\nExample:\n```java\ndb\n    .select(\"select * from person\")\n    // set fetch size\n    .fetchSize(10)\n    //\n    .autoMap(Person.class)\n    // \n    .take(20)\n\t// log\n\t.doOnEach(RxUtil.log());\n```\nIn this case, the JDBC driver will do two round trips, each time fetching 10 rows and transforming each row to an instance\nof `Person`. \n\nNote for SQLite Users\n----------------------------\n*rxjava-jdbc* does support [SQLite](http://sqlite.org/). But due to the [SQLite architecture](http://sqlite.org/faq.html#q5) there are limitations particularly with write operations (CREATE, INSERT, UPDATE, DELETE). If your application has any write operations, [use a single connection](#use-a-single-connection). If a source ```Observable``` pushes emissions through a series of database read/write operations, always collect emissions and flatten them between each database read/write operation. This will prevent a [SQLITE_INTERRUPT](https://sqlite.org/rescode.html#interrupt) exception by never having more than one query open at a time. \n\n```java\nObservable\u003cMyItem\u003e = Observable.just(itemstoInsert)\n\t\t.compose(executeInsertsAndGetKeys())\n\t\t.toList().concatMap(Observable::from)\n\t\t.compose(selectAndAutoMap());\n```\n","funding_links":[],"categories":["Java","数据库开发","Bindings"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidmoten%2Frxjava-jdbc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidmoten%2Frxjava-jdbc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidmoten%2Frxjava-jdbc/lists"}