依然是start.spring.io创建,大致的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.mariadb</groupId>
<artifactId>r2dbc-mariadb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
使用kotlin需要再加入:
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor.kotlin</groupId>
<artifactId>reactor-kotlin-extensions</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-reactor</artifactId>
</dependency>
配置数据库和(一介绍的一样)
spring:
r2dbc:
username: user
password: psw
url: r2dbc:mariadb://hostname:port/dataname
新建一个实体类例如:
import org.springframework.data.annotation.Id
class Customer(val firstName: String, val lastName: String) {
@Id
var id: Long? = null
override fun toString() = String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName
)
}
新建一个初始化bean来执行初始化sql(此bean同样适用于方式一)
@Bean
fun initializer(): ConnectionFactoryInitializer {
val initializer = ConnectionFactoryInitializer()
initializer.setConnectionFactory(connectionFactory)
initializer.setDatabasePopulator(ResourceDatabasePopulator(ClassPathResource("schema.sql")))
return initializer
}
schema.sql 如下:
DROP TABLE IF EXISTS customer;
CREATE TABLE IF NOT EXISTS customer (id SERIAL PRIMARY KEY, first_name VARCHAR(255), last_name VARCHAR(255)) CHARSET=utf8;
创建实体类仓库:
import org.springframework.data.repository.reactive.ReactiveCrudRepository
interface CustomerRepository : ReactiveCrudRepository<Customer?, Long?>
在RestController中使用:
class cot (
val customerRepository: CustomerRepository
) { ... }
以上java的写法就结束了
spring.io官网Going Reactive with Spring, Coroutines and Kotlin Flow有提到kotlin支持,推荐kotlin仓库的写法是这样的:
import org.springframework.data.repository.kotlin.CoroutineCrudRepository
interface CustomerRepository2: CoroutineCrudRepository<Customer,Long> {
}
根据说明这是一个协程仓库,经过测试crud速度跟spring mvc下速度接近,但是可以利用协程并发。
以下是对这些仓库方法的测试。
测试数据库为远程腾讯云上自己假设的,mariadb数据库(有网络延迟),测试全部为单插入500条数据(用save而不是saveAll)
测试1接口,使用响应式方式调ReactiveCrudRepository接口(时间约10-300毫秒,第一次最慢,几次后加快)非常快
@GetMapping("insert1")
fun insert1(): Long {
val time = System.currentTimeMillis()
Flux.range(1,500).flatMap { customerRepository.save(Customer("abc","bcd")) }.subscribe()
return System.currentTimeMillis() - time
}
测试2, 使用响应式方式调ReactiveCrudRepository接口(时间约70-300毫秒)非常快接近第一种
@GetMapping("insert2")
fun insert2(): Long {
val time = System.currentTimeMillis()
(1..500).forEach { _ -> customerRepository.save(Customer("abc","bcd")).subscribe() }
return System.currentTimeMillis() - time
}
测试3 ,使用CoroutineCrudRepository接口循环(时间快接近20000毫秒,不忍直视)
@GetMapping("insert3")
suspend fun insert3() = measureTimeMillis {
(1 ..500).forEach{ _ -> customerRepository2.save(Customer("abc","bcd"))}
}
契而不舍,官网推荐用法怎么可能,不停的修改接口代码(时间1100-1400毫秒,最大努力了)
@GetMapping("insert5")
suspend fun insert5() = measureTimeMillis {
foo().buffer().collect { it.await() }
}
suspend fun foo() = flow {
coroutineScope {
for (i in 1..500) {
emit(async { customerRepository2.save(Customer("abc", "bcd")) })
}
}
}
可能还是kotlin flow用的不够好,有更好的可以留言。
对比响应式的插入速度,如果现在开发新项目还选择jdbc的,实在对不起金主啊!
推荐大家使用
ReactiveCrudRepository
方式CRUD