{"id":18559066,"url":"https://github.com/linux-china/mybatis-r2dbc","last_synced_at":"2025-04-10T02:30:39.018Z","repository":{"id":41385613,"uuid":"293183140","full_name":"linux-china/mybatis-r2dbc","owner":"linux-china","description":"MyBatis R2DBC Adapter","archived":false,"fork":false,"pushed_at":"2022-01-17T01:53:51.000Z","size":999,"stargazers_count":134,"open_issues_count":10,"forks_count":32,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-04-02T02:51:12.248Z","etag":null,"topics":["mybatis","r2dbc"],"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/linux-china.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}},"created_at":"2020-09-06T01:39:14.000Z","updated_at":"2025-03-13T01:42:56.000Z","dependencies_parsed_at":"2022-09-12T04:11:02.936Z","dependency_job_id":null,"html_url":"https://github.com/linux-china/mybatis-r2dbc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linux-china%2Fmybatis-r2dbc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linux-china%2Fmybatis-r2dbc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linux-china%2Fmybatis-r2dbc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linux-china%2Fmybatis-r2dbc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/linux-china","download_url":"https://codeload.github.com/linux-china/mybatis-r2dbc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248144157,"owners_count":21054876,"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":["mybatis","r2dbc"],"created_at":"2024-11-06T21:41:57.853Z","updated_at":"2025-04-10T02:30:38.487Z","avatar_url":"https://github.com/linux-china.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"MyBatis R2DBC Adapter\n=====================\n\n将MyBatis Reactive化，将底层的JDBC替换为R2DBC，从而实现全面异步化，提升数据库访问性能。\n\n# MyBatis R2DBC工作原理\n\n![MyBatis R2DBC Structure](mybatis-r2dbc-structure.png)\n\n* 对数据CRUD操作SqlSession接口进行Reactive化，对于R2DBC适配来说为ReactiveSqlSession\n* Mapper接口Reactive化，将函数的类型从对象和List调整为Mono和Flux\n* SQL的执行器调整为R2DBC的接口，这里我们不需要类似MyBatis的Executor机制，Reactive框架为R2DBC提供了执行调度功能\n* 结果处理和映射：[R2DBC Data Types](https://r2dbc.io/spec/0.8.2.RELEASE/spec/html/#datatypes) 支持，你也可以扩展R2DBCTypeHandler添加自定义类型。MyBatis R2DBC增加了JDBC Types的支持，主要是考虑兼容性。\n\n# 如何使用?\n\n和MyBatis的使用流程一样，如下：\n\n```\n// construct MyBatis Configuration\nXMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(this.getClass().getResourceAsStream(\"/mybatis-config.xml\"));\nConfiguration configuration = xmlConfigBuilder.parse();\n\n// construct Reactive SQL Session Factory\nReactiveSqlSessionFactory reactiveSqlSessionFactory = new DefaultReactiveSqlSessionFactory(configuration);\nReactiveSqlSession reactiveSqlSession = reactiveSqlSessionFactory.openSession();\n\n// construct Reactive Mapper interface\nUserReactiveMapper userMapper = reactiveSqlSession.getMapper(UserReactiveMapper.class);\n```\n\n# 特性\n\n* 兼容MyBatis 3的配置和编程接口，主要是Mapper接口和SQL的XML配置方式\n* XxxMapper 调整为 Reactive 接口\n* Metrics support: 每一个statement的metrics都可以被记录\n* R2DBC Pool支持\n* Dynamic SQL支持\n\n# 核心工作\n\n* 将底层的JDBC调整为R2DBC\n* 兼容 Spring Data R2DBC\n* r2dbc-mysql的兼容性测试\n* TypeHandler支持\n\n# 主要调整点\n\n* Reactive SqlSessionFactory\n* Reactive SqlSession\n* Reactive Mapper interface\n\n### R2DBC-MyBatis支持的数据类型\n\nR2DBC有标准的Data Types支持，可以在 https://r2dbc.io/spec/0.8.1.RELEASE/spec/html/#datatypes 进行查阅\n\n考虑到MyBatis JDBC支持的类型，以下的类型也是被支持的：\n\n* Java Date: java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp\n\n# R2DBC Transaction\n\nPlease use R2dbcTransactionManager from Spring Data R2DBC\n\n# MySQL的R2DBC Driver\n\n* MariaDB R2DBC connector: 由MariaDB官方开发，作者有20年以上的Java开发经验 https://github.com/mariadb-corporation/mariadb-connector-r2dbc\n* R2DBC MySQL: Mirro Mutth，居住在深圳的中国工程师  https://github.com/mirromutth/r2dbc-mysql\n\n从目前开发的情况来看，可能MariaDB R2DBC更具有前景一些。\n\n# TypeHandler支持\n\nMyBatis针对resultMap的result有一个typeHandler的设置，可以进行Java对象字段和数据库表的column直接进行值转换。 在MyBatis R2DBC中我们保留这一特性。\n由于R2DBC和JDBC的API完全不同，所以新增了一个R2DBCTypeHandler接口，代码如下：\n\n```java\npublic interface R2DBCTypeHandler\u003cT\u003e {\n\n    void setParameter(Statement statement, int i, T parameter, JdbcType jdbcType) throws R2dbcException;\n\n    T getResult(Row row, String columnName, RowMetadata rowMetadata) throws R2dbcException;\n\n    T getResult(Row row, int columnIndex, RowMetadata rowMetadata) throws R2dbcException;\n\n    Class\u003c?\u003e getType();\n}\n```\n\n但是考虑到实际的MyBatis的配置验证，新建的TypeHandler实现需要同时继承TypeHandler和R2DBCTypeHandler接口，同时也保证在JDBC和R2DBC同时生效。\n\n你可以参考 org.apache.ibatis.r2dbc.type 开发包下的type handler进行编写，同时JDBC的时间类型的兼容，也是通过type handler完成的。\n\nJava Enum type handler和MyBatis处理机制类似，只是类名调整到R2DBC下的EnumOrdinalTypeHandler，代码如下：\n\n```xml\n \u003ctypeHandlers\u003e\n        \u003c!-- Enum ordinal type handler--\u003e\n        \u003ctypeHandler handler=\"org.apache.ibatis.r2dbc.type.EnumOrdinalTypeHandler\"\n                     javaType=\"java.math.RoundingMode\"/\u003e\n \u003c/typeHandlers\u003e\n```\n\n*注意：* @MappedJdbcTypes类型的TypeHandler不支持\n\n# Transaction支持\n\nReactive Transaction 支持  https://spring.io/blog/2019/05/16/reactive-transactions-with-spring\n\nSpring的R2dbcTransactionManager是基于R2DBC的ConnectionFactory，所以我们不需要进行什么配置，基本代码如下：\n\n```java\npublic class R2dbcTransactionManagerAutoConfiguration {\n\n\t@Bean\n\t@ConditionalOnMissingBean(ReactiveTransactionManager.class)\n\tpublic R2dbcTransactionManager connectionFactoryTransactionManager(ConnectionFactory connectionFactory) {\n\t\treturn new R2dbcTransactionManager(connectionFactory);\n\t}\n\n}\n```\n\n如果是基于R2DBC Pool，R2dbcTransactionManager就是基于ConnectionPool的，当然ConnectionPool就是继承自ConnectionFactory的。\n\n# 配置参数\n\n和R2DBC相关的配置参数，主要是通过MyBatis的mybatis-config.xml文件中的properties配置完成的，如下：\n\n```xml\n\u003cconfiguration\u003e\n    \u003cproperties\u003e\n        \u003cproperty name=\"metrics.enabled\" value=\"true\"/\u003e\n        \u003cproperty name=\"r2dbc.pool.initial-size\" value=\"1\"/\u003e\n        \u003cproperty name=\"r2dbc.pool.max-size\" value=\"10\"/\u003e\n        \u003c!-- max idle time, the unit is Minute--\u003e\n        \u003cproperty name=\"r2dbc.pool.max-idle-time\" value=\"5\"/\u003e\n    \u003c/properties\u003e\n\u003c/configuration\u003e\n```\n\n# R2DBC MyBatis的不同点\n\n背后的原因其实主要是JDBC和R2DBC的区别，我们都知道JDBC同步Block的，所以我们需要有连接池DataSource，防止应为JDBC的等待造成Thread的堵塞。\n\n## SqlSession ReactiveSqlSession\nMyBatis的一个核心是SqlSession，但是两者的设计还有很大的区别的：\n\n* MyBatis的原生SqlSession是基于JDBC，是Block的，所以存在SessionHolder的感念，如commit，rollback, close等API\n* ReactiveSqlSession则是 异步 + 单例(Singleton)的，和Spring的WebClient，Spring Data R2DBC的DatabaseClient一样的，只是名字还是叫做Session，和MyBatis一致而已。\n\n###  DataSource和R2DBC Pool\nJDBC使用连接池这个大家都明白， 通过增加连接数减少因为JDBC Connection等待的结果造成的block，不然应用整体的性能下降的非常快。\n在R2DBC中也有Connection Pool的概念，其主要是兼容传统数据库同步协议，如MySQL，解释如下：\n\n\u003e\u003e Traditionally, many MySQL drivers used a synchronous approach when executing SQL statements. This meant that operations such as opening connections and executing queries were blocked until completion, which could take a long time. To allow for parallel execution, a developer had to write a multithreaded application.\n\u003e\u003e Any MySQL client that supports the X Protocol can provide asynchronous execution, either using callbacks, Promises, or by explicitly waiting on a specific result at the moment in time when it is actually needed.\n\n如果数据库通讯协议是全部异步的，那么是没有必要介入Connection Pool的。 如mariadb-connector-r2dbc的实现中就有： \n\n```\nallowPipelining: Permit to send queries to server without waiting for previous query to finish\n```\n\n如果allowPipelining为false，这个时候，出于性能的考虑，你还是需要介入R2DBC Pool来提升应用性能。\n\nPipelining versus Parallel Query Execution请参考: https://www.percona.com/blog/2016/07/06/pipelining-versus-parallel-query-execution-with-mysql-5-7-x-plugin/\n\n# Cache特性\n\n对于Reactive来说，默认就包含了cache特性，也就是你可以调用 Mono.cache() 或 Flux.cache 就可以缓存响应的结果集，然后提供给后续的订阅者消费。\n\n对应MyBatis来说，cache也并复杂，Cache也是基于Mapper的，我们只需要基于paramObject缓存对应的Mono或者Flux对象就可以。\n\n```java\n Mono\u003cString\u003e user = Mono.defer(() -\u003e {\n            // r2dbc operation\n            return r2dbcResult;\n        }).cache(Duration.ofSeconds(2));\n```\n\n但是，这个只是基于内存的对象缓存，如果是分布式的，上述机制不能工作，这涉及到对象序列化和反序列化机制的机制。\n\n至于选择哪一种，这个可能要根据实际的缓存类型进行决定。\n\n# spring-boot-starter-data-r2dbc整合\n\n如果你项目中已经包含spring-boot-starter-data-r2dbc，那么你可以完全使用Spring Boot提供的R2DBC的ConnectionFactory bean完成数据库连接相关的任务。\n\n```java\n@Bean\npublic ReactiveSqlSessionFactory reactiveSqlSessionFactory(ConnectionFactory r2dbcConnectionFactory)  {\n  XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(this.getClass().getResourceAsStream(\"/mybatis-config.xml\"));\n  Configuration configuration = xmlConfigBuilder.parse();\n  return new DefaultReactiveSqlSessionFactory(configuration, r2dbcConnectionFactory);\n}\n```\n\n# 不支持的特性\n\n* MyBatis Plugin机制: org.apache.ibatis.plugin.Interceptor\n* 不支持配置文件中声明Reactive Mapper接口，R2DBC需要额外的API支持\n\n```xml\n\u003cmappers\u003e\n  \u003cmapper class=\"org.mybatis.builder.AuthorReactiveMapper\"/\u003e\n\u003c/mappers\u003e\n\n```\n\n# 参考\n\n* MyBatis3中文文档： http://www.mybatis.org/mybatis-3/zh/index.html\n* R2DBC: https://r2dbc.io/\n* R2DBC 规范: https://r2dbc.io/spec/0.8.2.RELEASE/spec/html/\n* R2DBC Pool: https://github.com/r2dbc/r2dbc-pool\n* Spring Data R2DBC: https://github.com/spring-projects/spring-data-r2dbc  https://docs.spring.io/spring-data/r2dbc/docs/1.0.0.M2/reference/html/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinux-china%2Fmybatis-r2dbc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flinux-china%2Fmybatis-r2dbc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinux-china%2Fmybatis-r2dbc/lists"}