1. 程式人生 > 其它 >mariadb/mysql R2DBC(CRUD)方式接入spring boot (二)

mariadb/mysql R2DBC(CRUD)方式接入spring boot (二)

技術標籤:springboot資料庫spring bootspringmysql

依然是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