免費(fèi)圖片轉(zhuǎn)換jpg格式軟件 免費(fèi)的圖片轉(zhuǎn)換格式軟件
2024-01-27
更新時(shí)間:2024-01-27 00:11:11作者:佚名
原標(biāo)題:Spring認(rèn)證中國(guó)教育管理中心-Spring Data R2DBC框架教程三(Spring中國(guó)教育管理中心)
您通常在存儲(chǔ)庫(kù)上觸發(fā)的大多數(shù)數(shù)據(jù)訪問(wèn)操作都會(huì)導(dǎo)致對(duì)數(shù)據(jù)庫(kù)運(yùn)行查詢。定義這樣的查詢就是在存儲(chǔ)庫(kù)接口上聲明一個(gè)方法,如以下示例所示:
示例 61.帶有查詢方法的 PersonRepository
interface ReactivePersonRepository extends ReactiveSortingRepository<Person, Long> { Flux<Person> findByFirstname(String firstname); Flux<Person> findByFirstname(Publisher<String> firstname); Flux<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable); Mono<Person> findByFirstnameAndLastname(String firstname, String lastname); Mono<Person> findFirstByLastname(String lastname); @Query("select * FROM person WHERE lastname = :lastname") Flux<Person> findByLastname(String lastname); @Query("select firstname, lastname FROM person WHERE lastname = $1") Mono<Person> findFirstByLastname(String lastname); }
該方法顯示了對(duì)所有具有給定 的人的查詢firstname。該查詢是通過(guò)解析可以與And和連接的約束的方法名稱來(lái)派生的Or。因此,方法名稱導(dǎo)致查詢表達(dá)式為select … FROM person WHERE firstname = :firstname。
firstname一旦給定的firstname發(fā)出 ,該方法就會(huì)顯示對(duì)所有具有給定的人的查詢Publisher。
使用Pageable來(lái)抵消和排序參數(shù)傳遞到數(shù)據(jù)庫(kù)。
查找給定條件的單個(gè)實(shí)體。它以
IncorrectResultSizeDataAccessException非唯一結(jié)果結(jié)束。
除非 <4>,否則即使查詢產(chǎn)生更多結(jié)果行,也總是發(fā)出第一個(gè)實(shí)體。
該findByLastname方法顯示了對(duì)所有具有給定姓氏的人的查詢。
對(duì)Person僅投影firstname和lastname列的單個(gè)實(shí)體的查詢。帶注釋的查詢使用本機(jī)綁定標(biāo)記,在本例中是 Postgres 綁定標(biāo)記。
請(qǐng)注意,@Query注釋中使用的 select 語(yǔ)句的列必須與NamingStrategy為相應(yīng)屬性生成的名稱匹配。如果 select 語(yǔ)句不包含匹配的列,則不會(huì)設(shè)置該屬性。如果持久性構(gòu)造函數(shù)需要該屬性,則提供 null 或(對(duì)于原始類型)默認(rèn)值。
下表顯示了查詢方法支持的關(guān)鍵字:
前面的部分描述了如何聲明查詢以訪問(wèn)給定的實(shí)體或?qū)嶓w集合。使用上表中的關(guān)鍵字可以與刪除匹配行的派生查詢結(jié)合使用delete…By或remove…By創(chuàng)建派生查詢。
示例 62.delete…By查詢
interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> { Mono<Integer> deleteByLastname(String lastname); Mono<Void> deletePersonByLastname(String lastname); Mono<Boolean> deletePersonByLastname(String lastname); }
使用返回類型Mono<Integer>返回受影響的行數(shù)。
使用Voidjust 報(bào)告行是否已成功刪除而不發(fā)出結(jié)果值。
使用Boolean報(bào)告是否至少刪除了一行。
由于這種方法對(duì)于全面的自定義功能是可行的,您可以通過(guò)使用 注釋查詢方法來(lái)修改只需要參數(shù)綁定的查詢@Modifying,如以下示例所示:
@Modifying@Query("update person SET firstname = :firstname where lastname = :lastname")Mono<Integer> setFixedFirstnameFor(String firstname, String lastname);
修改查詢的結(jié)果可以是:
Void(或 Kotlin Unit)丟棄更新計(jì)數(shù)并等待完成。Integer 或其他數(shù)字類型發(fā)出受影響的行數(shù)。Boolean 發(fā)出是否至少更新了一行。該@Modifying注釋是唯一與組合相關(guān)的@Query注釋。派生的自定義方法不需要此注釋。
或者,您可以使用Spring Data Repositories 的自定義實(shí)現(xiàn)中描述的工具添加自定義修改行為。
查詢字符串定義可與 SpEL 表達(dá)式一起使用以在運(yùn)行時(shí)創(chuàng)建動(dòng)態(tài)查詢。SpEL 表達(dá)式可以提供在運(yùn)行查詢之前計(jì)算的謂詞值。
表達(dá)式通過(guò)包含所有參數(shù)的數(shù)組公開(kāi)方法參數(shù)。以下查詢用于[0] 聲明 for lastname(相當(dāng)于:lastname參數(shù)綁定)的謂詞值:
@Query("select * FROM person WHERE lastname = :#{[0]}")Flux<Person> findByQueryWithExpression(String lastname);
查詢字符串中的 SpEL 可以成為增強(qiáng)查詢的強(qiáng)大方法。但是,它們也可以接受范圍廣泛的不需要的參數(shù)。您應(yīng)該確保在將字符串傳遞給查詢之前對(duì)其進(jìn)行清理,以避免對(duì)查詢進(jìn)行不必要的更改。
表達(dá)式的支持是可擴(kuò)展的通過(guò)查詢SPI:
org.springframework.data.spel.spi.EvaluationContextExtension。Query SPI 可以提供屬性和函數(shù),并且可以自定義根對(duì)象。構(gòu)建查詢時(shí),在 SpEL 評(píng)估時(shí)從應(yīng)用程序上下文中檢索擴(kuò)展。
將 SpEL 表達(dá)式與普通參數(shù)結(jié)合使用時(shí),請(qǐng)使用命名參數(shù)表示法而不是本機(jī)綁定標(biāo)記,以確保正確的綁定順序。
Spring Data R2DBC 還允許您使用 Query By Example 來(lái)設(shè)計(jì)查詢。此技術(shù)允許您使用“探針”對(duì)象。本質(zhì)上,任何不為空或null將用于匹配的字段。
下面是一個(gè)例子:
Employee employee = new Employee(); employee.setName("Frodo");Example<Employee> example = Example.of(employee); Flux<Employee> employees = repository.findAll(example); // do whatever with the flux
使用條件創(chuàng)建域?qū)ο螅╪ull字段將被忽略)。
使用域?qū)ο?,?chuàng)建一個(gè)Example.
通過(guò)R2dbcRepository, 執(zhí)行查詢(findOne用于 a Mono)。
這說(shuō)明了如何使用域?qū)ο笾谱骱?jiǎn)單的探針。在這種情況下,它將根據(jù)Employee對(duì)象的name字段等于 進(jìn)行查詢Frodo。 null字段被忽略。
Employee employee = new Employee();employee.setName("Baggins");employee.setRole("ring bearer");ExampleMatcher matcher = matching() .withMatcher("name", endsWith()) .withIncludeNullValues() .withIgnorePaths("role"); Example<Employee> example = Example.of(employee, matcher); Flux<Employee> employees = repository.findAll(example);// do whatever with the flux
創(chuàng)建一個(gè)ExampleMatcher匹配所有字段的自定義(用于matchingAny()匹配任何字段)
對(duì)于name字段,使用與字段末尾匹配的通配符
匹配列null(不要忘記在關(guān)系數(shù)據(jù)庫(kù)NULL中不相等NULL)。
role形成查詢時(shí)忽略該字段。
將自定義ExampleMatcher插入探頭。
也可以withTransform()對(duì)任何屬性應(yīng)用 a ,允許您在形成查詢之前轉(zhuǎn)換屬性。例如,您可以在創(chuàng)建查詢之前將 atoUpperCase()應(yīng)用于String-based 屬性。
當(dāng)您事先不知道查詢中所需的所有字段時(shí),Query By Example 真的很有用。如果您在網(wǎng)頁(yè)上構(gòu)建過(guò)濾器,用戶可以在其中選擇字段,按示例查詢是靈活地將其捕獲到有效查詢中的好方法。
下表描述了 Spring Data 提供的用于檢測(cè)實(shí)體是否為新實(shí)體的策略:
Spring Data R2DBC 使用 ID 來(lái)標(biāo)識(shí)實(shí)體。實(shí)體的 ID 必須使用 Spring Data 的@Id注解進(jìn)行注解。
當(dāng)您的數(shù)據(jù)庫(kù)具有用于 ID 列的自動(dòng)增量列時(shí),生成的值在將其插入數(shù)據(jù)庫(kù)后設(shè)置在實(shí)體中。
當(dāng)實(shí)體是新的并且標(biāo)識(shí)符值默認(rèn)為其初始值時(shí),Spring Data R2DBC 不會(huì)嘗試插入標(biāo)識(shí)符列的值。這適用0于原始類型,并且null如果標(biāo)識(shí)符屬性使用數(shù)字包裝類型,例如Long.
一個(gè)重要的限制是,在保存實(shí)體后,該實(shí)體不能再是新的。請(qǐng)注意,實(shí)體是否是新實(shí)體是實(shí)體狀態(tài)的一部分。對(duì)于自動(dòng)增量列,這會(huì)自動(dòng)發(fā)生,因?yàn)?ID 由 Spring Data 使用 ID 列中的值設(shè)置。
該@Version注釋在 R2DBC 的上下文中提供類似于 JPA 的語(yǔ)法,并確保更新僅應(yīng)用于具有匹配版本的行。因此,version 屬性的實(shí)際值被添加到更新查詢中,如果另一個(gè)操作同時(shí)更改了該行,則更新不會(huì)產(chǎn)生任何影響。在這種情況下,
OptimisticLockingFailureException會(huì)拋出an 。以下示例顯示了這些功能:
@Tableclass Person { @Id Long id; String firstname; String lastname; @Version Long version;}R2dbcEntityTemplate template = …;Mono<Person> daenerys = template.insert(new Person("Daenerys")); Person other = template.select(Person.class) .matching(query(where("id").is(daenerys.getId()))) .first().block(); daenerys.setLastname("Targaryen");template.update(daenerys); template.update(other).subscribe(); // emits OptimisticLockingFailureException
最初插入行。version設(shè)置為0。
加載剛剛插入的行。version還在0。
更新與行version = 0.SET的lastname和凹凸version來(lái)1。
嘗試更新仍具有 的先前加載的行version = 0。操作失敗并顯示
OptimisticLockingFailureException,因?yàn)楫?dāng)前version是1。
Spring Data 查詢方法通常返回存儲(chǔ)庫(kù)管理的聚合根的一個(gè)或多個(gè)實(shí)例。但是,有時(shí)可能需要根據(jù)這些類型的某些屬性創(chuàng)建投影。Spring Data 允許對(duì)專用返回類型進(jìn)行建模,以更有選擇地檢索托管聚合的部分視圖。
想象一個(gè)存儲(chǔ)庫(kù)和聚合根類型,例如以下示例:
示例 63. 示例聚合和存儲(chǔ)庫(kù)
class Person { @Id UUID id; String firstname, lastname; Address address; static class Address { String zipCode, city, street; }}interface PersonRepository extends Repository<Person, UUID> { Flux<Person> findByLastname(String lastname);}
現(xiàn)在假設(shè)我們只想檢索此人的姓名屬性。Spring Data 提供什么方法?來(lái)實(shí)現(xiàn)這一目標(biāo)?本章的其余部分回答了這個(gè)問(wèn)題。
將查詢結(jié)果限制為僅名稱屬性的最簡(jiǎn)單方法是聲明一個(gè)接口,該接口公開(kāi)要讀取的屬性的訪問(wèn)器方法,如以下示例所示:
示例 64. 用于檢索屬性子集的投影接口
interface NamesOnly { String getFirstname(); String getLastname();}
這里的重要一點(diǎn)是這里定義的屬性與聚合根中的屬性完全匹配。這樣做可以添加一個(gè)查詢方法,如下所示:
示例 65. 使用基于接口的投影和查詢方法的存儲(chǔ)庫(kù)
interface PersonRepository extends Repository<Person, UUID> { Flux<NamesOnly> findByLastname(String lastname);}
查詢執(zhí)行引擎在運(yùn)行時(shí)為每個(gè)返回的元素創(chuàng)建該接口的代理實(shí)例,并將對(duì)公開(kāi)方法的調(diào)用轉(zhuǎn)發(fā)到目標(biāo)對(duì)象。
在您Repository中聲明一個(gè)覆蓋基本方法的方法(例如,在 中聲明CrudRepository,特定于商店的存儲(chǔ)庫(kù)接口或Simple…Repository)會(huì)導(dǎo)致對(duì)基本方法的調(diào)用,而不管聲明的返回類型如何。確保使用兼容的返回類型,因?yàn)榛痉椒ú荒苡糜谕队?。一些商店模塊支持@Query注釋將覆蓋的基本方法轉(zhuǎn)換為查詢方法,然后可用于返回投影。
可以遞歸地使用投影。如果您還想包含一些Address信息,請(qǐng)為此創(chuàng)建一個(gè)投影接口并從 的聲明中返回該接口getAddress(),如下例所示:
示例 66. 用于檢索屬性子集的投影接口
interface PersonSummary { String getFirstname(); String getLastname(); AddressSummary getAddress(); interface AddressSummary { String getCity(); }}
在方法調(diào)用時(shí),address獲取目標(biāo)實(shí)例的屬性并依次包裝到投影代理中。
其訪問(wèn)器方法都與目標(biāo)聚合的屬性匹配的投影接口被認(rèn)為是封閉投影。以下示例(我們?cè)诒菊虑懊嬉彩褂眠^(guò))是一個(gè)封閉投影:
例 67. 一個(gè)封閉的投影
interface NamesOnly { String getFirstname(); String getLastname();}
如果使用封閉投影,Spring Data 可以優(yōu)化查詢執(zhí)行,因?yàn)槲覀冎乐С滞队按硭璧乃袑傩浴S嘘P(guān)更多詳細(xì)信息,請(qǐng)參閱參考文檔中特定于模塊的部分。
投影接口中的訪問(wèn)器方法也可用于通過(guò)使用@Value注釋計(jì)算新值,如以下示例所示:
例 68. 一個(gè)開(kāi)放的投影
interface NamesOnly { @Value("#{target.firstname + ' ' + target.lastname}") String getFullName(); …}
支持投影的聚合根在target變量中可用。使用的投影界面@Value是開(kāi)放式投影。在這種情況下,Spring Data 無(wú)法應(yīng)用查詢執(zhí)行優(yōu)化,因?yàn)?SpEL 表達(dá)式可以使用聚合根的任何屬性。
中使用的表達(dá)式@Value不應(yīng)太復(fù)雜——您希望避免在String變量中編程。對(duì)于非常簡(jiǎn)單的表達(dá)式,一種選擇可能是采用默認(rèn)方法(在 Java 8 中引入),如以下示例所示:
示例 69. 使用自定義邏輯的默認(rèn)方法的投影界面
interface NamesOnly { String getFirstname(); String getLastname(); default String getFullName() { return getFirstname().concat(" ").concat(getLastname()); }}
這種方法要求您能夠純粹基于投影接口上公開(kāi)的其他訪問(wèn)器方法來(lái)實(shí)現(xiàn)邏輯。第二個(gè)更靈活的選擇是在 Spring bean 中實(shí)現(xiàn)自定義邏輯,然后從 SpEL 表達(dá)式調(diào)用它,如以下示例所示:
示例 70. 示例 Person 對(duì)象
@Componentclass MyBean { String getFullName(Person person) { … }}interface NamesOnly { @Value("#{@myBean.getFullName(target)}") String getFullName(); …}
請(qǐng)注意 SpEL 表達(dá)式如何引用myBean和調(diào)用該getFullName(…)方法并將投影目標(biāo)作為方法參數(shù)轉(zhuǎn)發(fā)。由 SpEL 表達(dá)式評(píng)估支持的方法也可以使用方法參數(shù),然后可以從表達(dá)式中引用這些參數(shù)。方法參數(shù)可通過(guò)Object名為的數(shù)組獲得args。以下示例顯示了如何從args數(shù)組中獲取方法參數(shù):
示例 71. 示例 Person 對(duì)象
interface NamesOnly { @Value("#{args[0] + ' ' + target.firstname + '!'}") String getSalutation(String prefix);}
同樣,對(duì)于更復(fù)雜的表達(dá)式,您應(yīng)該使用 Spring bean 并讓表達(dá)式調(diào)用一個(gè)方法,如前所述。
投影接口中的 getter 可以使用可為空的包裝器來(lái)提高空安全性。當(dāng)前支持的包裝器類型有:
java.util.Optionalcom.google.common.base.Optionalscala.Optionio.vavr.control.Option示例 72. 使用可為空包裝器的投影接口
interface NamesOnly { Optional<String> getFirstname();}
如果基礎(chǔ)投影值不是null,則使用包裝器類型的當(dāng)前表示返回值。如果支持值是null,則 getter 方法返回所用包裝器類型的空表示。
定義投影的另一種方法是使用值類型 DTO(數(shù)據(jù)傳輸對(duì)象),這些 DTO 包含應(yīng)該檢索的字段的屬性。這些 DTO 類型的使用方式與使用投影接口的方式完全相同,只是不發(fā)生代理并且不可以應(yīng)用嵌套投影。
如果存儲(chǔ)通過(guò)限制要加載的字段來(lái)優(yōu)化查詢執(zhí)行,則要加載的字段由公開(kāi)的構(gòu)造函數(shù)的參數(shù)名稱確定。
以下示例顯示了一個(gè)投影 DTO:
例 73. 一個(gè)投影 DTO
class NamesOnly { private final String firstname, lastname; NamesOnly(String firstname, String lastname) { this.firstname = firstname; this.lastname = lastname; } String getFirstname() { return this.firstname; } String getLastname() { return this.lastname; } // equals(…) and hashCode() implementations}
避免投影 DTO 的樣板代碼
您可以使用Project Lombok顯著簡(jiǎn)化 DTO 的代碼,它提供了一個(gè)@Value注解(不要與@Value前面的接口示例中顯示的Spring 的注解混淆)。如果使用 Project Lombok 的@Value注釋,之前顯示的示例 DTO 將變?yōu)橐韵聝?nèi)容:
@Value
class NamesOnly {
String firstname, lastname;
}
字段是private final默認(rèn)的,并且該類公開(kāi)了一個(gè)構(gòu)造函數(shù),該構(gòu)造函數(shù)接受所有字段并自動(dòng)獲取equals(…)和hashCode()實(shí)現(xiàn)方法。
到目前為止,我們已經(jīng)使用投影類型作為集合的返回類型或元素類型。但是,您可能希望選擇在調(diào)用時(shí)使用的類型(這使其成為動(dòng)態(tài)的)。要應(yīng)用動(dòng)態(tài)投影,請(qǐng)使用如下例所示的查詢方法:
示例 74. 使用動(dòng)態(tài)投影參數(shù)的存儲(chǔ)庫(kù)
interface PersonRepository extends Repository<Person, UUID> { <T> Flux<T> findByLastname(String lastname, Class<T> type);}
這樣,該方法可用于按原樣或應(yīng)用投影獲取聚合,如以下示例所示:
示例 75.使用具有動(dòng)態(tài)投影的存儲(chǔ)庫(kù)
void someMethod(PersonRepository people) { Flux<Person> aggregates = people.findByLastname("Matthews", Person.class); Flux<NamesOnly> aggregates = people.findByLastname("Matthews", NamesOnly.class);}
返回接口或 DTO 投影的查詢方法由實(shí)際查詢生成的結(jié)果支持。接口投影通常首先依賴于將結(jié)果映射到域類型來(lái)考慮潛在的@Column類型映射,而實(shí)際的投影代理使用潛在的部分物化實(shí)體來(lái)公開(kāi)投影數(shù)據(jù)。
DTO 投影的結(jié)果映射取決于實(shí)際查詢類型。派生查詢使用域類型來(lái)映射結(jié)果,Spring Data 僅從域類型上可用的屬性創(chuàng)建 DTO 實(shí)例。不支持在 DTO 中聲明域類型上不可用的屬性。
基于字符串的查詢使用不同的方法,因?yàn)閷?shí)際查詢,特別是字段投影和結(jié)果類型聲明是緊密結(jié)合在一起的。與查詢方法一起使用的 DTO 投影,將@Query映射查詢結(jié)果直接注釋為 DTO 類型。不考慮域類型的字段映射。直接使用 DTO 類型,您的查詢方法可以從不限于域模型的更動(dòng)態(tài)的投影中受益。
ps:【項(xiàng)目】淘寶項(xiàng)目邀請(qǐng)用戶即可獲取收益
ps:【活動(dòng)】支付寶大額紅包點(diǎn)擊掃碼領(lǐng)取
ps:【本站主題】自適應(yīng)資訊類的網(wǎng)站主題