javascript
r2dbc_使用Spring Data R2DBC进行异步RDBMS访问
r2dbc
不久之前,發布了JDBC驅動程序的React性變體。 稱為R2DBC。 它允許將數據異步流傳輸到已預訂的任何端點。 通過將R2DBC之類的React性驅動程序與Spring WebFlux結合使用,可以編寫一個完整的應用程序,以異步方式處理數據的接收和發送。 在本文中,我們將重點介紹數據庫。 從連接到數據庫,然后最終保存和檢索數據。 為此,我們將使用Spring Data。 與所有Spring Data模塊一樣,它為我們提供了現成的配置。 減少為實現應用程序設置而需要編寫的樣板代碼數量。 最重要的是,它在數據庫驅動程序上提供了一層,使執行簡單任務變得更加容易,而較困難的任務則減輕了一些痛苦。
對于本文的內容,我正在使用Postgres數據庫。 在撰寫本文時,僅Postgres,H2和Microsoft SQL Server具有自己的R2DBC驅動程序實現。
之前,我曾寫過兩篇有關React式Spring Data庫的文章,一篇關于Mongo ,另一篇關于Cassandra 。 您可能已經注意到,這些數據庫都不是RDBMS數據庫。 現在有很長一段時間都可以使用其他React式驅動程序(我將近兩年前寫了Mongo文章),但是在為RDBMS數據庫編寫React式驅動程序時,這仍然是一件很新的事情。 這篇文章將遵循類似的格式。
此外,我還寫了一篇關于使用Spring WebFlux的文章 ,我在引言中提到過。 如果您有興趣生產完全React式的Web應用程序,請隨時查看。
依存關系
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-r2dbc</artifactId><version>1.0.0.M1</version></dependency><dependency><groupId>io.r2dbc</groupId><artifactId>r2dbc-postgresql</artifactId><version>1.0.0.M6</version></dependency><dependency><groupId>io.projectreactor</groupId><artifactId>reactor-core</artifactId></dependency> </dependencies><repositories><repository><id>repository.spring.milestone</id><name>Spring Milestone Repository</name><url>http://repo.spring.io/milestone</url></repository> </repositories>這里有幾點要指出。
使用Spring Boot的次數越多,就越會習慣于為您想做的很酷的事情導入單個spring-boot-starter依賴項。 例如,我希望會有spring-boot-starter-r2dbc依賴關系,但不幸的是,沒有依賴關系。 然而。 簡而言之,該庫位于較新的一側,在編寫本文時,它沒有自己的Spring Boot模塊,該模塊包含所需的任何依賴項以及通過自動配置的更快設置。 我確信這些事情會在某個時候出現,并使設置R2DBC驅動程序變得更加容易。
現在,我們將需要手動填寫一些額外的依賴項。
此外,R2DBC庫僅具有Milestone版本(更多證明它們是新版本),因此我們需要確保引入Spring Milestone存儲庫。 當我獲得發布版本時,將來可能會需要更新此帖子。
連接到數據庫
由于Spring Data為我們做了很多工作,因此唯一需要手動創建的Bean是ConnectionFactory ,其中包含數據庫的連接詳細信息:
@Configuration @EnableR2dbcRepositories class DatabaseConfiguration(@Value("\${spring.data.postgres.host}") private val host: String,@Value("\${spring.data.postgres.port}") private val port: Int,@Value("\${spring.data.postgres.database}") private val database: String,@Value("\${spring.data.postgres.username}") private val username: String,@Value("\${spring.data.postgres.password}") private val password: String ) : AbstractR2dbcConfiguration() {override fun connectionFactory(): ConnectionFactory {return PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder().host(host).port(port).database(database).username(username).password(password).build())} }這里首先要注意的是AbstractR2dbcConfiguration的擴展。 此類包含大量我們不再需要手動創建的Bean。 實現connectionFactory是該類的唯一要求,因為創建DatabaseClient Bean是必需的。 這種結構是Spring Data模塊的典型結構,因此在嘗試其他結構時會感到非常熟悉。 此外,我希望一旦可以使用自動配置,就可以刪除此手動配置,并且可以通過application.properties單獨驅動。
我在此處包括了port屬性,但是如果您還沒有使用Postgres配置,那么可以依靠默認值5432 。
PostgresqlConnectionFactory定義的四個屬性: host , database , username和password是使它正常工作的最低要求。 少了一點,您將在啟動過程中遇到異常。
使用此配置,Spring可以連接到正在運行的Postgres實例。
該示例中最后一個值得注意的信息是@EnableR2dbcRepositories的使用。 該注釋指示Spring查找擴展Spring的Repository接口的任何存儲Repository接口。 這用作檢測Spring Data存儲庫的基本接口。 我們將在下一部分中對此進行更仔細的研究。 從這里獲得的主要信息是,您需要使用@EnableR2dbcRepositories批注以充分利用Spring Data的功能。
創建一個Spring數據倉庫
如上所述,在本節中,我們將介紹添加Spring Data Repository。 這些存儲庫是Spring Data的一個不錯的功能,這意味著您無需寫很多額外的代碼即可簡單地編寫查詢。 不幸的是,至少到目前為止,Spring R2DBC不能像其他Spring Data模塊那樣以同樣的方式推斷查詢(我肯定會在某個時候添加它)。 這意味著您將需要使用@Query批注并手動編寫SQL。 讓我們來看看:
@Repository interface PersonRepository : R2dbcRepository<Person, Int> {@Query("SELECT * FROM people WHERE name = $1")fun findAllByName(name: String): Flux<Person>@Query("SELECT * FROM people WHERE age = $1")fun findAllByAge(age: Int): Flux<Person> }該接口擴展了R2dbcRepository 。 這依次擴展了ReactiveCrudRepository ,然后向下擴展到Repository 。 ReactiveCrudRepository提供了標準的CRUD功能,據我了解, R2dbcRepository不提供任何額外的功能,而是為更好的情境命名而創建的接口。
R2dbcRepository接受兩個通用參數,一個是它作為輸入并作為輸出產生的實體類。 第二個是主鍵的類型。 因此,在這種情況下, Person類由PersonRepository管理(有意義),并且Person內部的主鍵字段是Int 。
此類中的函數以及ReactiveCrudRepository提供的函數的返回類型為Flux和Mono (此處未顯示)。 這些是Spring用作默認React流類型的Project Reactor類型。 Flux代表多個元素的流,而Mono則是單個結果。
最后,正如我在示例之前提到的那樣,每個函數都使用@Query注釋。 語法非常簡單,SQL是注釋中的字符串。 $1 (用于更多輸入的$2 , $3等)表示輸入到函數中的值。 完成此操作后,Spring將處理其余部分,并將輸入傳遞到各自的輸入參數中,收集結果并將其映射到存儲庫的指定實體類。
快速瀏覽實體
在這里不多說,僅顯示PersonRepository使用的Person類。
@Table("people") data class Person(@Id val id: Int? = null,val name: String,val age: Int )實際上,這里有一點需要說明。 id已設置為可為空,并提供默認值null以允許Postgres自身生成下一個合適的值。 如果這不能為空并且提供了一個id值,那么Spring在保存時實際上將嘗試運行更新而不是插入操作。 還有其他解決方法,但是我認為這已經足夠了。
該實體將映射到下面定義的people表:
CREATE TABLE people (id SERIAL PRIMARY KEY, name VARCHAR NOT NULL, age INTEGER NOT NULL );看到一切都在行動
現在讓我們看看它實際上在做什么。 下面是一些插入一些記錄并以幾種不同方式檢索它們的代碼:
@SpringBootApplication class Application : CommandLineRunner {@Autowiredprivate lateinit var personRepository: PersonRepositoryoverride fun run(vararg args: String?) {personRepository.saveAll(listOf(Person(name = "Dan Newton", age = 25),Person(name = "Laura So", age = 23))).log().subscribe()personRepository.findAll().subscribe { log.info("findAll - $it") }personRepository.findAllById(Mono.just(1)).subscribe { log.info("findAllById - $it") }personRepository.findAllByName("Laura So").subscribe { log.info("findAllByName - $it") }personRepository.findAllByAge(25).subscribe { log.info("findAllByAge - $it") }} }關于此代碼,我將提到一件事。 它很可能在沒有實際插入或讀取某些記錄的情況下執行。 但是,當您考慮它時。 這說得通。 React性應用程序旨在異步執行操作,因此該應用程序已開始處理不同線程中的函數調用。 如果不阻塞主線程,這些異步進程可能永遠不會完全執行。 因此,此代碼中有一些Thread.sleep調用,但我從示例中刪除了它們,以使所有內容保持整潔。
運行上面的代碼的輸出如下所示:
2019-02-11 09:04:52.294 INFO 13226 --- [ main] reactor.Flux.ConcatMap.1 : onSubscribe(FluxConcatMap.ConcatMapImmediate) 2019-02-11 09:04:52.295 INFO 13226 --- [ main] reactor.Flux.ConcatMap.1 : request(unbounded) 2019-02-11 09:04:52.572 INFO 13226 --- [actor-tcp-nio-1] reactor.Flux.ConcatMap.1 : onNext(Person(id=35, name=Dan Newton, age=25)) 2019-02-11 09:04:52.591 INFO 13226 --- [actor-tcp-nio-1] reactor.Flux.ConcatMap.1 : onNext(Person(id=36, name=Laura So, age=23)) 2019-02-11 09:04:52.591 INFO 13226 --- [actor-tcp-nio-1] reactor.Flux.ConcatMap.1 : onComplete() 2019-02-11 09:04:54.472 INFO 13226 --- [actor-tcp-nio-2] com.lankydanblog.tutorial.Application : findAll - Person(id=35, name=Dan Newton, age=25) 2019-02-11 09:04:54.473 INFO 13226 --- [actor-tcp-nio-2] com.lankydanblog.tutorial.Application : findAll - Person(id=36, name=Laura So, age=23) 2019-02-11 09:04:54.512 INFO 13226 --- [actor-tcp-nio-4] com.lankydanblog.tutorial.Application : findAllByName - Person(id=36, name=Laura So, age=23) 2019-02-11 09:04:54.524 INFO 13226 --- [actor-tcp-nio-5] com.lankydanblog.tutorial.Application : findAllByAge - Person(id=35, name=Dan Newton, age=25)這里有一些要注意的地方:
- onSubscribe和request發生在調用Flux的主線程上。 僅saveAll輸出此內容,因為它已包含log功能。 將其添加到其他調用中將導致記錄到主線程的結果相同。
- subscription函數中包含的執行和Flux的內部步驟在單獨的線程上運行。
這與在實際應用程序中如何使用React式流的真實表示不盡相同,但是希望可以演示如何使用它們,并對它們的執行方式提供一些見解。
結論
總之,由于R2DBC驅動程序和Spring Data在頂部建立了一層,使所有內容變得更加整潔,因此Reactive Streams進入了某些RDBMS數據庫。 通過使用Spring Data R2DBC,我們可以創建與數據庫的連接并開始查詢它,而無需太多代碼。 盡管Spring已經為我們做了大量工作,但它可能會做更多的事情。 當前,它不具有Spring Boot自動配置支持。 這有點煩人。 但是,我相信有人很快就會做起來并使所有事情變得比現在更好。
這篇文章中使用的代碼可以在我的GitHub上找到 。
翻譯自: https://www.javacodegeeks.com/2019/02/asynchronous-rdbms-access-spring-r2dbc.html
r2dbc
總結
以上是生活随笔為你收集整理的r2dbc_使用Spring Data R2DBC进行异步RDBMS访问的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jax-ws和jax-rs_JAX-RS
- 下一篇: 【IT之家图赏】时隔 7 年首次大改款!