新聞中心
本文轉(zhuǎn)載自微信公眾號(hào)「Java方向盤」,作者方向盤 。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java方向盤公眾號(hào)。

10年積累的網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有東風(fēng)免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
正文
Bean Validation
數(shù)據(jù)校驗(yàn):在任何時(shí)候,當(dāng)你想要處理一個(gè)應(yīng)用程序的邏輯時(shí),確保數(shù)據(jù)的正確性是你必須要考慮和面對(duì)的事情。也就說(shuō)我們必須通過(guò)某種手段,確保輸入進(jìn)來(lái)的數(shù)據(jù)是正確的。
然而,應(yīng)用程序一般是分層的,同樣的驗(yàn)證邏輯往往會(huì)出現(xiàn)在不同的層,這樣就會(huì)給代碼組織管理上帶來(lái)冗余負(fù)擔(dān)。為了避免這類情況的發(fā)生,最好就是做一層抽象:將驗(yàn)證邏輯與響應(yīng)的模型進(jìn)行綁定,這就是Bean Validation。
Bean Validation簡(jiǎn)直就是業(yè)務(wù)開發(fā)中祛掉壞味道代碼的利器,完美的實(shí)現(xiàn)契約式編程,大大提高開發(fā)效率,降低出錯(cuò)概率。
注意:Bean Validation它是一種通用規(guī)范,并不只屬于Web層技術(shù),即使大概率你可能只在Spring MVC中使用過(guò)它~
javax.validation validation-api 2.0.1.Final jakarta.validation jakarta.validation-api 3.0.0
版本歷程
| 版本 | 發(fā)布日期 | JSR版本 | 對(duì)應(yīng)Java EE版本 | 主要特性 |
|---|---|---|---|---|
| 1.0 | 2009.11 | JSR 303 | Java EE 6 | 對(duì)JavaBean進(jìn)行驗(yàn)證,提供13個(gè)注解 |
| 1.1 | 2013.05 | JSR 349 | Java EE 7 | 新增方法級(jí)驗(yàn)證(參數(shù)、返回值) |
| 2.0 | 2017.08 | JSR 380 | Java EE 8 | 新增9個(gè)注解達(dá)到22個(gè)。支持容器元素驗(yàn)證 |
| 3.0 | 2020.07 | Jakarta管理 | Jakarta EE 9 | 同上 |
截止到2.0/3.0版本,共計(jì)13 + 9 = 22個(gè)內(nèi)建標(biāo)準(zhǔn)的注解:
| 序號(hào) | 注解 | 支持類型 | 含義 | null值是否校驗(yàn) |
|---|---|---|---|---|
| 01 | @AssertFalse | bool | 元素必須是false | 否 |
| 02 | @AssertTrue | bool | 元素必須是true | 否 |
| 03 | @DecimalMax | Number的子類型(浮點(diǎn)數(shù)除外)以及String | 元素必須是一個(gè)數(shù)字,且值必須<=最大值 | 否 |
| 04 | @DecimalMin | 同上 | 元素必須是一個(gè)數(shù)字,且值必須>=最大值 | 否 |
| 05 | @Max | 同上 | 同上 | 否 |
| 06 | @Min | 同上 | 同上 | 否 |
| 07 | @Digits | 同上 | 元素構(gòu)成是否合法(整數(shù)部分和小數(shù)部分) | 否 |
| 08 | @Future | 時(shí)間類型(包括JSR310) | 元素必須為一個(gè)將來(lái)(不包含相等)的日期(比較精確到毫秒) | 否 |
| 09 | @Past | 同上 | 元素必須為一個(gè)過(guò)去(不包含相等)的日期(比較精確到毫秒) | 否 |
| 10 | @NotNull | any | 元素不能為null | 是 |
| 11 | @Null | any | 元素必須為null | 是 |
| 12 | @Pattern | 字符串 | 元素需符合指定的正則表達(dá)式 | 否 |
| 13 | @Size | String/Collection/Map/Array | 元素大小需在指定范圍中 | 否 |
| -- | -- | 2.0版本新增了9個(gè)注解,如下 | -- | -- |
| 14 | 字符串 | 元素必須為電子郵箱地址 | 否 | |
| 15 | @NotEmpty | 容器類型 | 集合的Size必須大于0 | 是 |
| 16 | @NotBlank | 字符串 | 字符串必須包含至少一個(gè)非空白的字符 | 是 |
| 17 | @Positive | 數(shù)字類型 | 元素必須為正數(shù)(不包括0) | 否 |
| 18 | @PositiveOrZero | 同上 | 同上(包括0) | 否 |
| 19 | @Negative | 同上 | 元素必須為負(fù)數(shù)(不包括0) | 否 |
| 20 | @NegativeOrZero | 同上 | 同上(包括0) | 否 |
| 21 | @PastOrPresent | 時(shí)間類型 | 在@Past基礎(chǔ)上包括相等 | 否 |
| 22 | @FutureOrPresent | 時(shí)間類型 | 在@Futrue基礎(chǔ)上包括相等 | 否 |
值得注意的是,還有些比較常用的注解如@DurationMin、@DurationMax、@Length、@ScriptAssert、@ParameterScriptAssert、@Range、@UniqueElements等,它們不屬于標(biāo)準(zhǔn)注解,而屬于Hibernate的。但正如上面所說(shuō),Hibernate Validator它幾乎就是標(biāo)準(zhǔn),所以在開發(fā)中使用也是沒(méi)有任何問(wèn)題的。
生存現(xiàn)狀
Spring在數(shù)據(jù)驗(yàn)證這塊的API設(shè)計(jì)得比較失敗,Bean Validation很好的彌補(bǔ)了其不足。
雖然Bean Validation存在時(shí)間已經(jīng)很長(zhǎng)了,但很多程序員對(duì)其依舊“無(wú)感”。隨著DDD領(lǐng)域驅(qū)動(dòng)模型的流行和普及,它的重要性日趨凸顯,畢竟它的設(shè)計(jì)思想和域模型是一致的,能起到很好的“化學(xué)反應(yīng)”,從而事半功倍。
簡(jiǎn)單的講,若你是一個(gè)模塊化設(shè)計(jì)愛好者、優(yōu)雅代碼的擁護(hù)者、聲明式編程的追求者,那么Bean Validation對(duì)你的幫助絕非一點(diǎn)點(diǎn)。
實(shí)現(xiàn)(框架)
雖說(shuō)BV規(guī)范的實(shí)現(xiàn)框架一般有兩種:Hibernate Validator和Apache BVal,但實(shí)際上基本可認(rèn)為前者是唯一實(shí)現(xiàn),它就等同于標(biāo)準(zhǔn),版本對(duì)應(yīng)關(guān)系如下:
| BV版本 | HV實(shí)現(xiàn)版本 |
|---|---|
| 1.0 | 4.x |
| 1.1 | 5.x |
| 2.0 | 6.x |
| 3.0 | 7.x |
說(shuō)明:Hibernate Validator 7.x專為Jakarta Bean Validation 3.0打造,適配jakarta.*命名空間
代碼示例
導(dǎo)入BV的實(shí)現(xiàn)框架:
org.hibernate.validator hibernate-validator
準(zhǔn)備一個(gè)Java Bean,并通過(guò)注解聲明規(guī)則:
- /**
- * 在此處添加備注信息
- *
- * @author YourBatman. Send email to me
- * @site https://yourbatman.cn
- * @date 2021/10/6 10:16
- * @since 0.0.1
- */
- @Data
- public class Person {
- @Positive
- private long id;
- @NotBlank
- private String name;
- @NotNull
- @PositiveOrZero
- private Integer age;
- }
書寫驗(yàn)證代碼:
- @Test
- public void validBean() {
- // 使用默認(rèn)配置獲得驗(yàn)證器
- Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
- // 準(zhǔn)備(待校驗(yàn)的)Bean
- Person person = new Person();
- person.setId(-1);
- person.setName("YourBatman");
- person.setAge(18);
- // 使用校驗(yàn)器對(duì)其執(zhí)行校驗(yàn)
- Set
> violations = validator.validate(person); - // 打印校驗(yàn)結(jié)果
- if (violations.isEmpty()) {
- System.out.println("校驗(yàn)通過(guò)!");
- } else {
- System.out.println("校驗(yàn)不通過(guò)!錯(cuò)誤詳情如下:");
- violations.forEach(v -> System.out.println("\t" + v.getPropertyPath() + v.getMessage() + ",但你的值是:" + v.getInvalidValue()));
- }
- }
運(yùn)行程序,輸出為:
- 校驗(yàn)不通過(guò)!錯(cuò)誤詳情如下:
- id必須是正數(shù),但你的值是:-1
若運(yùn)行時(shí)碰到這個(gè)類找不著:
不要“害怕”,這不正是上篇文章提到內(nèi)容嗎:EL表達(dá)式用于數(shù)據(jù)校驗(yàn),加入進(jìn)來(lái)即可。
org.glassfish jakarta.el
為了加強(qiáng)理解,再來(lái)一個(gè)方法級(jí)的校驗(yàn)示例(參數(shù)、返回值):
- public @Positive int toInt(@NotNull String numStr) {
- int result = Integer.parseInt(numStr);
- return result;
- }
不同于Java Bean表示狀態(tài)(靜態(tài)的),方法/構(gòu)造器是執(zhí)行期(動(dòng)態(tài)的)才能進(jìn)行校驗(yàn),所以執(zhí)行校驗(yàn)的代碼可以這么做:
- public @Positive int toInt(@NotNull String numStr) throws NoSuchMethodException {
- // 使用默認(rèn)配置獲得驗(yàn)證器(用于方法、構(gòu)造器的校驗(yàn)器)
- ExecutableValidator executableValidator = Validation.buildDefaultValidatorFactory().getValidator().forExecutables();
- // 執(zhí)行參數(shù)校驗(yàn)邏輯start...
- Method currMethod = BeanValidationDemo.class.getMethod("toInt", String.class);
- Set
> violations = executableValidator.validateParameters(this, currMethod, new Object[]{numStr}); - // 打印校驗(yàn)結(jié)果
- if (violations.isEmpty()) {
- System.out.println("校驗(yàn)通過(guò)!");
- } else {
- System.out.println("校驗(yàn)不通過(guò)!錯(cuò)誤詳情如下:");
- violations.forEach(v -> System.out.println("\t" + v.getPropertyPath() + v.getMessage() + ",但你的值是:" + v.getInvalidValue()));
- throw new IllegalArgumentException("校驗(yàn)不通過(guò)!"); //拋出異常,終止此方法
- }
- // 執(zhí)行參數(shù)校驗(yàn)邏輯end...
- int result = Integer.parseInt(numStr);
- // 執(zhí)行返回值校驗(yàn)邏輯start
- // executableValidator.validateReturnValue();
- // 執(zhí)行返回值校驗(yàn)邏輯end
- return result;
- }
這樣每次方法運(yùn)行時(shí)就能觸發(fā)校驗(yàn)邏輯了。也許,你會(huì)覺得這么做不算完美:侵入性太強(qiáng)了。
是的,不夠優(yōu)雅。但有經(jīng)驗(yàn)的小伙伴似乎一眼就能看出來(lái)如何優(yōu)化:
沒(méi)錯(cuò),就是用AOP來(lái)改善壞味道的代碼,讓校驗(yàn)邏輯和業(yè)務(wù)邏輯完全分離。
至于如何使用AOP,額,筆者就不用再貼代碼示例了吧,給你點(diǎn)提示:
在非Spring場(chǎng)景下,可基于Java EE的@Inteceptors實(shí)現(xiàn)
在Spring場(chǎng)景下,你熟悉的場(chǎng)景
本專欄源代碼:https://github.com/yourbatman/FXP-java-ee
JPA
Java Persistence API:通過(guò)注解或者XML描述【對(duì)象-關(guān)系表】之間的映射關(guān)系,并將實(shí)體對(duì)象持久化到數(shù)據(jù)庫(kù)中。JPA規(guī)范給開發(fā)者帶來(lái)了福音:開發(fā)者面向JPA規(guī)范的接口,但底層的JPA實(shí)現(xiàn)可以任意切換:覺得Hibernate好的,可以選擇Hibernate JPA實(shí)現(xiàn);覺得TopLink好的,可以選擇TopLink JPA實(shí)現(xiàn)……這樣開發(fā)者可以避免為使用Hibernate學(xué)習(xí)一套ORM框架,為使用TopLink又要再學(xué)習(xí)一套ORM框架。
如圖亦可見,JPA是Java EE的野心:Sun公司希望通過(guò)JPA整合ORM技術(shù),實(shí)現(xiàn)天下歸一。實(shí)際情況是,它做到了,除了天朝鐘愛更輕量級(jí)的MyBatis外,海外依舊是使用JPA居多。
學(xué)習(xí)過(guò)Hibernate的應(yīng)當(dāng)可以很輕易的上手Java persistence,因?yàn)镴ava persistence的開發(fā)者其實(shí)就是原h(huán)ibernate的開發(fā)者。再配合以annotation,可以很輕易的開發(fā)出Entity Bean。
注意:是先有Hibernate、TopLink等ORM框架,后才有的JPA來(lái)統(tǒng)一天下的
javax.persistence javax.persistence-api 2.2 jakarta.persistence jakarta.persistence-api 3.0.0
版本歷程
| 版本 | 發(fā)布日期 | JSR版本 | 對(duì)應(yīng)Java EE版本 |
|---|---|---|---|
| 1.0 | 2006.05 | -- | Java EE 5 |
| 2.0 | 2009.12 | JSR 317 | Java EE 6 |
| 2.2 | 2017.08 | JSR 338 | Java EE 8 |
| 3.0 | 2020.11 | Jakarta管理 | Jakarta EE 9 |
JPA 2.1已經(jīng)是一個(gè)非常成熟的規(guī)范,提供了現(xiàn)代應(yīng)用程序所需的大部分功能。2017年夏天發(fā)布的2.2版本(規(guī)范內(nèi)容和2.1規(guī)范差不多),新增了對(duì)Java 8更好的支持,如它的日期時(shí)間Date/Time、重復(fù)注解@Repeatable、Stream形式等等,大大增加了其易用性。
下面列出JPA最常用的一些注解:
| 序號(hào) | 注解 | 標(biāo)注在哪 | 釋義 |
|---|---|---|---|
| 01 | @Entity | 類 | 標(biāo)識(shí)實(shí)體類是JPA實(shí)體,告訴JPA在程序運(yùn)行時(shí)生成實(shí)體類對(duì)應(yīng)表 |
| 02 | @Table | 類 | 定義entity主表的name,catalog,schema等屬性。也就是說(shuō)ORM規(guī)則自定義 |
| 03 | @Id | 屬性 | 標(biāo)注此屬性為主鍵 |
| 04 | @GeneratedValue | 屬性 | JPA通用主鍵策略生成器,此方式依賴具體數(shù)據(jù)庫(kù)(和@Id聯(lián)用) |
| 05 | @Column | 屬性 | 定義了映射到數(shù)據(jù)庫(kù)的列的所有屬性:列名,是否唯一,是否允許為空,是否允許更新等 |
| 06 | @Transient | 屬性 | 該屬性并不是一個(gè)到數(shù)據(jù)庫(kù)表的字段的映射,指定的這些屬性不會(huì)被持久化,ORM框架將忽略該屬性 |
| 07 | @Temporal | 屬性 | 當(dāng)為java.util中的日期/時(shí)間類型時(shí),通過(guò)它來(lái)指定格式 |
| 08 | @Enumerated | 屬性 | 標(biāo)注枚舉類型如何存庫(kù) |
| 09 | @TableGenerator | 類/屬性 | 定義一個(gè)主鍵值生成器,在Id這個(gè)元數(shù)據(jù)的generate=TABLE時(shí),generator屬性中可以使用生成器的名字 |
| 10 | @SequenceGenerator | 類/屬性 | 定義一個(gè)主鍵值生成器,在Id這個(gè)元數(shù)據(jù)的generator屬性中可以使用生成器的名字 |
| 11 | @SecondaryTable | 類 | 一個(gè)entity class可以映射到多表,SecondaryTable用來(lái)定義單個(gè)從表的名字,主鍵名字等屬性 |
| 12 | @UniqueConstraint | 注解元數(shù)據(jù) | 定義在Table或SecondaryTable元數(shù)據(jù)里,用來(lái)指定建表時(shí)需要建唯一約束的列 |
| 13 | @OneToOne | 類 | 一對(duì)一的關(guān)聯(lián)??膳渲米ト〔呗?、級(jí)聯(lián)操作等 |
| 14 | @ManyToOne | 類 | 多對(duì)一的映射,該注解標(biāo)注的屬性通常是數(shù)據(jù)庫(kù)表的外鍵 |
| 15 | @OneToMany | 類 | 一對(duì)多的關(guān)聯(lián),該屬性應(yīng)該為集體類型,在數(shù)據(jù)庫(kù)中并沒(méi)有實(shí)際字段 |
| 16 | @ManyToMany | 類 | 多對(duì)多的關(guān)聯(lián).多對(duì)多關(guān)聯(lián)上是兩個(gè)一對(duì)多關(guān)聯(lián),但是在ManyToMany描述中,中間表是由ORM框架自動(dòng)處理 |
| 17 | @JoinColumn | 屬性 | 如果在entity class的field上定義了關(guān)系(one2one或one2many等),通過(guò)JoinColumn來(lái)定義關(guān)系的屬性 |
| 18 | @IdClass | 類 | 當(dāng)entity class使用復(fù)合主鍵時(shí),需要定義一個(gè)類作為id class |
| 19 | @MapKey | 屬性 | 在一對(duì)多,多對(duì)多關(guān)系中,我們可以用Map來(lái)保存集合對(duì)象。默認(rèn)用主鍵值做key,如果使用復(fù)合主鍵,則用id class的實(shí)例做key,如果指定了name屬性,就用指定的field的值做key |
| 20 | @OrderBy | 屬性 | 在一對(duì)多,多對(duì)多關(guān)系中,有時(shí)希望從數(shù)據(jù)庫(kù)加載出來(lái)的集合對(duì)象是按一定方式排序的,這可以通過(guò)OrderBy來(lái)實(shí)現(xiàn),默認(rèn)是按對(duì)象的主鍵升序排列 |
| 21 | @Version | 屬性 | 實(shí)體類在樂(lè)觀事務(wù)中的version屬性 |
| 22 | @Lob | 屬性 | 指定一個(gè)屬性作為數(shù)據(jù)庫(kù)支持的大對(duì)象類型在數(shù)據(jù)庫(kù)中存儲(chǔ)。使用LobType這個(gè)枚舉來(lái)定義Lob是二進(jìn)制類型還是字符類型 |
| 23 | @DiscriminatorColumn | 屬性 | 定義在使用SINGLE_TABLE或JOINED繼承策略的表中區(qū)別不繼承層次的列 |
@Entity和@Table有何區(qū)別?答:@Entity表示這個(gè)class是實(shí)體類,并且使用 默認(rèn)的 orm規(guī)則,即class名即數(shù)據(jù)庫(kù)表中表名,class字段名即表中的字段名。若想自定義規(guī)則,就要使用@Table來(lái)改變包括表名、schema等,且輔助@Column來(lái)改變class中字段名與db中表的字段名的映射規(guī)則。很明顯,@Table + @Column組合方式更靈活和更常用。
典型示例:
- @Entity //聲明該類是和數(shù)據(jù)庫(kù)表映射的實(shí)體類
- @Table(name="t_user") //建立實(shí)體類與表的映射關(guān)系
- public class User implements Serializable {
- @Id //聲明當(dāng)前私有屬性為主鍵
- @GeneratedValue(strategy=GenerationType.IDENTITY) //配置主鍵的生成策略,為自增主鍵
- @Column(name = "user_id")
- private Long userId;
- @Column(name="user_name")
- private String userName;
- @Column(name="user_source")
- private String userSource;
- }
生存現(xiàn)狀
國(guó)內(nèi)不樂(lè)觀,海外堅(jiān)挺。
實(shí)現(xiàn)(框架)
雖說(shuō)ORM框架較多,但Hibernate的市占率獨(dú)步天下,幾乎能等同于JPA規(guī)范。畢竟制定JPA規(guī)范的人之前在Hibernate上班呢~
Hibernate 從3.2開始,開始兼容JPA。
| 版本 | 日期 | JPA版本 | info |
|---|---|---|---|
| 5.0 | 2015.08 | 2.1 | 兼容到JDK 6,提供hibernate-java8兼容到JDK 8 |
| 5.1 | 2016.02 | 2.1 | 小版本迭代逐步放棄6、7,最低要求8 |
| 5.2 | 2016.06 | 2.1 | 最低要求JDK 8 |
| 5.3 | 2018.05 | 2.2 | 最低要求JDK 8,全面使用maven管理器所有的模塊artifacts |
| 5.4 | 2018.12 | 2.2 | EntityGraph增強(qiáng)。最低要求8,支持11和17 |
| 5.5 | 2021.06 | 2.2/3.0 | 同上。額外通過(guò)hibernate-core-jakarta增加了對(duì)JPA 3.0規(guī)范的支持 |
| 5.6 | 開發(fā)中 | 2.2/3.0 | 開發(fā)中 |
| 6.0 | 開發(fā)中 | 2.2/3.0 | 開發(fā)中 |
值得注意的是,Hibernate采用模塊化管理,其中最重要的當(dāng)屬hibernate-core,還有hibernate-tools、hibernate-jcache、hibernate-hikaricp、hibernate-ehcache等等
代碼示例
在classpath下準(zhǔn)備一個(gè)hibernate.cfg.xml標(biāo)準(zhǔn)文件(亦可不使用xml文件,完全采用編程方式設(shè)置configuration):
- "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
- "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
com.mysql.cj.jdbc.Driver jdbc:mysql://localhost:3306/demo root root org.hibernate.dialect.MySQLDialect com.zaxxer.hikari.hibernate.HikariConnectionProvider true true validate
準(zhǔn)備一個(gè)entity(并在數(shù)據(jù)庫(kù)創(chuàng)建好表結(jié)構(gòu)):
- /**
- * 在此處添加備注信息
- *
- * @author YourBatman. Send email to me
- * @site https://yourbatman.cn
- * @date 2021/10/6 16:11
- * @since 0.0.1
- */
- @Data
- @Entity
- @Table(name = "user")
- public class User {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- @Column(name = "name")
- private String name;
- @Column(name = "age")
- private Integer age;
- }
啟動(dòng)Hibernate:
- /**
- * 在此處添加備注信息
- *
- * @author YourBatman. Send email to me
- * @site https://yourbatman.cn
- * @date 2021/10/6 16:06
- * @since 0.0.1
- */
- public class JPADemo {
- @Test
- public void fun1() {
- // 準(zhǔn)備Hibernate的Session
- Configuration configure = new Configuration().configure();
- SessionFactory sessionFactory = configure.buildSessionFactory();
- Session session = sessionFactory.openSession();
- Transaction transaction = session.beginTransaction();
- // 業(yè)務(wù)邏輯start
- User user = new User();
- user.setName("YourBatman");
- user.setAge(10);
- session.save(user);
- System.out.println("保存成功,id為:" + user.getId());
- // 業(yè)務(wù)邏輯end
- transaction.commit();
- session.close();
- sessionFactory.close();
- }
- }
大功告成。
在國(guó)內(nèi),即使使用JPA,大都是使用Spring Data JPA,可大大簡(jiǎn)化開發(fā)。當(dāng)然嘍,企業(yè)級(jí)項(xiàng)目使用MyBatis還是居多~
工程源代碼:https://github.com/yourbatman/FXP-java-ee
總結(jié)
本文拉齊了Java EE的兩項(xiàng)熱門技術(shù):Bean Validation和JPA,它倆的實(shí)現(xiàn)恰巧都是Hibernate,所以放在本篇一起毫無(wú)違和感。
本文標(biāo)題:【方向盤】版本歷史&代碼示例之:Bean Validation、JPA
文章來(lái)源:http://www.5511xx.com/article/dhgjocs.html


咨詢
建站咨詢
