天天看點

Spring認證中國教育管理中心-Spring Data R2DBC架構教程六

原标題:Spring認證中國教育管理中心-Spring Data R2DBC架構教程六(Spring中國教育管理中心)

16.1.4.Kotlin 支援

Spring Data 調整了 Kotlin 的細節以允許建立和更改對象。

Kotlin 對象建立

Kotlin 類支援執行個體化,預設情況下所有類都是不可變的,并且需要顯式屬性聲明來定義可變屬性。考慮以下data類Person:

data class Person(val id: String, val name: String)

上面的類編譯為具有顯式構造函數的典型類。我們可以通過添加另一個構造函數來自定義這個類,并使用注釋@PersistenceConstructor來訓示構造函數首選項:

data class Person(var id: String, val name: String) {

@PersistenceConstructor
constructor(id: String) : this(id, "unknown")           

}

Kotlin 通過允許在未提供參數時使用預設值來支援參數可選性。當 Spring Data 檢測到具有參數預設值的構造函數時,如果資料存儲不提供值(或簡單地傳回null),它就會使這些參數不存在,是以 Kotlin 可以應用參數預設值。考慮以下應用參數預設值的類name

data class Person(var id: String, val name: String = "unknown")

每次name參數不是結果的一部分或其值為 時null,則name預設為unknown。

Kotlin 資料類的屬性填充

在 Kotlin 中,預設情況下所有類都是不可變的,并且需要明确的屬性聲明來定義可變屬性。考慮以下data類Person:

這個類實際上是不可變的。它允許建立新執行個體,因為 Kotlin 生成copy(…)建立新對象執行個體的方法,該方法從現有對象複制所有屬性值并将作為參數提供的屬性值應用到該方法。

Kotlin 覆寫屬性

Kotlin 允許聲明屬性覆寫來改變子類中的屬性。

open class SuperType(open var field: Int)

class SubType(override var field: Int = 1) :

