{"id":13605093,"url":"https://github.com/davidmoten/rxjava2-jdbc","last_synced_at":"2025-04-04T11:12:32.527Z","repository":{"id":12630668,"uuid":"72403045","full_name":"davidmoten/rxjava2-jdbc","owner":"davidmoten","description":"RxJava2 integration with JDBC including Non-blocking Connection Pools","archived":false,"fork":false,"pushed_at":"2024-01-01T17:41:43.000Z","size":1431,"stargazers_count":389,"open_issues_count":29,"forks_count":41,"subscribers_count":26,"default_branch":"master","last_synced_at":"2024-01-07T13:28:13.294Z","etag":null,"topics":["concurrency","database","java","jdbc","reactive","reactive-programming","reactive-streams","rxjava"],"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.adoc","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}},"created_at":"2016-10-31T05:07:38.000Z","updated_at":"2024-01-16T23:29:49.835Z","dependencies_parsed_at":"2023-10-02T22:29:49.231Z","dependency_job_id":"3714e9da-768a-4bd8-a90b-b02ca5ed68a8","html_url":"https://github.com/davidmoten/rxjava2-jdbc","commit_stats":null,"previous_names":[],"tags_count":52,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Frxjava2-jdbc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Frxjava2-jdbc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Frxjava2-jdbc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Frxjava2-jdbc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidmoten","download_url":"https://codeload.github.com/davidmoten/rxjava2-jdbc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247166168,"owners_count":20894654,"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":["concurrency","database","java","jdbc","reactive","reactive-programming","reactive-streams","rxjava"],"created_at":"2024-08-01T19:00:54.594Z","updated_at":"2025-04-04T11:12:32.511Z","avatar_url":"https://github.com/davidmoten.png","language":"Java","readme":"ifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n:toc:\n:toc-placement!:\n\n= rxjava2-jdbc\n\nimage:https://github.com/davidmoten/rxjava2-jdbc/actions/workflows/ci.yml/badge.svg[\"CI\",link=\"https://github.com/davidmoten/rxjava2-jdbc/actions/workflows/ci.yml\"]\nimage:https://codecov.io/gh/davidmoten/rxjava2-jdbc/branch/master/graph/badge.svg[\"CodeCov\",link=\"https://codecov.io/gh/davidmoten/rxjava2-jdbc\"]\nimage:https://maven-badges.herokuapp.com/maven-central/com.github.davidmoten/rxjava2-jdbc/badge.svg?style=flat[link=\"https://maven-badges.herokuapp.com/maven-central/com.github.davidmoten/rxjava2-jdbc\"]\n\nJDBC is so much simpler with _rxjava2-jdbc_.\n\nFor Rxjava 3.x see https://github.com/davidmoten/rxjava3-jdbc[rxjava3-jdbc].\n\nStatus: _published to Maven Central_\n\nMaven site reports are https://davidmoten.github.io/rxjava2-jdbc/index.html[here] including https://davidmoten.github.io/rxjava2-jdbc/apidocs/index.html[javadoc].\n\ntoc::[]\n\n== How to build\n\nUse maven:\n```bash\nmvn clean install\n```\nThe project builds fine on Java 8 to 17.\n\n== Getting started\nUse this maven dependency:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.davidmoten\u003c/groupId\u003e\n  \u003cartifactId\u003erxjava2-jdbc\u003c/artifactId\u003e\n  \u003cversion\u003eVERSION_HERE\u003c/version\u003e\n\u003c/dependency\u003e\n```\nIf you want to use the built-in test database then add the Apache Derby dependency (otherwise you'll need the jdbc dependency for the database you want to connect to):\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.apache.derby\u003c/groupId\u003e\n  \u003cartifactId\u003ederby\u003c/artifactId\u003e\n  \u003cversion\u003e10.14.2.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n== Database\n\nTo start things off you need a `Database` instance. Given the jdbc url of your database you can create a `Database` object like this:\n\n```java\nDatabase db = Database.from(url, maxPoolSize);\n```\n\n== Support for playing with rxjava2-jdbc!\n\nIf you want to have a play with a built-in test database then do this:\n\n```java\nDatabase db = Database.test(maxPoolSize);\n```\nTo use the built-in test database you will need the Apache Derby dependency:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.apache.derby\u003c/groupId\u003e\n  \u003cartifactId\u003ederby\u003c/artifactId\u003e\n  \u003cversion\u003e10.14.2.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nThe test database has a few tables (see link:rxjava2-jdbc/src/main/resources/database-test.sql[script]) including `Person` and `Address` with three rows in `Person` and two rows in `Address`:\n\nimage::https://raw.githubusercontent.com/davidmoten/rxjava2-jdbc/master/rxjava2-jdbc/src/docs/tables.png?raw=true[schema]\n\nEach time you call `Database.test(maxPoolSize)` you will have a fresh new database to play with that is loaded with data as described above.\n\n== A query example\n\nLet's use a `Database` instance to perform a select query on the `Person` table and write the names to the console:\n\n```java\nDatabase db = Database.test();\ndb.select(\"select name from person\")\n  .getAs(String.class)\n  .blockingForEach(System.out::println);\n```\n\nOutput is\n```\nFRED\nJOSEPH\nMARMADUKE\n```\n\nNote the use of `blockingForEach`. This is only for demonstration purposes. When a query is executed it is executed asynchronously on a scheduler that you can specify if desired. The default scheduler is:\n\n```java\nSchedulers.from(Executors.newFixedThreadPool(maxPoolSize));\n```\nWhile you are processing reactively you should avoid blocking calls but domain boundaries sometimes force this to happen (e.g. accumulate the results and return them as xml over the network from a web service call). Bear in mind also that if you are worried about the complexities of debugging RxJava programs then you might wish to make brief limited forays into reactive code. That's completely fine too. What you lose in efficiency you may gain in simplicity.\n\n== Asynchrony\nThe query flowables returned by the `Database` all run asynchronously. This is required because of the use of non-blocking connection pools. When a connection is returned to the pool and then checked-out by another query that checkout must occur on a different thread so that stack overflow does not occur. See the \u003c\u003cNon-blocking connection pools\u003e\u003e section for more details.\n\n\n== Nulls\nRxJava2 does not support streams of nulls. If you want to represent nulls in your stream then use `java.util.Optional`.\n\nIn the special case where a single nullable column is being returned and mapped to a class via `getAs` you should instead use `getAsOptional`:\n\n```java\nDatabase.test() \n  .select(\"select date_of_birth from person where name='FRED'\")\n  .getAsOptional(Instant.class)\n  .blockingForEach(System.out::println);\n```\nOutput:\n```\nOptional.empty\n```\nNulls will happily map to Tuples (see the next section) when you have two or more columns.\n\n=== Null parameters\nYou can specify an explicit null parameter like this:\n\n```java\nDatabase.test()\n  .update(\"update person set date_of_birth = ?\") \n  .parameter(null)\n  .counts()\n  .blockingForEach(System.out::println);\n``` \nor using named parameters:\n```java\nDatabase.test()\n  .update(\"update person set date_of_birth = :dob\") \n  .parameter(Parameter.create(\"dob\", null))\n  .counts()\n  .blockingForEach(System.out::println);\n``` \nIf you use a stream of parameters then you have to be more careful (nulls are not allowed in RxJava streams):\\\n```java\nDatabase.test()\n  .update(\"update person set date_of_birth = ?\") \n  .parameterStream(Flowable.just(Parameter.NULL))\n  .counts()\n  .blockingForEach(System.out::println);\n``` \n== Tuple support\n\nWhen you specify more types in the `getAs` method they are matched to the columns in the returned result set from the query and combined into a `Tuple` instance. Here's an example that returns `Tuple2`:\n\n```java\nDatabase db = Database.test();\ndb.select(\"select name, score from person\")\n  .getAs(String.class, Integer.class)\n  .blockingForEach(System.out::println);\n```\nOutput\n```\nTuple2 [value1=FRED, value2=21]\nTuple2 [value1=JOSEPH, value2=34]\nTuple2 [value1=MARMADUKE, value2=25]\n```\nTuples are defined from `Tuple2` to `Tuple7` and for above that to `TupleN`.\n\n== Manual mapping\n\nYou can map each row of the JDBC ResultSet to your own object using the `get` method:\n\n```java\nDatabase db = Database.test();\ndb.select(\"select name, score from person order by name\")\n  .get(rs -\u003e new Person(rs.getString(\"name\"), rs.getInt(\"score\")))\n  .doOnNext(System.out::println) //\n  .subscribe();\n```\nBy the way it is definitely not a good idea to hang on to a bunch of rs objects as their state will always be that of the latest read row. For this reason you immediately map it to something else either by manually mapping or automapping.\n\n== Automap\n\nTo map the result set values to an interface, first declare an interface:\n\n```java\ninterface Person {\n  @Column\n  String name();\n\n  @Column\n  int score();\n}\n```\n\nIn the query use the `autoMap` method and let's use some of the built-in testing methods of RxJava2 to confirm we got what we expected:\n\n```java\nDatabase db = Database.test();\ndb.select(\"select name, score from person order by name\")\n  .autoMap(Person.class)\n  .doOnNext(System.out::println)\n  .firstOrError()\n  .map(Person::score) \n  .test()\n  .assertValue(21) \n  .assertComplete();\n```\n\nIf your interface method name does not exactly match the column name (underscores and case are ignored) then you can add more detail to the `Column` annotation:\n\n```java\ninterface Person {\n  @Column(\"name\")\n  String fullName();\n\n  @Column(\"score\")\n  int examScore();\n}\n```\n\nYou can also refer to the 1-based position of the column in the result set instead of its name:\n```java\ninterface Person {\n  @Index(1)\n  String fullName();\n\n  @Index(2)\n  int examScore();\n}\n```\n\nIn fact, you can mix use of named columns and indexed columns in automapped interfaces.\n\nIf you don't configure things correctly these exceptions may be emitted and include extra information in the error message about the affected automap interface:\n\n* `AnnotationsNotFoundException`\n* `ColumnIndexOutOfRangeException`\n* `ColumnNotFoundException`\n* `ClassCastException`\n* `AutomappedInterfaceInaccessibleException`\n\n\n=== Automapped toString\nThe `toString()` method is implemented for automapped objects. For example the `toString` method for a `Person` object produces something like:\n\n```\nPerson[name=FRED, score=21]\n```\n\n=== Automapped equals/hashCode\nThe `equals` and `hashCode` methods on automapped objects have been implemented based on method value comparisons. For example\n\n* `Person[name=FRED, score=21]` is equal to `Person[name=FRED, score=21]`\n* `Person[name=FRED, score=21]` is not equal to `Person[name=FRED, score=22]`\n* `Person[name=FRED, score=21]` is not equal to `Person2[name=FRED, score=21]`\n\nNote that if you try to compare an automapped object with a custom implementation of the automapped interface then the custom implementation must implement equals/hashCode in the same way. In short, avoid doing that!\n\n=== Automapped interface with default methods\n\n* Java 8 - Calling a default method on an automapped interface is supported provided the interface is public and you use the default SecurityManager.\n* Java 9 - not supported yet (TODO)\n\n=== Automap with annotated query\n\nThe automapped interface can be annotated with the select query:\n\n```java\n@Query(\"select name, score from person order by name\")\ninterface Person {\n   @Column\n   String name();\n\n   @Column\n   int score();\n}\n```\n\nTo use the annotated interface:\n\n```java\nDatabase\n  .test()\n  .select(Person.class)\n  .get()\n  .map(Person::name)\n  .blockingForEach(System.out::println);\n```\n\nOutput:\n\n```\nFRED\nJOSEPH\nMARMADUKE\n```\n\nIn fact the `.map` is not required if you use a different overload of `get`:\n\n```java\nDatabase\n  .test()\n  .select(Person.class)\n  .get(Person::name)\n  .blockingForEach(System.out::println);\n```\n=== Automap with Kotlin\nSee https://github.com/davidmoten/rxjava2-jdbc/blob/master/rxjava2-jdbc-kotlin-example/src/test/kotlin/RxJava2JdbcKotlinTest.kt[example]. Below is how you annotate an interface in Kotlin for automap:\n\n```kotlin\n@Query(\"select name from person order by name\")\ninterface Person {\n    @Column(\"name\")\n    fun nm() : String\n}\n```\n\n=== Auto mappings\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.Date```,```java.sql.Time```,```java.sql.Timestamp```  ==\u003e ```java.time.Instant```\n* ```java.sql.Date```,```java.sql.Time```,```java.sql.Timestamp```  ==\u003e ```java.time.ZonedDateTime```\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\n== Parameters\n\nParameters are passed to individual queries but can also be used as a streaming source to prompt the query to be run many times.\n\nParameters can be named or anonymous. Named parameters are not supported natively by the JDBC specification but _rxjava2-jdbc_ does support them.\n\nThis is sql with a named parameter:\n\n```sql\nselect name from person where name=:name\n```\n\nThis is sql with an anonymous parameter:\n\n```sql\nselect name from person where name=?\n```\n\n=== Explicit anonymous parameters\n\nIn the example below the query is first run with `name='FRED'` and then `name=JOSEPH`. Each query returns one result which is printed to the console.\n\n```java\nDatabase.test()\n  .select(\"select score from person where name=?\") \n  .parameters(\"FRED\", \"JOSEPH\")\n  .getAs(Integer.class)\n  .blockingForEach(System.out::println);\n```\nOutput is:\n```\n21\n34\n```\n\n=== Flowable anonymous parameters\n\nYou can specify a stream as the source of parameters:\n\n```java\nDatabase.test()\n  .select(\"select score from person where name=?\") \n  .parameterStream(Flowable.just(\"FRED\",\"JOSEPH\").repeat())\n  .getAs(Integer.class)\n  .take(3)\n  .blockingForEach(System.out::println);\n```\n\nOutput is:\n```\n21\n34\n21\n```\n\n=== Mixing explicit and Flowable parameters\n\n```java\nDatabase.test()\n  .select(\"select score from person where name=?\") \n  .parameterStream(Flowable.just(\"FRED\",\"JOSEPH\"))\n  .parameters(\"FRED\", \"JOSEPH\")\n  .getAs(Integer.class)\n  .blockingForEach(System.out::println);\n```\nOutput is:\n```\n21\n34\n21\n34\n```\n=== Multiple parameters per query\n\nIf there is more than one parameter per query:\n\n```java\nDatabase.test()\n  .select(\"select score from person where name=? and score=?\") \n  .parameterStream(Flowable.just(\"FRED\", 21, \"JOSEPH\", 34).repeat())\n  .getAs(Integer.class)\n  .take(3)\n  .blockingForEach(System.out::println);\n```\nor you can group the parameters into lists (each list corresponds to one query) yourself:\n\n```java\nDatabase.test()\n  .select(\"select score from person where name=? and score=?\") \n  .parameterListStream(Flowable.just(Arrays.asList(\"FRED\", 21), Arrays.asList(\"JOSEPH\", 34)).repeat())\n  .getAs(Integer.class)\n  .take(3)\n  .blockingForEach(System.out::println);\n```\n\n=== Running a query many times that has no parameters\nIf the query has no parameters you can use the parameters to drive the number of query calls (the parameter values themselves are ignored):\n\n```java\nDatabase.test()\n  .select(\"select count(*) from person\") \n  .parameters(\"a\", \"b\", \"c\")\n  .getAs(Integer.class)\n  .blockingForEach(System.out::println);\n```\n\nOutput:\n```\n3\n3\n3\n```\n\n=== Collection parameters\nCollection parameters are useful for supplying to IN clauses. For example:\n\n```java\nDatabase.test()\n  .select(\"select score from person where name in (?) order by score\")\n  .parameter(Sets.newHashSet(\"FRED\", \"JOSEPH\"))\n  .getAs(Integer.class)\n  .blockingForEach(System.out::println);\n```\nor with named parameters:\n```java\nDatabase.test()\n  .update(\"update person set score=0 where name in (:names)\")\n  .parameter(\"names\", Lists.newArrayList(\"FRED\", \"JOSEPH\"))\n  .counts()\n  .blockingForEach(System.out::println);\n```\nYou need to pass an implementation of `java.util.Collection` to one of these parameters (for example `java.util.List` or `java.util.Set`).\n\nUnder the covers _rxjava2-jdbc_ does not use `PreparedStatement.setArray` because of the patchy support for this method (not supported by DB2 or MySQL for instance) and the extra requirement of specifying a column type.\n\nNote that databases normally have a limit on the number of parameters in a statement (or indeed the size of array that can be passed in `setArray`). For Oracle it's O(1000), H2 it is O(20000).\n\n`select` and `update` statements are supported as of 0.1-RC23. If you need callable statement support raise an issue.\n\n== Non-blocking connection pools\n\nA new exciting feature of _rxjava2-jdbc_ is the availability of non-blocking connection pools. \n\nIn normal non-reactive database programming a couple of different threads (started by servlet calls for instance) will _race_ for the next available connection from a pool of database connections. If no unused connection remains in the pool then the standard non-reactive approach is to *block the thread* until a connection becomes available. \n\nBlocking a thread is a resource issue as each blocked thread holds onto ~0.5MB of stack and may incur context switch and memory-access delays (adds latency to thread processing) when being switched to. For example 100 blocked threads hold onto ~50MB of memory (outside of java heap).\n\n_rxjava-jdbc2_ uses non-blocking JDBC connection pools by default (but is configurable to use whatever you want). What happens in practice is that for each query a subscription is made to a `MemberSingle` instance controlled by the `NonBlockingConnectionPool` object that emits connections when available to its subscribers (first in best dressed). So the definition of the processing of that query is stored on a queue to be started when a connection is available. Adding the Flowable definition of your query to the queue can be quite efficient in terms of memory use compared to the memory costs of thread per query. For example a heap dump of 1000 queued simple select statements from the person table in the test database used 429K of heap. That is 429 bytes per query.\n\nThe simplest way of creating a `Database` instance with a non-blocking connection pool is:\n\n```java\nDatabase db = Database.from(url, maxPoolSize);\n```\n\nIf you want to play with the in-memory built-in test database (requires Apache Derby dependency) then:\n\n```java\nDatabase db = Database.test(maxPoolSize);\n\n```\nIf you want more control over the behaviour of the non-blocking connection pool:\n\n```java\nDatabase db = Database\n  .nonBlocking()\n  // the jdbc url of the connections to be placed in the pool\n  .url(url)\n  // an unused connection will be closed after thirty minutes\n  .maxIdleTime(30, TimeUnit.MINUTES)\n  // connections are checked for healthiness on checkout if the connection \n  // has been idle for at least 5 seconds\n  .healthCheck(DatabaseType.ORACLE)\n  .idleTimeBeforeHealthCheck(5, TimeUnit.SECONDS)\n  // if a connection fails creation then retry after 30 seconds\n  .createRetryInterval(30, TimeUnit.SECONDS)\n  // the maximum number of connections in the pool\n  .maxPoolSize(3)\n  .build();\n```\n\nNote that the health check varies from database to database. The following databases are directly supported with `DatabaseType` instances:\n\n* DB2\n* Derby \n* HSQLDB\n* H2 \n* Informix\n* MySQL\n* Oracle \n* Postgres\n* Microsoft SQL Server\n* SQLite\n\n=== Demonstration\n\nLets create a database with a non-blocking connection pool of size 1 only and demonstrate what happens when two queries run concurrently. We use the in-built test database for this one \nso you can copy and paste this code to your ide and it will run (in a main method or unit test say):\n\n```java\n// create database with non-blocking connection pool \n// of size 1\nDatabase db = Database.test(1); \n\n// start a slow query\ndb.select(\"select score from person where name=?\") \n  .parameter(\"FRED\") \n  .getAs(Integer.class) \n   // slow things down by sleeping\n  .doOnNext(x -\u003e Thread.sleep(1000)) \n   // run in background thread\n  .subscribeOn(Schedulers.io()) \n  .subscribe();\n\n// ensure that query starts\nThread.sleep(100);\n\n// query again while first query running\ndb.select(\"select score from person where name=?\") \n  .parameter(\"FRED\") \n  .getAs(Integer.class) \n  .doOnNext(x -\u003e System.out.println(\"emitted on \" + Thread.currentThread().getName())) \n  .subscribe();\n\nSystem.out.println(\"second query submitted\");\n\n// wait for stuff to happen asynchronously\nThread.sleep(5000);\n```\n\nThe output of this is \n\n```\nsecond query submitted\nemitted on RxCachedThreadScheduler-1\n```\n\nWhat has happened is that \n\n* the second query registers itself as something that will run as soon as a connection is released (by the first query). \n* no blocking occurs and we immediately see the first line of output\n* the second query runs after the first\n* in fact we see that the second query runs on the same Thread as the first query as a direct consequence of non-blocking design  \n\n\n== Large objects support\nBlobs 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 = ...\nFlowable\u003cInteger\u003e count = db\n  .update(\"insert into person_clob(name,document) values(?,?)\")\n  .parameters(\"FRED\", document)\n  .count();\n```\nIf your document is nullable then you should use `Database.clob(document)`:\n```java\nString document = ...\nFlowable\u003cInteger\u003e count = db\n  .update(\"insert into person_clob(name,document) values(?,?)\")\n  .parameters(\"FRED\", Database.clob(document))\n  .count();\n```\nUsing a ```java.io.Reader```:\n```java\nReader reader = ...;\nFlowable\u003cInteger\u003e count = db\n  .update(\"insert into person_clob(name,document) values(?,?)\")\n  .parameters(\"FRED\", reader)\n  .count();\n```\n=== Insert a Null Clob\n```java\nFlowable\u003cInteger\u003e count = db\n  .update(\"insert into person_clob(name,document) values(?,?)\")\n  .parameters(\"FRED\", Database.NULL_CLOB)\n  .count();\n```\nor \n```java\nFlowable\u003cInteger\u003e count = db\n  .update(\"insert into person_clob(name,document) values(?,?)\")\n  .parameters(\"FRED\", Database.clob(null))\n  .count();\n```\n\n=== Read a Clob\n```java\nFlowable\u003cString\u003e document = \n  db.select(\"select document from person_clob\")\n    .getAs(String.class);\n```\nor\n```java\nFlowable\u003cReader\u003e document = \n  db.select(\"select document from person_clob\")\n    .getAs(Reader.class);\n```\n=== Read a Null Clob\nFor the special case where you want to return one value from a select statement and that value is a nullable CLOB then use `getAsOptional`:\n```java\ndb.select(\"select document from person_clob where name='FRED'\")\n  .getAsOptional(String.class)\n```\n\n=== Insert a Blob\nSimilarly for Blobs (_document_ column below is of type ```BLOB```):\n```java\nbyte[] bytes = ...\nFlowable\u003cInteger\u003e count = db\n  .update(\"insert into person_blob(name,document) values(?,?)\")\n  .parameters(\"FRED\", Database.blob(bytes))\n  .count();\n```\n=== Insert a Null Blob\nThis requires _either_ a special call (```parameterBlob(String)``` to identify the parameter as a CLOB:\n```java\nFlowable\u003cInteger\u003e count = db\n  .update(\"insert into person_blob(name,document) values(?,?)\")\n  .parameters(\"FRED\", Database.NULL_BLOB)\n  .count();\n```\nor \n```java\nFlowable\u003cInteger\u003e count = db\n  .update(\"insert into person_clob(name,document) values(?,?)\")\n  .parameters(\"FRED\", Database.blob(null))\n  .count();\n```\n=== Read a Blob\n```java\nFlowable\u003cbyte[]\u003e document = \n  db.select(\"select document from person_clob\")\n    .getAs(byte[].class);\n```\nor\n```java\nFlowable\u003cInputStream\u003e document = \n  db.select(\"select document from person_clob\")\n    .getAs(InputStream.class);\n```\n\n== Returning generated keys\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\nFlowable\u003cInteger\u003e keys = \n    db.update(\"insert into note(text) values(?)\")\n      .parameters(\"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\n== Transactions\nTransactions are a critical feature of relational databases. \n\nWhen we're talking RxJava we need to consider the behaviour of individual JDBC objects when called by different threads, possibly concurrently. The approach taken by _rxjava2-jdbc_ outside of a transaction safely uses Connection pools (in a non-blocking way). Inside a transaction we must make all calls to the database using the same Connection object so the behaviour of that Connection when called from different threads is important. Some JDBC drivers provide thread-safety on JDBC objects by synchronizing every call.\n\nThe safest approach with transactions is to perform all db interaction synchronously. Asynchronous processing within transactions was problematic in _rxjava-jdbc_ because `ThreadLocal` was used to hold the Connection. Asynchronous processing with transactions _is_ possible with _rxjava2-jdbc_ but should be handled with care given that your JDBC driver may block or indeed suffer from race conditions that most users don't encounter.\n\nLet's look at some examples. The first example uses a transaction across two select statement calls:\n\n```java\nDatabase.test()\n  .select(\"select score from person where name=?\") \n  .parameters(\"FRED\", \"JOSEPH\") \n  .transacted() \n  .getAs(Integer.class) \n  .blockingForEach(tx -\u003e \n    System.out.println(tx.isComplete() ? \"complete\" : tx.value()));\n```\n\nOutput:\n```\n21\n34\ncomplete\n```\n\nNote that the commit/rollback of the transaction happens automatically.\n\nWhat we see above is that each emission from the select statement is wrapped with a Tx object including the terminal event (error or complete). This is so you can for instance perform an action using the same transaction. \n\nLet's see another example that uses the `Tx` object to update the database. We are going to do something a bit laborious that would normally be done in one update statement (`update person set score = -1`) just to demonstrate usage:\n\n```java\nDatabase.test()\n  .select(\"select name from person\") \n  // don't emit a Tx completed event\n  .transactedValuesOnly() \n  .getAs(String.class) \n  .flatMap(tx -\u003e tx\n    .update(\"update person set score=-1 where name=:name\") \n    .parameter(\"name\", tx.value()) \n    // don't wrap value in Tx object \n    .valuesOnly() \n    .counts()) \n  .toList()\n  .blockingForEach(System.out::println);\n```\n\nOutput:\n```\n[1, 1, 1]\n\n```\n\n== Callable Statements\n\nCallable statement support is a major addition to the code base as of 0.1-RC23.\n\nCallable support is present only outside of transactions (transaction support coming later). If you're keen for it, raise an issue. The primary impediment is the duplication of a bunch of chained builders for the transacted case.\n\nFor example:\n\n```java\nFlowable\u003cTuple2\u003cInteger,Integer\u003e\u003e tuples = \n  db.call(\"call in1out2(?,?,?)\") \n    .in() \n    .out(Type.INTEGER, Integer.class) \n    .out(Type.INTEGER, Integer.class) \n    .input(0, 10, 20);\n```\n\nNote above that each question mark in the call statement correponds in order with a call to `in()` or `out(...)`. Once all parameters have been defined then the `input(0, 10, 20)` call drives the running of the query with that input. The output `Flowable` is strongly typed according to the `out` parameters specified.\n\nWhen you start specifying output `ResultSet` s from the call then you lose output parameter strong typing but gain `ResultSet` mapped strong typing as per normal `select` statements in _rxjava2-jdbc_.\n\nHere's an example for one `in` parameter and two output `ResultSet` s with `autoMap`. You can of course use `getAs` instead (or `get`): \n\n```java\nFlowable\u003cString\u003e namePairs = \n  db\n    .call(\"call in1out0rs2(?)\")\n    .in()\n    .autoMap(Person2.class)\n    .autoMap(Person2.class)\n    .input(0, 10, 20)\n    .flatMap(x -\u003e \n      x.results1()\n       .zipWith(x.results2(), (y, z) -\u003e y.name() + z.name()));    \n```\nThe above example is pretty nifty in that we can zip the two result sets resulting from the call and of course the whole thing was easy to define (as opposed to normal JDBC).\n\nYou just saw `autoMap` used to handle an output `ResultSet` but `getAs` works too:\n\n```java\nFlowable\u003cString\u003e namePairs = \n  db\n    .call(\"call in1out0rs2(?)\")\n    .in()\n    .getAs(String.class, Integer.class)\n    .getAs(String.class, Integer.class\n    .input(0, 10, 20)\n    .flatMap(x -\u003e \n      x.results1()\n       .zipWith(x.results2(), (y, z) -\u003e y._1() + z._1()));    \n```\n\nYou can explore more examples of this in [`DatabaseTest.java`](rxjava2-jdbc/src/test/java/org/davidmoten/rx/jdbc/DatabaseTest.java). Search for `.call`.\n\n== Using raw JDBC\n\nA few nifty things in JDBC may not yet directly supported by *rxjava2-jdbc* but you can get acccess to the underlying `Connection` s from the `Database` object by using `Database.apply` or `Database.member()`.\n\nHere's an example where you want to return something from a `Connection` (say you called a stored procedure and returned an integer):\n\n```java\nDatabase db = ...\nSingle\u003cInteger\u003e count =\n  db.apply(\n     con -\u003e {\n       //do whatever you want with the connection\n       // just don't close it!\n       return con.getHoldability();\n     });\n```\n\nIf you don't want to return something then use a different overload of `apply`:\n\n```java\nCompletable c = \n  db.apply(con -\u003e {\n       //do whatever you want with the connection\n     }); \n```\nHere are lower level versions of the above examples where you take on the responsibility of returning the connection to the pool.\n\n```java\nDatabase db = ...\nSingle\u003cInteger\u003e count = db.member() \n  .map(member -\u003e {\n     Connection con = member.value();\n     try {\n       //do whatever you want with the connection\n       return count;\n     } finally {\n       // don't close the connection, just hand it back to the pool\n       // and don't use this member again!\n       member.checkin();\n     });\n```\n\nand\n\n```java\nCompletable completable = db.member() \n  .doOnSuccess(member -\u003e {\n     Connection con = member.value();\n     try {\n       //do whatever you want with the connection\n     } finally {\n       // don't close the connection, just hand it back to the pool\n       // and don't use this member again!\n       member.checkin();\n     }).ignoreElements();\n```\n\n== Logging\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 https://github.com/davidmoten/rxjava2-jdbc/blob/master/pom.xml[rxjava2-jdbc/pom.xml].\n\n\n","funding_links":[],"categories":["Java"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidmoten%2Frxjava2-jdbc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidmoten%2Frxjava2-jdbc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidmoten%2Frxjava2-jdbc/lists"}