{"id":13694748,"url":"https://github.com/mirromutth/r2dbc-mysql","last_synced_at":"2025-05-03T04:30:53.419Z","repository":{"id":37285396,"uuid":"163492945","full_name":"mirromutth/r2dbc-mysql","owner":"mirromutth","description":"R2DBC MySQL Implementation","archived":true,"fork":false,"pushed_at":"2023-12-13T02:32:29.000Z","size":2177,"stargazers_count":656,"open_issues_count":58,"forks_count":98,"subscribers_count":27,"default_branch":"main","last_synced_at":"2024-11-12T21:39:11.672Z","etag":null,"topics":["database","java","mysql","non-blocking","r2dbc","reactive","reactive-streams"],"latest_commit_sha":null,"homepage":null,"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/mirromutth.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-12-29T08:30:57.000Z","updated_at":"2024-09-21T07:49:04.000Z","dependencies_parsed_at":"2024-01-14T19:12:20.844Z","dependency_job_id":"e75b1f26-0c7c-4f9d-894d-d4e23be00331","html_url":"https://github.com/mirromutth/r2dbc-mysql","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirromutth%2Fr2dbc-mysql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirromutth%2Fr2dbc-mysql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirromutth%2Fr2dbc-mysql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirromutth%2Fr2dbc-mysql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mirromutth","download_url":"https://codeload.github.com/mirromutth/r2dbc-mysql/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252144460,"owners_count":21701417,"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":["database","java","mysql","non-blocking","r2dbc","reactive","reactive-streams"],"created_at":"2024-08-02T17:01:40.036Z","updated_at":"2025-05-03T04:30:51.096Z","avatar_url":"https://github.com/mirromutth.png","language":"Java","funding_links":[],"categories":["Java"],"sub_categories":[],"readme":"# ⚠️ Archived ⚠️\n\nThe current repository is NO LONGER MAINTAINED, please switch to `io.asyncer:r2dbc-mysql`.\n\nSee also [https://github.com/asyncer-io/r2dbc-mysql](https://github.com/asyncer-io/r2dbc-mysql). It is currently being maintained by [@jchrys](https://github.com/jchrys).\n\n# Reactive Relational Database Connectivity MySQL Implementation\n\n[![Maven Central](https://img.shields.io/maven-central/v/dev.miku/r2dbc-mysql?color=green\u0026label=Maven%20Central)](https://search.maven.org/search?q=g:%22dev.miku%22%20AND%20a:%22r2dbc-mysql%22)\n[![Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)\n[![Unit tests status](https://github.com/mirromutth/r2dbc-mysql/workflows/Unit%20tests/badge.svg)](https://github.com/mirromutth/r2dbc-mysql/actions?query=workflow%3A%22Unit+tests%22)\n\nThis project contains the [MySQL][m] implementation of the [R2DBC SPI](https://github.com/r2dbc/r2dbc-spi).\nThis implementation is not intended to be used directly, but rather to be\nused as the backing implementation for a humane client library to\ndelegate to. See [R2DBC Homepage](https://r2dbc.io).\n\nThis driver provides the following features:\n\n- [x] Unix domain socket.\n- [x] Execution of simple or batch statements without bindings.\n- [x] Execution of prepared statements with bindings.\n- [x] Reactive LOB types (e.g. BLOB, CLOB)\n- [x] All charsets from MySQL, like `utf8mb4_0900_ai_ci`, `latin1_general_ci`, `utf32_unicode_520_ci`, etc.\n- [x] All authentication types for MySQL, like `caching_sha2_password`, `mysql_native_password`, etc.\n- [x] General exceptions with error code and standard SQL state mappings.\n- [x] Secure connection with verification (SSL/TLS), auto-select TLS version for community and enterprise editions.\n- [x] SSL tunnel for proxy protocol of MySQL.\n- [x] Transactions with savepoint.\n- [x] Native ping command that can be verifying when argument is `ValidationDepth.REMOTE`\n- [x] Extensible, e.g. extend built-in `Codec`(s).\n\n## Version compatibility / Integration tests states\n\n[![MySQL 5.5 status](https://github.com/mirromutth/r2dbc-mysql/workflows/MySQL%205.5/badge.svg)](https://github.com/mirromutth/r2dbc-mysql/actions?query=workflow%3A%22MySQL+5.5%22)\n[![MySQL 5.6 status](https://github.com/mirromutth/r2dbc-mysql/workflows/MySQL%205.6/badge.svg)](https://github.com/mirromutth/r2dbc-mysql/actions?query=workflow%3A%22MySQL+5.6%22)\n[![MySQL 5.7 status](https://github.com/mirromutth/r2dbc-mysql/workflows/MySQL%205.7/badge.svg)](https://github.com/mirromutth/r2dbc-mysql/actions?query=workflow%3A%22MySQL+5.7%22)\n[![MySQL 8.0 status](https://github.com/mirromutth/r2dbc-mysql/workflows/MySQL%208.0/badge.svg)](https://github.com/mirromutth/r2dbc-mysql/actions?query=workflow%3A%22MySQL+8.0%22)\n\nIn fact, it supports lower versions, in the theory, such as 4.1, 4.0, etc.\n\nHowever, Docker-certified images do not have these versions lower than 5.5.0, so tests are not integrated on these versions.\n\n## Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003edev.miku\u003c/groupId\u003e\n    \u003cartifactId\u003er2dbc-mysql\u003c/artifactId\u003e\n    \u003cversion\u003e0.8.2.RELEASE\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nIf you'd rather like the latest snapshots of the upcoming major version, use SonaType Maven snapshot repository and declare the appropriate dependency version.\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003edev.miku\u003c/groupId\u003e\n    \u003cartifactId\u003er2dbc-mysql\u003c/artifactId\u003e\n    \u003cversion\u003e${r2dbc-mysql.version}.BUILD-SNAPSHOT\u003c/version\u003e\n\u003c/dependency\u003e\n\n\u003crepository\u003e\n    \u003cid\u003esonatype-snapshots\u003c/id\u003e\n    \u003cname\u003eSonaType Snapshots\u003c/name\u003e\n    \u003curl\u003ehttps://oss.sonatype.org/content/repositories/snapshots\u003c/url\u003e\n    \u003csnapshots\u003e\n        \u003cenabled\u003etrue\u003c/enabled\u003e\n    \u003c/snapshots\u003e\n\u003c/repository\u003e\n```\n\n## Gradle\n\n### Groovy DSL\n\n```groovy\ndependencies {\n    implementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE'\n}\n```\n\n### Kotlin DSL\n\n```kotlin\ndependencies {\n    // Maybe should to use `compile` instead of `implementation` on the lower version of Gradle.\n    implementation(\"dev.miku:r2dbc-mysql:0.8.2.RELEASE\")\n}\n```\n\n## Getting Started\n\nHere is a quick teaser of how to use R2DBC MySQL in Java:\n\n### URL Connection Factory Discovery\n\n```java\n// Notice: the query string must be URL encoded\nConnectionFactory connectionFactory = ConnectionFactories.get(\n    \"r2dbcs:mysql://root:database-password-in-here@127.0.0.1:3306/r2dbc?\" +\n    \"zeroDate=use_round\u0026\" +\n    \"sslMode=verify_identity\u0026\" +\n    \"useServerPrepareStatement=true\u0026\" +\n    \"tlsVersion=TLSv1.3%2CTLSv1.2%2CTLSv1.1\u0026\" +\n    \"sslCa=%2Fpath%2Fto%2Fmysql%2Fca.pem\u0026\" +\n    \"sslKey=%2Fpath%2Fto%2Fmysql%2Fclient-key.pem\u0026\" +\n    \"sslCert=%2Fpath%2Fto%2Fmysql%2Fclient-cert.pem\u0026\" +\n    \"sslKeyPassword=key-pem-password-in-here\"\n)\n\n// Creating a Mono using Project Reactor\nMono\u003cConnection\u003e connectionMono = Mono.from(connectionFactory.create());\n```\n\n\u003e It is just example, see also Programmatic Connection Factory Discovery for more options.\n\nOr use unix domain socket like following:\n\n```java\n// Minimum configuration for unix domain socket\nConnectionFactory connectionFactory = ConnectionFactories.get(\"r2dbc:mysql://root@unix?unixSocket=%2Fpath%2Fto%2Fmysql.sock\")\n\nMono\u003cConnection\u003e connectionMono = Mono.from(connectionFactory.create());\n```\n\n### Programmatic Connection Factory Discovery\n\n```java\nConnectionFactoryOptions options = ConnectionFactoryOptions.builder()\n    .option(DRIVER, \"mysql\")\n    .option(HOST, \"127.0.0.1\")\n    .option(USER, \"root\")\n    .option(PORT, 3306)  // optional, default 3306\n    .option(PASSWORD, \"database-password-in-here\") // optional, default null, null means has no password\n    .option(DATABASE, \"r2dbc\") // optional, default null, null means not specifying the database\n    .option(CONNECT_TIMEOUT, Duration.ofSeconds(3)) // optional, default null, null means no timeout\n    .option(Option.valueOf(\"socketTimeout\"), Duration.ofSeconds(4)) // optional, default null, null means no timeout\n    .option(SSL, true) // optional, default sslMode is \"preferred\", it will be ignore if sslMode is set\n    .option(Option.valueOf(\"sslMode\"), \"verify_identity\") // optional, default \"preferred\"\n    .option(Option.valueOf(\"sslCa\"), \"/path/to/mysql/ca.pem\") // required when sslMode is verify_ca or verify_identity, default null, null means has no server CA cert\n    .option(Option.valueOf(\"sslCert\"), \"/path/to/mysql/client-cert.pem\") // optional, default null, null means has no client cert\n    .option(Option.valueOf(\"sslKey\"), \"/path/to/mysql/client-key.pem\") // optional, default null, null means has no client key\n    .option(Option.valueOf(\"sslKeyPassword\"), \"key-pem-password-in-here\") // optional, default null, null means has no password for client key (i.e. \"sslKey\")\n    .option(Option.valueOf(\"tlsVersion\"), \"TLSv1.3,TLSv1.2,TLSv1.1\") // optional, default is auto-selected by the server\n    .option(Option.valueOf(\"sslHostnameVerifier\"), \"com.example.demo.MyVerifier\") // optional, default is null, null means use standard verifier\n    .option(Option.valueOf(\"sslContextBuilderCustomizer\"), \"com.example.demo.MyCustomizer\") // optional, default is no-op customizer\n    .option(Option.valueOf(\"zeroDate\"), \"use_null\") // optional, default \"use_null\"\n    .option(Option.valueOf(\"useServerPrepareStatement\"), true) // optional, default false\n    .option(Option.valueOf(\"tcpKeepAlive\"), true) // optional, default false\n    .option(Option.valueOf(\"tcpNoDelay\"), true) // optional, default false\n    .option(Option.valueOf(\"autodetectExtensions\"), false) // optional, default false\n    .build();\nConnectionFactory connectionFactory = ConnectionFactories.get(options);\n\n// Creating a Mono using Project Reactor\nMono\u003cConnection\u003e connectionMono = Mono.from(connectionFactory.create());\n```\n\nOr use unix domain socket like following:\n\n```java\n// Minimum configuration for unix domain socket\nConnectionFactoryOptions options = ConnectionFactoryOptions.builder()\n    .option(DRIVER, \"mysql\")\n    .option(Option.valueOf(\"unixSocket\"), \"/path/to/mysql.sock\")\n    .option(USER, \"root\")\n    .build();\nConnectionFactory connectionFactory = ConnectionFactories.get(options);\n\nMono\u003cConnection\u003e connectionMono = Mono.from(connectionFactory.create());\n```\n\n### Programmatic Configuration\n\n```java\nMySqlConnectionConfiguration configuration = MySqlConnectionConfiguration.builder()\n    .host(\"127.0.0.1\")\n    .user(\"root\")\n    .port(3306) // optional, default 3306\n    .password(\"database-password-in-here\") // optional, default null, null means has no password\n    .database(\"r2dbc\") // optional, default null, null means not specifying the database\n    .serverZoneId(ZoneId.of(\"Continent/City\")) // optional, default null, null means query server time zone when connection init\n    .connectTimeout(Duration.ofSeconds(3)) // optional, default null, null means no timeout\n    .socketTimeout(Duration.ofSeconds(4)) // optional, default null, null means no timeout\n    .sslMode(SslMode.VERIFY_IDENTITY) // optional, default SslMode.PREFERRED\n    .sslCa(\"/path/to/mysql/ca.pem\") // required when sslMode is VERIFY_CA or VERIFY_IDENTITY, default null, null means has no server CA cert\n    .sslCert(\"/path/to/mysql/client-cert.pem\") // optional, default has no client SSL certificate\n    .sslKey(\"/path/to/mysql/client-key.pem\") // optional, default has no client SSL key\n    .sslKeyPassword(\"key-pem-password-in-here\") // optional, default has no client SSL key password\n    .tlsVersion(TlsVersions.TLS1_3, TlsVersions.TLS1_2, TlsVersions.TLS1_1) // optional, default is auto-selected by the server\n    .sslHostnameVerifier(MyVerifier.INSTANCE) // optional, default is null, null means use standard verifier\n    .sslContextBuilderCustomizer(MyCustomizer.INSTANCE) // optional, default is no-op customizer\n    .zeroDateOption(ZeroDateOption.USE_NULL) // optional, default ZeroDateOption.USE_NULL\n    .useServerPrepareStatement() // Use server-preparing statements, default use client-preparing statements\n    .tcpKeepAlive(true) // optional, controls TCP Keep Alive, default is false\n    .tcpNoDelay(true) // optional, controls TCP No Delay, default is false\n    .autodetectExtensions(false) // optional, controls extension auto-detect, default is true\n    .extendWith(MyExtension.INSTANCE) // optional, manual extend an extension into extensions, default using auto-detect\n    .build();\nConnectionFactory connectionFactory = MySqlConnectionFactory.from(configuration);\n\n// Creating a Mono using Project Reactor\nMono\u003cConnection\u003e connectionMono = Mono.from(connectionFactory.create());\n```\n\nOr use unix domain socket like following:\n\n```java\n// Minimum configuration for unix domain socket\nMySqlConnectionConfiguration configuration = MySqlConnectionConfiguration.builder()\n    .unixSocket(\"/path/to/mysql.sock\")\n    .user(\"root\")\n    .build();\nConnectionFactory connectionFactory = MySqlConnectionFactory.from(configuration);\n\nMono\u003cConnection\u003e connectionMono = Mono.from(connectionFactory.create());\n```\n\n### Configuration items\n\n| name | valid values | required | description |\n|---|---|---|---|\n| driver | A constant \"mysql\" | Required in R2DBC discovery | This driver needs to be discovered by name in R2DBC |\n| host | A hostname or IP | Required when `unixSocket` does not exists | The host of MySQL database server |\n| unixSocket | An absolute or relative path | Required when `host` does not exists | The `.sock` file of Unix Domain Socket |\n| port | A positive integer less than 65536 | Optional, default 3306 | The port of MySQL database server |\n| user | A valid MySQL username and not be empty | Required | Who wants to connect to the MySQL database |\n| password | Any printable string | Optional, default no password | The password of the MySQL database user |\n| database | A valid MySQL database name | Optional, default does not initialize database | Database used by the MySQL connection |\n| connectTimeout | A `Duration` which must be positive duration | Optional, default has no timeout | TCP connect timeout |\n| socketTimeout | A `Duration` which must be positive duration | Optional, default has no timeout | TCP socket timeout |\n| serverZoneId | An id of `ZoneId` | Optional, default query time zone when connection init | Server time zone id |\n| tcpKeepAlive | `true` or `false` | Optional, default disabled | Controls TCP KeepAlive |\n| tcpNoDelay | `true` or `false` | Optional, default disabled | Controls TCP NoDelay |\n| sslMode | A value of `SslMode` | Optional, default `PREFERRED` when using hosting connection, `DISABLED` when using Unix Domain Socket | SSL mode, see following notice |\n| sslCa | A path of local file which type is `PEM` | Required when `sslMode` is `VERIFY_CA` or `VERIFY_IDENTITY` | The CA cert of MySQL database server |\n| sslCert | A path of local file which type is `PEM` | Required when `sslKey` exists | The SSL cert of client |\n| sslKey | A path of local file which type is `PEM` | Required when `sslCert` exists | The SSL key of client |\n| sslKeyPassword | Any valid password for `PEM` file | Optional, default `sslKey` has no password | The password for client SSL key (i.e. `sslKey`) |\n| tlsVersion | Any value list of `TlsVersions` | Optional, default is auto-selected by the server | The TLS version for SSL, see following notice |\n| sslHostnameVerifier | A `HostnameVerifier` | Optional, default use RFC standard | Used only if `SslMode` is `VERIFY_CA` or higher |\n| sslContextBuilderCustomizer | A `Function\u003cSslContextBuilder, SslContextBuilder\u003e` | Optional, default is NO-OP function | Used only if `SslMode` is not `DISABLED` |\n| zeroDateOption | Any value of `ZeroDateOption` | Optional, default `USE_NULL` | The option indicates \"zero date\" handling, see following notice |\n| autodetectExtensions | `true` or `false` | Optional, default is `true` | Controls auto-detect `Extension`s |\n| useServerPrepareStatement | `true`, `false` or `Predicate\u003cString\u003e` | Optional, default is `false` | See following notice |\n\n- `SslMode` Considers security level and verification for SSL, make sure the database server supports SSL before you want change SSL mode to `REQUIRED` or higher. **The Unix Domain Socket only offers \"DISABLED\" available**\n  - `DISABLED` I don't care about security and don't want to pay the overhead for encryption\n  - `PREFERRED` I don't care about encryption but will pay the overhead of encryption if the server supports it. **Unavailable on Unix Domain Socket**\n  - `REQUIRED` I want my data to be encrypted, and I accept the overhead. I trust that the network will make sure I always connect to the server I want. **Unavailable on Unix Domain Socket**\n  - `VERIFY_CA` I want my data encrypted, and I accept the overhead. I want to be sure I connect to a server that I trust. **Unavailable on Unix Domain Socket**\n  - `VERIFY_IDENTITY` (the highest level, most like web browser): I want my data encrypted, and I accept the overhead. I want to be sure I connect to a server I trust, and that it's the one I specify. **Unavailable on Unix Domain Socket**\n  - `TUNNEL` Use SSL tunnel to connect to MySQL, it may be useful for some RDS that's using SSL proxy **Unavailable on Unix Domain Socket**\n- `TlsVersions` Considers TLS version names for SSL, can be **multi-values** in the configuration, make sure the database server supports selected TLS versions. Usually sorted from higher to lower. **Unavailable on Unix Domain Socket**\n  - `TLS1` (i.e. \"TLSv1\") Under generic circumstances, MySQL database supports it if database supports SSL\n  - `TLS1_1` (i.e. \"TLSv1.1\") Under generic circumstances, MySQL database supports it if database supports SSL\n  - `TLS1_2` (i.e. \"TLSv1.2\") Supported only in Community Edition `8.0.4` or higher, and Enterprise Edition `5.6.0` or higher\n  - `TLS1_3` (i.e. \"TLSv1.3\") Supported only available as of MySQL `8.0.16` or higher, and requires compiling MySQL using OpenSSL `1.1.1` or higher\n- `ZeroDateOption` Considers special handling when MySQL database server returning \"zero date\" (i.e. `0000-00-00 00:00:00`)\n  - `EXCEPTION` Just throw an exception when MySQL database server return \"zero date\"\n  - `USE_NULL` Use `null` when MySQL database server return \"zero date\"\n  - `USE_ROUND` **NOT** RECOMMENDED, only for compatibility. Use \"round\" date (i.e. `0001-01-01 00:00:00`) when MySQL database server return \"zero date\"\n- Prepare Statement: Considers based on server-preparing or client-preparing, some database server maybe not support server-preparing binary-query, such as Vitess\n  - `useClientPrepareStatement()` default preparing mode, use client-preparing text-query for parametrized statements\n  - `useServerPrepareStatement()` use server-preparing binary-query for parametrized statements\n  - `useServerPrepareStatement(Predicate\u003cString\u003e)` use server-preparing binary-query for parametrized statements, and enforce server-preparing usage for simple query (not parametrized statements). The usage is judged by `Predicate`, it's parameter is the simple SQL statement, enforce server-preparing if return `true`\n- `extendWith` Manual extend `Extension`, only available in **programmatic configuration**\n  - It is **NOT** RECOMMENDED, enable the `autodetectExtensions` is the best way for extensions\n  - The `Extensions` will not remove duplicates, make sure it would be not extended twice or more\n  - The auto-detected `Extension`s will not affect manual extends and will not remove duplicates\n\nShould use `enum` in [Programmatic](#programmatic-configuration) configuration that not like discovery configurations, except `TlsVersions` (All elements of `TlsVersions` will be always `String` which is case-sensitive).\n\n### Pooling\n\nSee [r2dbc-pool](https://github.com/r2dbc/r2dbc-pool).\n\n### Simple statement\n\n```java\nconnection.createStatement(\"INSERT INTO `person` (`first_name`, `last_name`) VALUES ('who', 'how')\")\n    .execute(); // return a Publisher include one Result\n```\n\n### Parametrized statement\n\n```java\nconnection.createStatement(\"INSERT INTO `person` (`birth`, `nickname`, `show_name`) VALUES (?, ?name, ?name)\")\n    .bind(0, LocalDateTime.of(2019, 6, 25, 12, 12, 12))\n    .bind(\"name\", \"Some one\") // Not one-to-one binding, call twice of native index-bindings, or call once of name-bindings.\n    .add()\n    .bind(0, LocalDateTime.of(2009, 6, 25, 12, 12, 12))\n    .bind(1, \"My Nickname\")\n    .bind(2, \"Naming show\")\n    .returnGeneratedValues(\"generated_id\")\n    .execute(); // return a Publisher include two Results.\n```\n\n- All parameters must be bound before execute, even parameter is `null` (use `bindNull` to bind `null`).\n- It will be using client-preparing by default, see `useServerPrepareStatement` in configuration.\n- In one-to-one binding, because native MySQL prepared statements use index-based parameters, *index-bindings* will have **better** performance than *name-bindings*.\n\n### Batch statement\n\n```java\nconnection.createBatch()\n    .add(\"INSERT INTO `person` (`first_name`, `last_name`) VALUES ('who', 'how')\")\n    .add(\"UPDATE `earth` SET `count` = `count` + 1 WHERE `id` = 'human'\")\n    .execute(); // return a Publisher include two Results.\n```\n\n\u003e The last `;` will be removed if and only if last statement contains ';', and statement has only whitespace follow the last `;`.\n\n### Transactions\n\n```java\nconnection.beginTransaction()\n    .then(Mono.from(connection.createStatement(\"INSERT INTO `person` (`first_name`, `last_name`) VALUES ('who', 'how')\").execute()))\n    .flatMap(Result::getRowsUpdated)\n    .thenMany(connection.createStatement(\"INSERT INTO `person` (`birth`, `nickname`, `show_name`) VALUES (?, ?name, ?name)\")\n        .bind(0, LocalDateTime.of(2019, 6, 25, 12, 12, 12))\n        .bind(\"name\", \"Some one\")\n        .add()\n        .bind(0, LocalDateTime.of(2009, 6, 25, 12, 12, 12))\n        .bind(1, \"My Nickname\")\n        .bind(2, \"Naming show\")\n        .returnGeneratedValues(\"generated_id\")\n        .execute())\n    .flatMap(Result::getRowsUpdated)\n    .then(connection.commitTransaction());\n```\n\n## Data Type Mapping\n\nThe default built-in `Codec`s  reference table shows the type mapping between [MySQL][m] and Java data types:\n\n| MySQL Type | Unsigned | Support Data Type |\n|---|---|---|\n| `INT` | `UNSIGNED` | [**`Long`**][java-Long-ref], [`BigInteger`][java-BigInteger-ref] |\n| `INT` | `SIGNED` | [**`Integer`**][java-Integer-ref], [`Long`][java-Long-ref], [`BigInteger`][java-BigInteger-ref] |\n| `TINYINT` | `UNSIGNED` | [**`Short`**][java-Short-ref], [`Integer`][java-Integer-ref], [`Long`][java-Long-ref], [`BigInteger`][java-BigInteger-ref], [`Boolean`][java-Boolean-ref] (Size is 1) |\n| `TINYINT` | `SIGNED` | [**`Byte`**][java-Byte-ref], [`Short`][java-Short-ref], [`Integer`][java-Integer-ref], [`Long`][java-Long-ref], [`BigInteger`][java-BigInteger-ref], [`Boolean`][java-Boolean-ref] (Size is 1) |\n| `SMALLINT` | `UNSIGNED` | [**`Integer`**][java-Integer-ref], [`Long`][java-Long-ref], [`BigInteger`][java-BigInteger-ref] |\n| `SMALLINT` | `SIGNED` | [**`Short`**][java-Short-ref], [`Integer`][java-Integer-ref], [`Long`][java-Long-ref], [`BigInteger`][java-BigInteger-ref] |\n| `MEDIUMINT` | `SIGNED/UNSIGNED` | [**`Integer`**][java-Integer-ref], [`Long`][java-Long-ref], [`BigInteger`][java-BigInteger-ref] |\n| `BIGINT` | `UNSIGNED` | [**`BigInteger`**][java-BigInteger-ref], [`Long`][java-Long-ref] (Not check overflow) |\n| `BIGINT` | `SIGNED` | [**`Long`**][java-Long-ref], [`BigInteger`][java-BigInteger-ref] |\n| `FLOAT` | `SIGNED` / `UNSIGNED` | [**`Float`**][java-Float-ref], [`BigDecimal`][java-BigDecimal-ref] |\n| `DOUBLE` | `SIGNED` / `UNSIGNED` | [**`Double`**][java-Double-ref], [`BigDecimal`][java-BigDecimal-ref]  |\n| `DECIMAL` | `SIGNED` / `UNSIGNED` | [**`BigDecimal`**][java-BigDecimal-ref], [`Float`][java-Float-ref] (Size less than 7), [`Double`][java-Double-ref] (Size less than 16) |\n| `BIT` | - | [**`ByteBuffer`**][java-ByteBuffer-ref], [`BitSet`][java-BitSet-ref], [`Boolean`][java-Boolean-ref] (Size is 1), `byte[]` |\n| `DATETIME` / `TIMESTAMP` | - | [**`LocalDateTime`**][java-LocalDateTime-ref], [`ZonedDateTime`][java-ZonedDateTime-ref], [`OffsetDateTime`][java-OffsetDateTime-ref], [`Instant`][java-Instant-ref] |\n| `DATE` | - | [**`LocalDate`**][java-LocalDate-ref] |\n| `TIME` | - | [**`LocalTime`**][java-LocalTime-ref], [`Duration`][java-Duration-ref], [`OffsetTime`][java-OffsetTime-ref] |\n| `YEAR` | - | [**`Short`**][java-Short-ref], [`Integer`][java-Integer-ref], [`Long`][java-Long-ref], [`BigInteger`][java-BigInteger-ref], [`Year`][java-Year-ref] |\n| `VARCHAR` / `NVARCHAR` | - | [**`String`**][java-String-ref] |\n| `VARBINARY` | - | [**`ByteBuffer`**][java-ByteBuffer-ref], `Blob`, `byte[]` |\n| `CHAR` / `NCHAR` | - | [**`String`**][java-String-ref] |\n| `ENUM` | - | [**`String`**][java-String-ref], [`Enum\u003c?\u003e`][java-Enum-ref] |\n| `SET` | - | **`String[]`**, [`String`][java-String-ref], [`Set\u003cString\u003e`][java-Set-ref] and [`Set\u003cEnum\u003c?\u003e\u003e`][java-Set-ref] ([`Set\u003cT\u003e`][java-Set-ref] need use [`ParameterizedType`][java-ParameterizedType-ref]) |\n| `BLOB`s (`LONGBLOB`, etc.) | - | [**`ByteBuffer`**][java-ByteBuffer-ref], `Blob`, `byte[]` |\n| `TEXT`s (`LONGTEXT`, etc.) | - | [**`String`**][java-String-ref], `Clob` |\n| `JSON` | - | [**`String`**][java-String-ref], `Clob` |\n| `GEOMETRY` | - | **`byte[]`**, `Blob` |\n\n## Add a `Codec`\n\nThis is an extension of a highly customized driver behavior of encoding parameter or decoding field data.\n\nExample for an extending `Codec` of JSON based-on Jackson.\n\nFirst, implement a `Codec`, `ParametrizedCodec`, `MassiveCodec` or `MassiveParametrizedCodec`.\n\n- `Codec` the normal codec\n  - Data type is `Class\u003c?\u003e`\n  - Data buffer size is less than or equal to `Integer.MAX_VALUE`\n- `ParametrizedCodec`\n  - Data type is `Class\u003c?\u003e` or `ParametrizedType`\n  - Data buffer size is less than or equal to `Integer.MAX_VALUE`\n- `MassiveCodec`\n  - Data type is `Class\u003c?\u003e`\n  - Data buffer size is less than or equal to `UnsignedInteger.MAX_VALUE` (Java does not have unsigned integers, it just only represents the range)\n- `MassiveParametrizedCodec`\n  - Data type is `Class\u003c?\u003e` or `ParametrizedType`\n  - Data buffer size is less than or equal to `UnsignedInteger.MAX_VALUE`\n  \nActually, `JSON` can store large json data, and its byte size can be `UnsignedInteger.MAX_VALUE`. However, this is just an example.\n\n```java\npublic final class JacksonCodec implements Codec\u003cObject\u003e {\n\n    /**\n     * JUST for example, should configure it in real applications.\n     */\n    private static final ObjectMapper MAPPER = new ObjectMapper();\n\n    private final ByteBufAllocator allocator;\n\n    /**\n     * Used for encoding/decoding mode, see also registrar in second step.\n     */\n    private final boolean encoding;\n\n    public JacksonCodec(ByteBufAllocator allocator, boolean encoding) {\n        this.allocator = allocator;\n        this.encoding = encoding;\n    }\n\n    @Override\n    public Object decode(ByteBuf value, FieldInformation info, Class\u003c?\u003e target, boolean binary, CodecContext context) {\n        // If you ensure server is using UTF-8, you can just use InputStream\n        try (Reader r = new InputStreamReader(new ByteBufInputStream(value), CharCollation.fromId(info.getCollationId(), context.getServerVersion()).getCharset())) {\n            return MAPPER.readValue(r, target);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public Parameter encode(Object value, CodecContext context) {\n        return new JacksonParameter(allocator, value, context);\n    }\n\n    @Override\n    public boolean canDecode(FieldInformation info, Class\u003c?\u003e target) {\n        return !encoding \u0026\u0026 info.getType() == DataTypes.JSON \u0026\u0026 info.getCollationId() != CharCollation.BINARY_ID;\n    }\n\n    @Override\n    public boolean canEncode(Object value) {\n        return encoding;\n    }\n\n    private static final class JacksonParameter implements Parameter {\n\n        private final ByteBufAllocator allocator;\n\n        private final Object value;\n\n        private final CodecContext context;\n\n        private JacksonParameter(ByteBufAllocator allocator, Object value, CodecContext context) {\n            this.allocator = allocator;\n            this.value = value;\n            this.context = context;\n        }\n\n        @Override\n        public Mono\u003cByteBuf\u003e publishBinary() {\n            // JSON in binary protocol should be a var-integer sized encoded string.\n            // That means we should write a var-integer as a size of following content\n            // bytes firstly, then write the encoded string as content.\n            //\n            // Binary protocol may be different for each type of encoding, so if do not\n            // use binary protocol, just return a Mono.error() instead.\n            return Mono.fromSupplier(() -\u003e {\n                Charset charset = context.getClientCollation().getCharset();\n                ByteBuf content = allocator.buffer();\n\n                // Encode and calculate content bytes first, we should know bytes size.\n                try (Writer w = new OutputStreamWriter(new ByteBufOutputStream(content), charset)) {\n                    MAPPER.writeValue(w, value);\n                } catch (IOException e) {\n                    content.release();\n                    throw new CustomRuntimeException(e);\n                } catch (Throwable e) {\n                    content.release();\n                    throw e;\n                }\n\n                ByteBuf buf = null;\n                try {\n                    buf = allocator.buffer();\n                    // VarIntUtils is an unstable, internal utility.\n                    VarIntUtils.writeVarInt(buf, content.readableBytes());\n                    return buf.writeBytes(content);\n                } catch (Throwable e) {\n                    if (buf != null) {\n                        buf.release();\n                    }\n                    throw e;\n                } finally {\n                    content.release();\n                }\n            });\n        }\n\n        @Override\n        public Mono\u003cVoid\u003e publishText(ParameterWriter writer) {\n            return Mono.fromRunnable(() -\u003e {\n                try {\n                    MAPPER.writeValue(writer, value);\n                } catch (IOException e) {\n                    throw new CustomRuntimeException(e);\n                }\n            });\n        }\n\n        @Override\n        public short getType() {\n            return DataTypes.VARCHAR;\n        }\n    }\n}\n```\n\nSecond, implement a `CodecRegistrar`.\n\n```java\n// It is just an example of package name and does not represent any company, individual or organization.\npackage org.example.demo.json;\n\n// Some imports...\n\npublic final class JacksonCodecRegistrar implements CodecRegistrar {\n\n    @Override\n    public void register(ByteBufAllocator allocator, CodecRegistry registry) {\n        // Decoding JSON by highest priority, encoding anything by lowest priority.\n        registry.addFirst(new JacksonCodec(allocator, false))\n            .addLast(new JacksonCodec(allocator, true));\n    }\n}\n```\n\nFinally, create a file in `META-INF/services`, which file name is `dev.miku.r2dbc.mysql.extension.Extension`, it contains this line:\n\n```\norg.example.demo.json.JacksonCodecRegistrar\n```\n\n## Reporting Issues\n\nThe R2DBC MySQL Implementation uses GitHub as issue tracking system to record bugs and feature requests. \nIf you want to raise an issue, please follow the recommendations below:\n\n- Before log a bug, please search the [issue tracker](https://github.com/mirromutth/r2dbc-mysql/issues) to see if someone has already reported the problem.\n- If the issue doesn't already exist, [create a new issue](https://github.com/mirromutth/r2dbc-mysql/issues/new).\n- Please provide as much information as possible with the issue report, we like to know the version of R2DBC MySQL that you are using and JVM version.\n- If you need to paste code, or include a stack trace use Markdown **\u0026#96;\u0026#96;\u0026#96;** escapes before and after your text.\n- If possible try to create a test-case or project that replicates the issue. Attach a link to your code or a compressed file containing your code.\n\n## Before use\n\n- The MySQL data fields encoded by index-based natively, get fields by an index will have **better** performance than get by column name.\n- Each `Result` should be used (call `getRowsUpdated` or `map`, even table definition), can **NOT** just ignore any `Result`, otherwise inbound stream is unable to align. (like `ResultSet.close` in jdbc, `Result` auto-close after used by once)\n- The MySQL server does not **actively** return time zone when query `DATETIME` or `TIMESTAMP`, this driver does not attempt time zone conversion. That means should always use `LocalDateTime` for SQL type `DATETIME` or `TIMESTAMP`. Execute `SHOW VARIABLES LIKE '%time_zone%'` to get more information.\n- Should not turn-on the `trace` log level unless debugging. Otherwise, the security information may be exposed through `ByteBuf` dump.\n- If `Statement` bound `returnGeneratedValues`, the `Result` of the `Statement` can be called both: `getRowsUpdated` to get affected rows, and `map` to get last inserted ID.\n- The MySQL may be not support search rows by a binary field, like `BIT`, `BLOB` and `JSON`, because those data fields maybe just an address of reference in MySQL server, or maybe need strict bit-aligned. (but `VARBINARY` is OK)\n\n## License\n\nThis project is released under version 2.0 of the [Apache License](https://www.apache.org/licenses/LICENSE-2.0).\n\n# Acknowledgements\n\n## Contributors\n\n[![Avatars of contributors](https://contrib.rocks/image?repo=mirromutth/r2dbc-mysql)](https://github.com/mirromutth/r2dbc-mysql/graphs/contributors)\n\nThanks a lot for your support!\n\n## Supports\n\n- [R2DBC Team](https://r2dbc.io) - Thanks for their support by sharing all relevant resources around R2DBC \n  projects.\n\n[m]: https://www.mysql.com\n[java-BigDecimal-ref]: https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html\n[java-BigInteger-ref]: https://docs.oracle.com/javase/8/docs/api/java/math/BigInteger.html\n[java-BitSet-ref]: https://docs.oracle.com/javase/8/docs/api/java/util/BitSet.html\n[java-Boolean-ref]: https://docs.oracle.com/javase/8/docs/api/java/lang/Boolean.html\n[java-Byte-ref]: https://docs.oracle.com/javase/8/docs/api/java/lang/Byte.html\n[java-ByteBuffer-ref]: https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html\n[java-Double-ref]: https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html\n[java-Float-ref]: https://docs.oracle.com/javase/8/docs/api/java/lang/Float.html\n[java-Integer-ref]: https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html\n[java-Long-ref]: https://docs.oracle.com/javase/8/docs/api/java/lang/Long.html\n[java-LocalDateTime-ref]: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html\n[java-ZonedDateTime-ref]: https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html\n[java-OffsetDateTime-ref]: https://docs.oracle.com/javase/8/docs/api/java/time/OffsetDateTime.html\n[java-Instant-ref]: https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html\n[java-LocalDate-ref]: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html\n[java-Duration-ref]: https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html\n[java-LocalTime-ref]: https://docs.oracle.com/javase/8/docs/api/java/time/LocalTime.html\n[java-OffsetTime-ref]: https://docs.oracle.com/javase/8/docs/api/java/time/OffsetTime.html\n[java-Year-ref]: https://docs.oracle.com/javase/8/docs/api/java/time/Year.html\n[java-Short-ref]: https://docs.oracle.com/javase/8/docs/api/java/lang/Short.html\n[java-Enum-ref]: https://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html\n[java-String-ref]: https://docs.oracle.com/javase/8/docs/api/java/lang/String.html\n[java-Set-ref]: https://docs.oracle.com/javase/8/docs/api/java/util/Set.html\n[java-ParameterizedType-ref]: https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/ParameterizedType.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmirromutth%2Fr2dbc-mysql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmirromutth%2Fr2dbc-mysql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmirromutth%2Fr2dbc-mysql/lists"}