SuperType(field) {

這樣的安排呈現了兩個名稱為 的屬性field。Kotlin 為每個類中的每個屬性生成屬性通路器(getter 和 setter)。實際上,代碼如下所示:

public class SuperType {

private int field;

public SuperType(int field) {

this.field = field;           

public int getField() {

return this.field;           

public void setField(int field) {

this.field = field;           

public final class SubType extends SuperType {

public SubType(int field) {

super(field);
  this.field = field;           
return this.field;           
this.field = field;           

Spring認證中國教育管理中心-Spring Data R2DBC架構教程六

getter 和 setterSubType隻在set 上,SubType.field而不是SuperType.field. 在這種安排中,使用構造函數是設定的唯一預設方法SuperType.field。添加方法 to SubTypeset

SuperType.fieldviathis.SuperType.field = …是可能的,但不屬于支援的約定。屬性覆寫在某種程度上會産生沖突,因為屬性共享相同的名稱但可能代表兩個不同的值。我們通常建議使用不同的屬性名稱。

Spring Data 子產品通常支援包含不同值的覆寫屬性。從程式設計模型的角度來看,需要考慮以下幾點:

應該保留哪個屬性(預設為所有聲明的屬性)?您可以通過使用 注釋這些屬性來排除屬性@Transient。

如何表示資料存儲中的屬性?對不同的值使用相同的字段/列名稱通常會導緻資料損壞,是以您應該使用明确的字段/列名稱來注釋至少一個屬性。

using@AccessType(PROPERTY)不能使用,因為不能設定超級屬性。

16.2.基于約定的映射

MappingR2dbcConverter當沒有提供額外的映射中繼資料時,有一些将對象映射到行的約定。這些約定是:

簡短的 Java 類名以下列方式映射到表名。将com.bigbank.SavingsAccount類映射到SAVINGS_ACCOUNT表名。相同的名稱映射應用于将字段映射到列名稱。例如,firstName字段映射到FIRST_NAME列。您可以通過提供自定義NamingStrategy. 有關更多詳細資訊,請參閱映射配置。預設情況下,在 SQL 語句中使用從屬性或類名派生的表名和列名,不帶引号。您可以通過設定來控制這種行為R2dbcMappingContext.setForceQuote(true)。

不支援嵌套對象。

轉換器使用任何注冊的 Spring 轉換器來覆寫對象屬性到行列和值的預設映射。

對象的字段用于在行中的列之間進行轉換。JavaBean不使用公共屬性。

如果您有一個非零參數構造函數,其構造函數參數名稱與行的頂級列名稱比對,則使用該構造函數。否則,将使用零參數構造函數。如果有多個非零參數構造函數,則會引發異常。

16.3.映射配置

預設情況下(除非明确配置)MappingR2dbcConverter在您建立DatabaseClient. 您可以建立自己的MappingR2dbcConverter. 通過建立您自己的執行個體,您可以注冊 Spring 轉換器以将特定類映射到資料庫或從資料庫映射。

您可以配置MappingR2dbcConverter以及DatabaseClient和ConnectionFactory使用基于Java的中繼資料。以下示例使用 Spring 的基于 Java 的配置:

如果設定setForceQuote為R2dbcMappingContext totrue,則從類和屬性派生的表名和列名将與資料庫特定的引号一起使用。這意味着可以在這些名稱中使用保留的 SQL 字(例如 order)。您可以通過重寫這樣做r2dbcMappingContext(Optional)的

AbstractR2dbcConfiguration。Spring Data 将此類名稱的字母大小寫轉換為不使用引用時配置的資料庫也使用的形式。是以,您可以在建立表時使用不帶引号的名稱,隻要您的名稱中不使用關鍵字或特殊字元即可。對于遵循 SQL 标準的資料庫,這意味着名稱被轉換為大寫。引用字元和名稱大寫的方式由 used 控制Dialect。有關如何配置自定義方言的資訊,請參閱R2DBC 驅動程式。

例 87.@Configuration 類來配置 R2DBC 映射支援

@Configuration

public class MyAppConfig extends AbstractR2dbcConfiguration {

public ConnectionFactory connectionFactory() {

return ConnectionFactories.get("r2dbc:…");           

// the following are optional

@Override

protected List

getCustomConverters() {
List<Converter<?, ?>> converterList = new ArrayList<Converter<?, ?>>();
converterList.add(new org.springframework.data.r2dbc.test.PersonReadConverter());
converterList.add(new org.springframework.data.r2dbc.test.PersonWriteConverter());
return converterList;           

AbstractR2dbcConfiguration要求您實作一個定義ConnectionFactory.

您可以通過覆寫該r2dbcCustomConversions方法向轉換器添加其他轉換器。

您可以NamingStrategy通過将自定義注冊為 bean來配置它。該NamingStrategy控件類和屬性的名稱是如何地轉化為表和列的名稱。

AbstractR2dbcConfiguration建立一個DatabaseClient執行個體并将其注冊到名為 的容器中databaseClient。

16.4.基于中繼資料的映射

要充分利用 Spring Data R2DBC 支援中的對象映射功能,您應該使用注釋對映射的對象進行@Table注釋。盡管映射架構沒有必要具有此注釋(您的 POJO 已正确映射,即使沒有任何注釋),但它允許類路徑掃描器查找和預處理您的域對象以提取必要的中繼資料。如果你不使用這個注解,你的應用程式在你第一次存儲域對象時會受到輕微的性能影響,因為映射架構需要建立它的内部中繼資料模型,以便它知道你的域對象的屬性以及如何堅持他們。以下示例顯示了一個域對象:

示例 88. 示例域對象

package com.mycompany.domain;

@Table

public class Person {

@Id

private Long id;

private Integer ssn;

private String firstName;

private String lastName;

該@Id注解告訴你想作為主鍵使用哪個屬性映射器。

16.4.1.預設類型映射

下表解釋了實體的屬性類型如何影響映射:

列的本機資料類型取決于 R2DBC 驅動程式類型映射。驅動程式可以提供額外的簡單類型,例如幾何類型。

16.4.2.映射注釋概述

所述MappingR2dbcConverter可以使用中繼資料來驅動對象的映射的行。以下注釋可用:

@Id: 在字段級别應用以标記主鍵。

@Table: 應用于類級别,表示該類是映射到資料庫的候選。您可以指定存儲資料庫的表的名稱。

@Transient: 預設情況下,所有字段都映射到行。此注釋将應用它的字段排除在資料庫中。瞬态屬性不能在持久性構造函數中使用,因為轉換器無法實作構造函數參數的值。

@PersistenceConstructor: 标記給定的構造函數——即使是受包保護的構造函數——在從資料庫執行個體化對象時使用。構造函數參數按名稱映射到檢索行中的值。

@Value:這個注解是Spring架構的一部分。在映射架構内,它可以應用于構造函數參數。這使您可以使用 Spring 表達式語言語句來轉換在資料庫中檢索到的鍵值,然後再使用它來構造域對象。為了引用給定行的列,必須使用以下表達式:@Value("#root.myProperty")其中 root 指的是給定的根Row。

@Column: 在字段級别應用,用于描述列在行中表示的名稱,讓名稱與類的字段名稱不同。用@Column注釋指定的名稱在 SQL 語句中使用時總是被引用。對于大多數資料庫,這意味着這些名稱區分大小寫。這也意味着您可以在這些名稱中使用特殊字元。但是,不建議這樣做,因為它可能會導緻其他工具出現問題。

@Version:應用于字段級别用于樂觀鎖定并檢查儲存操作的修改。值是null(zero對于原始類型)被視為新實體的标記。最初存儲的值是zero(one對于原始類型)。每次更新時,版本都會自動增加。請參閱樂觀鎖定以擷取更多參考。

映射中繼資料基礎結構在與spring-data-commons技術無關的單獨項目中定義。在 R2DBC 支援中使用特定的子類來支援基于注釋的中繼資料。也可以采用其他政策(如果有需求)。

16.4.3.自定義對象建構

映射子系統允許通過使用注釋對構造函數進行注釋來自定義對象構造。@PersistenceConstructor用于構造函數參數的值通過以下方式解析:

如果一個參數用注解進行@Value注解,則給定的表達式被求值,并将結果用作參數值。

如果 Java 類型具有名稱與輸入行的給定字段比對的屬性,則其屬性資訊用于選擇将輸入字段值傳遞到的适當構造函數參數。這僅在 Java.class檔案中存在參數名稱資訊時才有效,您可以通過使用調試資訊編譯源代碼或使用Java 8 中的-parameters指令行開關來實作javac。

否則,MappingException抛出 a 以訓示無法綁定給定的構造函數參數。

class OrderItem {

private @Id final String id;

private final int quantity;

private final double unitPrice;

OrderItem(String id, int quantity, double unitPrice) {

this.id = id;
this.quantity = quantity;
this.unitPrice = unitPrice;           

// getters/setters ommitted

16.4.4.使用顯式轉換器覆寫映射

在存儲和查詢對象時,擁有一個R2dbcConverter執行個體來處理所有 Java 類型到OutboundRow執行個體的映射通常很友善。但是,有時您可能希望R2dbcConverter執行個體完成大部分工作,但讓您有選擇地處理特定類型的轉換——也許是為了優化性能。

要自己有選擇地處理轉換,請

org.springframework.core.convert.converter.Converter使用R2dbcConverter.

您可以使用 中的r2dbcCustomConversions方法

AbstractR2dbcConfiguration來配置轉換器。本章開頭的示例展示了如何使用 Java 執行配置。

自定義頂級實體轉換需要非對稱類型進行轉換。入站資料是從 R2DBC 的Row. 出站資料(與INSERT/UPDATE語句一起使用)被表示為OutboundRow然後被組裝成一個語句。

以下 Spring Converter 實作的示例從 aRow轉換為PersonPOJO:

@ReadingConverter

public class PersonReadConverter implements Converter<Row, Person> {

public Person convert(Row source) {

Person p = new Person(source.get("id", String.class),source.get("name", String.class));
p.setAge(source.get("age", Integer.class));
return p;           

請注意,轉換器适用于單一屬性。集合屬性(例如Collection)按元素進行疊代和轉換。Converter<List>, OutboundRow不支援集合轉換器(例如)。

R2DBC 使用裝箱原語(Integer.class而不是int.class)來傳回原語值。

以下示例從 a 轉換Person為 a OutboundRow:

@WritingConverter

public class PersonWriteConverter implements Converter<Person, OutboundRow> {

public OutboundRow convert(Person source) {

OutboundRow row = new OutboundRow();
row.put("id", SettableValue.from(source.getId()));
row.put("name", SettableValue.from(source.getFirstName()));
row.put("age", SettableValue.from(source.getAge()));
return row;           

使用顯式轉換器覆寫枚舉映射

某些資料庫(例如Postgres)可以使用其特定于資料庫的枚舉列類型本機寫入枚舉值。Spring DataEnum預設将String值轉換為最大可移植性的值。要保留實際枚舉值,請注冊一個@Writing轉換器,其源和目标類型使用實際枚舉類型以避免使用Enum.name()轉換。此外,您需要在驅動程式級别配置枚舉類型,以便驅動程式知道如何表示枚舉類型。

以下示例顯示了Color本機讀取和寫入枚舉值的相關元件:

enum Color {

Grey, Blue           

class ColorConverter extends EnumWriteSupport {

class Product {

@Id long id;
Color color;

// …