环境:Springboot2.4.12R2DBC简介SpringdataR2DBC是更大的Spring数据家族的一部分,可以很容易的实现基于R2DBC的repository。R2DBC代表ReactiveRelationalDatabaseConnectivity,这是一种使用反应驱动程序集成SQL数据库的规范。SpringDataR2DBC使用应用于R2DBC的属性和存储库支持的Spring抽象。它使得在反应式应用程序堆栈中使用关系数据访问技术构建Spring驱动的应用程序变得更加容易。SpringDataR2DBC的目标是概念上简单。为实现这一点,它不提供缓存、延迟加载、写后处理或ORM框架的许多其他功能。这使得SpringDataR2DBC成为一个简单、有限、自以为是的对象映射器。SpringDataR2DBC允许以功能方式与数据库交互,提供R2dbcEntityTemplate作为应用程序的入口点。首先选择数据库驱动,创建R2dbcEntityTemplate实例:H2(io.r2dbc:r2dbc-h2)MariaDB(org.mariadb:r2dbc-mariadb)MicrosoftSQLServer(io.r2dbc:r2dbc-mssql)MySQL(dev.miku:r2dbc-mysql)jasync-sqlMySQL(com.github.jasync-sql:jasync-r2dbc-mysql)Postgres(io.r2dbc:r2dbc-postgresql)Oracle(com.oracle.database.r2dbc:oracle-r2dbc)WebFlux介绍Spring框架包含原始的web框架SpringWebMVC是专门为ServletAPI和Servlet容器构建的。响应式堆栈web框架SpringWebFlux是后来在5.0版本中添加的。它是完全非阻塞的,支持反应流背压(消费者控制生产者的速度),并在Netty、Undertow和Servlet3.1+容器等服务器上运行。这两个Web框架都反映了它们的源模块(SpringWebMVC和SpringWebFlux)的名称,并在Spring框架内共存。每个模块都是可选的。一个应用程序可以使用一个或另一个模块,在某些情况下,可以同时使用这两个模块——例如,一个带有反应式WebClient的SpringMVC控制器。依赖管理org.springframework.bootspring-boot-starter-webfluxorg.springframework.bootspring-boot-starter-data-r2dbcdev.mikur2dbc-mysql应用配置spring:r2dbc:url:r2dbc:mysql://localhost:3306/reactive_dbusername:rootpassword:123123pool:initialSize:100maxSize:100---logging:level:org.springframework.r2dbc:DEBUG#输出执行的sql关于MySQL的R2DBC详细配置查看:https://github.com/mirromutth/r2dbc-mysql实体&Service@Table("T_USERS")publicclassUsers{@IdprivateLongid;私有字符串名称;私人字符串性别;私人整数年龄;}Service@ResourceprivateR2dbcEntityTemplate模板;@TransactionalpublicMonoinsertByTemplate(Usersusers){returntemplate.insert(users);}publicMonoremoveByTemplate(Longid){Queryquery=Query.query(Criteria.where("id").is(ID));returntemplate.delete(query,Users.class);}publicMonoupdateByTemplate(Usersusers){CriteriaDefinitioncriteria=Criteria.where("id").is(users.getId());查询query=Query.query(criteria);更新update=Update.update("name",users.getName());returntemplate.update(query,update,Users.class);}publicMonoselectByTemplate(Longid){Queryquery=Query.query(Criteria.where("id").is(id));returntemplate.select(query,Users.class).single();}publicFluxselectByTemplate(Integerpage,Integersize){查询查询=Query.empty().offset((page-1)*size).limit(尺寸);returntemplate.select(query,Users.class);}publicMonoselecyByTemplateCount(){returntemplate.select(Users.class).count();}publicMono>>selectByTemplatePager(Integerpage,Integersize){Mono>datas=this.selectByTemplate(page,size).collectList();Monocount=this.selecyByTemplateCount();returndatas.zipWith(count,(list,c)->{returnResponseEntity.ok().header("count",c+"").header("page",page+"").header("大小",size+"").body(list);});}Controller@ResourceprivateUsersServiceus;@PostMapping("/insert")publicMonoinsertByTemplate(@RequestBodyUsersusers){returnus.insertByTemplate(users);}@GetMapping("/remove/{id}")publicMonoremoveByTemplate(@PathVariable("id")Longid){returnus.removeByTemplate(id);}@PostMapping("/update")publicMonoupdateByTemplate(@RequestBodyUsersusers){returnus.updateByTemplate(users);}@GetMapping("/query/{id}")publicMonoselectByTemplate(@PathVariable("id")Longid){returnus.selectByTemplate(id).single();}@GetMapping("/pager")publicMono>>selectByTemplate(Integerpage,Integersize){returnus.selectByTemplatePager(page,size);}@GetMapping("/count")publicMonoselecyByTemplateCount(){返回us.selecyByTemplateCount();}R2DBCRepository通过继承ReactiveCrudRepository或者是ReactiveSortingRepositoryRepository支持的方法查询如下表所示:MonodeletePersonByLastname(Stringlastname);MonodeletePersonByLastname(Stringlastname);}自定义修改操作:@Modifying@Query("UPDATEpersonSETfirstname=:firstnamewherelastname=:lastname")MonosetFixedFirstnameFor(Stringfirstname,Stringlastname);支持乐观锁:@Version注解在R2DBC上下文中提供与JPA类似的语法,并确保更新仅应用于具有匹配版本的行。因此,version属性的实际值被添加到更新查询中,如果另一个操作同时更改了该行,则更新将不起作用。在这种情况下,将抛出OptimisticLockingFailureException。以下示例显示了这些功能:@TablepublicclassPerson{@IdLongid;字符串名字;字符串姓氏;@VersionLongversion;}下面的例子演示了乐观锁异常的触发:R2dbcEntityTemplatetemplate=...;//1.InitialinsertData此时version=0Monodaenerys=template.insert(newPerson(“丹妮莉丝”));//2.加载刚刚插入的数据,此时加载的版本=0Personother=template.select(Person.class).matching(query(where("id").is(daenerys.getId()))).first().block();//3.更新数据,其中数据的更新版本=1daenerys.setLastname("Targaryen");template.update(daenerys);//4.更新数据,因为其他的version=0;并且数据库已经是1,所以这里会触发OptimisticLockingFailureExceptiontemplate.update(other)。订阅();