SpringBoot WebFlux整合R2DBC实现数据库反应式编程
环境:Springboot2.4.12
R2DBC简介
Spring data R2DBC是更大的Spring data 系列的一部分,它使得实现基于R2DBC的存储库变得容易。R2DBC代表反应式关系数据库连接,这是一种使用反应式驱动程序集成SQL数据库的规范。Spring Data R2DBC使用属性的Spring抽象和Repository支持应用于R2DBC。它使得在反应式应用程序堆栈中使用关系数据访问技术构建Spring驱动的应用程序变得更加容易。
Spring Data R2DBC的目标是在概念上变得简单。为了实现这一点,它不提供缓存、延迟加载、写后处理或ORM框架的许多其他特性。这使得Spring Data R2DBC成为一个简单、有限、固执己见的对象映射器。
Spring Data R2DBC允许一种 functional 方法与数据库交互,提供R2dbcEntityTemplate作为应用程序的入口点。
首先选择数据库驱动程序并创建R2dbcEntityTemplate实例:
WebFlux介绍
Spring框架中包含原始web框架Spring Web MVC是专门为ServletAPI和Servlet容器构建的。反应式堆栈web框架Spring Web Flux是后来在5.0版中添加。它是完全非阻塞的,支持反应流背压(由消费者控制生产者的速度),并在Netty、Undertow和Servlet 3.1+容器等服务器上运行。
这两个web框架都反映了它们的源模块(Spring Web MVC和Spring Web Flux)的名称,并在Spring框架中共存。每个模块都是可选的。应用程序可以使用一个或另一个模块,在某些情况下,可以同时使用这两个模块 — 例如,带有反应式WebClient的Spring MVC控制器。
依赖管理
org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-data-r2dbc dev.miku r2dbc-mysql
应用配置
spring: r2dbc: url: r2dbc:mysql://localhost:3306/reactive_db username: root password: 123123 pool: initialSize: 100 maxSize: 100---logging: level: org.springframework.r2dbc: DEBUG #输出执行的sql
实体&Service
@Table("T_USERS")public class Users { @Id private Long id ; private String name ; private String sex ; private Integer age ;}
Service
@Resourceprivate R2dbcEntityTemplate template ; @Transactionalpublic Mono insertByTemplate(Users users) { return template.insert(users) ;}public Mono removeByTemplate(Long id) { Query query = Query.query(Criteria.where("id").is(id)) ; return template.delete(query, Users.class) ;}public Mono updateByTemplate(Users users) { CriteriaDefinition criteria = Criteria.where("id").is(users.getId()) ; Query query = Query.query(criteria) ; Update update = Update.update("name", users.getName()) ; return template.update(query, update, Users.class) ;}public Mono selectByTemplate(Long id) { Query query = Query.query(Criteria.where("id").is(id)) ; return template.select(query, Users.class).single() ;}public Flux selectByTemplate(Integer page, Integer size) { Query query = Query.empty().offset((page - 1) * size).limit(size) ; return template.select(query, Users.class) ;}public Mono selecyByTemplateCount() { return template.select(Users.class).count() ;}public Mono>> selectByTemplatePager(Integer page, Integer size) { Mono> datas = this.selectByTemplate(page, size).collectList() ; Mono count = this.selecyByTemplateCount() ; return datas.zipWith(count, (list, c) -> { return ResponseEntity.ok().header("count", c + "").header("page", page + "").header("size", size + "").body(list) ; }) ;}
Controller
@Resourceprivate UsersService us ;@PostMapping("/insert")public Mono insertByTemplate(@RequestBody Users users) { return us.insertByTemplate(users) ;}@GetMapping("/remove/{id}")public Mono removeByTemplate(@PathVariable("id")Long id) { return us.removeByTemplate(id) ;}@PostMapping("/update")public Mono updateByTemplate(@RequestBody Users users) { return us.updateByTemplate(users) ;}@GetMapping("/query/{id}")public Mono selectByTemplate(@PathVariable("id") Long id) { return us.selectByTemplate(id).single() ;}@GetMapping("/pager")public Mono>> selectByTemplate(Integer page, Integer size) { return us.selectByTemplatePager(page, size) ;}@GetMapping("/count")public Mono selecyByTemplateCount() { return us.selecyByTemplateCount() ;}
R2DBC Repository
通过继承ReactiveCrudRepository或者是ReactiveSortingRepository。Repository支持的方法查询如下表所示:
Repository修改操作:
interface ReactivePersonRepository extends ReactiveSortingRepository { Mono deleteByLastname(String lastname); Mono deletePersonByLastname(String lastname); Mono deletePersonByLastname(String lastname); }
自定义修改操作:
@Modifying@Query("UPDATE person SET firstname = :firstname where lastname = :lastname")Mono setFixedFirstnameFor(String firstname, String lastname);
支持乐观锁:
@Version注释提供了与R2DBC上下文中JPA类似的语法,并确保更新仅应用于具有匹配版本的行。因此,version属性的实际值被添加到update查询中,如果另一个操作同时更改了行,则更新不会产生任何影响。在这种情况下,将抛出OptimisticLockingFailureException。以下示例显示了这些功能:
@Tablepublic class Person { @Id Long id; String firstname; String lastname; @Version Long version;}
如下示例演示了乐观锁异常的触发:
R2dbcEntityTemplate template = …;// 1. 初始插入数据 此时version = 0Mono daenerys = template.insert(new Person("Daenerys")); // 2. 加载刚刚插入的数据,此时加载的version = 0Person other = template.select(Person.class) .matching(query(where("id").is(daenerys.getId()))).first().block(); // 3. 更新数据,此处更新后该条数据的version = 1daenerys.setLastname("Targaryen");template.update(daenerys); // 4. 更新数据,由于other中的version = 0 ;而数据库已经是1了,所以这里会触发OptimisticLockingFailureException异常template.update(other).subscribe();
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。