SpringDataJpa官方文檔的翻譯(試讀,入門水平,請指正錯誤,不勝感激)

5.三、查詢方法

    這一節描述了使用Spring Data JPA建立查詢的各類方式。html

5.3.一、查詢的查找策略

    JPA支持經過一個字符串或者方法名來獲得一個查詢。
    使用如下謂詞:IsStartingWith、StartingWith、StartsWith,並以如下語句結尾的:EndingWith、EndsWith、IsNotContaining、NotContaining、NotContains、IsContaninig、Containing、Contains等組成的方法名和它們的參數能夠獲得一個查詢。這意味着若是參數包含了LIKE通配字符,將會轉換成文字。這些轉義字符能夠經過使用@EnableSpringRepositories註釋來設置escapeCharacter。完整文檔請看5.3.7節。java

聲明查詢

    從方法名裏獲得一個查詢是很方便的,可是可能會面臨兩個問題:關鍵字不支持解析或方法名很是難看。所以你還能夠經過命名約束來使用JPA的命名查詢(詳情請看5.3.3節),或在查詢方法上面使用@Query註釋(詳情請看5.3.4節)。算法

5.3.二、查詢的建立

    一般狀況下,查詢的建立算法是根據4.2文檔的說明來工做的,下面的例子展現了查詢方法會轉換成什麼:sql

例子5二、從方法名建立查詢數據庫

public interface UserRepository extends Repository<User, Long> {

  List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}

    咱們使用JPA的標準API建立了一個查詢,可是,基本上,這個查詢會轉換成這樣的sql語句:select u from User u where u.emailAddress = ?1 and u.lastname = ?2。Spring Data JPA不會遍歷檢查屬性,詳情請看4.4.3節。api

    下面的列表展現了JPA支持的關鍵字和方法會轉換成的內容:
表格三、方法名裏面支持的關鍵字數組

關鍵字 示例 JPQL片斷
And findByLastnameAndFirstname ... where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname ... where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals ... where x.firstname = ?1
Between findByStartDateBetween ... where x.startDate between ?1 and ?2
LessThan findByAgeLessThan ... where x.age < ?1
LessThanEqual findByAgeLessThanEqual ... where x.age <= ?1
GreaterThan findByAgeGreaterThan ... where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual ... where x.age >= ?1
After findByStartDateAfter ... where x.startDate > ?1
Before findByStartDateBefore ... where x.startDate < ?1
IsNull findByAgeIsNull ... where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull ... where x.age not null
Like findByFirstnameLike ... where x.firstname like ?1
NotLike findByFirstnameNotLike ... where x.firstname not like ?1
StartingWith findByFirstnameStartingWith ... where x.firstname like ?1 (參數會綁定到%後面)
EndingWith findByFirstnameEndingWith ... where x.firstname like ?1 (參數會綁定在%前面)
Containing findByFirstnameContaining ... where x.firstname like ?1 (參數會綁定在兩個%中間)
OrderBy findByAgeOrderByLastnameDesc ... where x.age = ?1 order by lastname desc
Not findByLastnameNot ... where x.lastname <> ?1
In findByAgeIn(Collection<Age> ages) ... where x.age in ?1
NotIn findByAgeNotIn(Connection<Age> ages) ... where x.age not in ?1
True findByActiveTrue() ... where x.active = true
Flase findByActiveFalse() ... where x.active = false
IgnoreCase findByFirstnameIgnoreCase ... where UPPER(x.firstname) = UPPER(?1)

    In和NotIn容許有一個Collection任何子類來作爲參數,能夠是數組或可變變量。對於相同邏輯的其餘的語法,可查看附錄C。安全

5.3.三、使用JPA命名查詢

    例子使用了<named-query />元素和@NamedQuery註釋。這些查詢配置元素在JPA查詢語言裏面定義了。固然,你還可使用<named-native-query />和@NamedNativeQuery。這些元素和註釋可讓你使用特定數據庫的本地查詢語句。oracle

XML命名查詢的定義
略。。。app

基於註釋的配置
    基於註釋的配置能夠不用去配置其餘的配置文件,這是一個優點,能夠下降維護成本。可是沒聲明一個新的查詢,都須要從新編譯domain類。

例子5四、基於註釋的查詢配置

@Entity
@NamedQuery(name = "User.findByEmailAddress",
  query = "select u from User u where u.emailAddress = ?1")
public class User {

}

聲明接口
    要讓命名查詢能夠執行,須要給UserRepository指定以下接口:

例子5五、在UserRepository裏聲明查詢方法

public interface UserRepository extends JpaRepository<User, Long> {

  List<User> findByLastname(String lastname);

  User findByEmailAddress(String emailAddress);
}

    Spring Data JPA在調用這個方法的時候,會嘗試解析方法名,從domain類的簡單名開始,接着是名字後面的.點分隔符。所以上面的例子會嘗試從方法名裏來建立一個命名查詢。

5.3.四、使用Query

    使用命名查詢來進行查詢entities是一個有效的方法,而且對少許的查詢都是能工做的很好。對於查詢他們是綁定到了java方法上面執行的。你能夠直接使用Spring Data JPA的@Query註釋來直接綁定它們,而不是將它們註釋到domain類。這個domain類是從特定的持久化信息裏面釋放的,並將查詢定位到repository接口。     使用@NamedQuery經過查詢定義來註釋一個上面的查詢方法,或者在orm.xml裏面定義一個查詢。

    下面的例子展現了一個使用@Query註釋建立的查詢

例子5六、在查詢方法上面使用@Query來聲明一個查詢

public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.emailAddress = ?1")
  User findByEmailAddress(String emailAddress);
}

使用高級的LIKE表達式

    查詢執行算法在使用@Query手動建立查詢的時候容許在查詢裏面使用高級的LIKE進行定義,以下例子所示:

例子5七、@Query裏面的like表達式

public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.firstname like %?1")
  List<User> findByFirstnameEndsWith(String firstname);
}

    上面的例子裏,like分隔符(%)會被分辨出來的,而且查詢會轉換成有效的JPQL查詢(刪除了%)。上面方法的執行,傳遞給方法的參數會匹配like模式。

本地查詢

    @Query註釋能夠經過將nativeQuery設置爲true,來進行本地查詢語句的執行,以下例子所示:

public interface UserRepository extends JpaRepository<User, Long> {

  @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
  User findByEmailAddress(String emailAddress);
}

    Spring Data JPA目前不支持本地查詢裏面的動態排序,由於它必須處理實際聲明的查詢,對於本地的SQL是不可靠的。可是你能夠經過在本地查詢裏面指定查詢的數量來實現頁的查詢,以下例子所示:

例子5九、經過在查詢方法上面使用@Query註釋聲明一個本地的頁碼總數的查詢

public interface UserRepository extends JpaRepository<User, Long> {

  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

    一個相似的方法是使用命名本地查詢,經過添加.count到你的查詢後面。你可能須要給你的count query註冊一個map結果。

5.3.三、使用Sort

    排序能夠經過提供一個PageRequest或直接用Sort。Sort實例中實際使用的屬性須要跟domain模型匹配,這意味着他們須要解析成一個屬性或者用於query裏面的別名。JPQL將這定義成一個狀態字段路徑表達式。

    使用任何不能引用的路徑表達式會拋出異常。

    然而,和@Query一塊兒使用Sort,可讓你潛入進包含方法到Order By字句的非檢查的路徑Order的實例。這是可能的,由於Orderis追加到查詢字符串後面的。默認狀況下,Spring Data JPA拒絕包含方法調用的任何Order的實例,可是你可使用JpaSort.unsafe來添加潛在不安全的排序。
下面的例子使用了Sort和JpaSort,JpaSort上面包含了一個不安全的選項:
例子60、使用Sort和JpaSort

public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.lastname like ?1%")
  List<User> findByAndSort(String lastname, Sort sort);

  @Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%")
  List<Object[]> findByAsArrayAndSort(String lastname, Sort sort);
}

repo.findByAndSort("lannister", new Sort("firstname"));               (1)
repo.findByAndSort("stark", new Sort("LENGTH(firstname)"));           (2)
repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)")); (3)
repo.findByAsArrayAndSort("bolton", new Sort("fn_len"));              (4)

(1)有效的Sort,表達式指向domain模型的屬性
(2)無效的包含方法調用的Sort,拋出異常
(3)有效的Sort,明確的包含不安全的order
(4)有效的Sort,表達式指向方法別名

5.3.六、使用命名參數

    如前面的例子所講,默認狀況下,Spring Data JPA使用基於位置的參數綁定。當重構有關參數位置時,會讓查詢方法容易出錯。要解決這個問題,可使用@Param註釋個體方法的參數一個具體的名字,而且綁定名字到查詢上面,以下例子所示:
例子6一、使用命名參數

public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
  User findByLastnameOrFirstname(@Param("lastname") String lastname,
                                 @Param("firstname") String firstname);
}

    方法參數在定義的查詢裏面,位置已經交換了

    對於版本4,Spring徹底支持java8的使用編譯器的--parameters參數來發現參數名。在構建中使用這個參數能夠做爲調試信息的替代,你能夠在命名參數上面省略@Param註釋。 ###5.3.七、使用SpEL表達式     對於Sprng Data JPA的1.4版本,咱們支持使用@Query手動定義的查詢裏面對受限制的SpEl模板表達式的使用。上面查詢的執行,這些表達式的評價依據是變量的集合。Spring Data JPA支持名爲entityName的變量。它的用法是select x from #{#entityName} x。它會插入給定的repository的doamin類型相關的entityName。entityName按以下方式解析:若是的domain的屬性上面設置了@Entity註釋,則使用它。不然,使用domain類型的簡單的類名。

    下面的例子演示了一個查詢字符串裏#{#entityName}的用例,使用查詢方法和手動定義查詢,來定義一個repository接口:
例子6二、在repository查詢方法裏使用SpEL表達式-entityName

@Entity
public class User {

  @Id
  @GeneratedValue
  Long id;

  String lastname;
}

public interface UserRepository extends JpaRepository<User,Long> {

  @Query("select u from #{#entityName} u where u.lastname = ?1")
  List<User> findByLastname(String lastname);
}

    爲了不陳述使用了@Query註釋的查詢字符串裏面的真實的entity名稱,你可使用#{#entityName}變量。

    entityName能夠經過使用@Entity註釋進行自定義。在orm.xml裏面自定義的不支持SpEL表達式。

    固然你能夠在查詢聲明裏面直接使用User,可是它須要你來修改查詢。對#entityName的引用能夠在之後將潛在的User類映射到不一樣的entity名稱上面(如:@Entity(name = "MyUser"))。

    查詢字符串裏面的#{#entityName}的其餘用法是,若是你想給指定的domain類型建立指定的repository接口的普通的repository,不要重複在指定接口上面定義自定的方法,你能夠在普通的repository接口上面裏面經過在查詢字符串上面使用@Query註釋來使用entityName表達式,以下例子所示:
例子6三、在repository的查詢方法裏面使用SpEL表達式-entityName的繼承

@MappedSuperclass
public abstract class AbstractMappedType {
  …
  String attribute
}

@Entity
public class ConcreteType extends AbstractMappedType { … }

@NoRepositoryBean
public interface MappedTypeRepository<T extends AbstractMappedType>
  extends Repository<T, Long> {

  @Query("select t from #{#entityName} t where t.attribute = ?1")
  List<T> findAllByAttribute(String attribute);
}

public interface ConcreteRepository
  extends MappedTypeRepository<ConcreteType> { … }

    上面的例子裏,MappendTypeRepository是少許的幾個繼承了AbStractMappendType的通用的父接口。它還定義了findAllByAttribute(...)方法,它能夠被用到指定repository接口的實例上面,若是你如今在ConcreteRepository上面執行findAllByAttribute(...)方法,則執行下面的查詢:select t from ConcreteType t where t.attribute = ?1

    SpEL表達式去操做參數還能夠被用來操做方法的參數。在這個SpEL表達式裏entity名是不可用的,可是參數是可用的,能夠經過名稱或索引來訪問,以下例子所示: 例子6四、在repository查詢裏面使用SpEL表達式-訪問參數

@Query("select u from User u where u.firstname = ?1 and u.firstname=?#{[0]} and u.emailAddress = ?#{principal.emailAddress}")
List<User> findByFirstnameAndCurrentUserWithCustomQuery(String firstname);

    若是想提供LIKE條件,能夠在值的前面或後面加上&。能夠在參數或SpEL表達式的前面或後面添加%。以下例子所示: *例子6五、在repository查詢裏面使用SpEL表達式-通配符的縮寫

@Query("select u from User u where u.lastname like %:#{[0]}% and u.lastname like %:lastname%")
List<User> findByLastnameWithSpelExpression(@Param("lastname") String lastname);

    當使用一個帶值的LIKE條件的時候,是從一個不安全的源碼裏面獲得的,所以須要去除隱患,它們不能包含任何的通配符以及任何能夠攻擊數據庫的字符,爲了解決這個問題,可使用SpEL表達式裏面的escape(String)方法,它會在第一個單字符的參數和第二個參數開始給前面加上_%前綴結合LIKE表達式的escape方法能夠用在JPQL和標準的SQL,能夠很方便的處理參數裏面的特殊字符。 例子6六、在repository查詢裏面使用SpEL表達式-處理輸入值的攻擊隱患

@Query("select u from User u where u.firstname like %?#{escape([0])}% escape ?#{escapeCharacter()}")
List<User> findContainingEscaped(String namePart);

    在repository接口裏聲明一個方法findContainingEscaped("Peter_"),能夠查找到Peter_Paker,可是查找不到Peter Paker,escape字符能夠經過使用@EnableJpaRepositories註釋來設置escapeCharacter。注意escape(String)方法用在SpEL上下文裏,只能做用在JPQL和SQL標註的通配符_%上面。若是底層的數據庫或JPA的實現支付額外的通配符,這些通配符都不會被escape處理。

5.3.八、修改數據的查詢

    所有上一節的說明,是如何來聲明一個查詢來訪問entity或entity的集合。你可使用4.6節提到的內容來修改查詢的行爲。全部的這些方法對於所有的自定義方法都是可行的,你能夠在在查詢上面使用@Modify註釋只修改參數,以下例子所示: 例子6七、定義操做查詢

@Modifying
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);

    調用這個方法會更新指定的值。對於一個EntityManager在執行完修改查詢後可能會包含未修改的entities,咱們不會自動去清理(詳情請看EntityManager.clear()文檔),由於這會有效的刪除EntityManager裏面等待的未刷新的修改,若是你但願EntityManager自動清理,能夠設置@Modifying註釋的屬性clearAutomaticallytrue。     @Modifying註釋只有結合@Query註釋使用纔會生效。派生的或自定義的查詢不須要這個註釋。

派生一個刪除查詢

    Spring Data JPA還支持派生刪除查詢,可讓你避免明確的聲明一個JPQL查詢,以下例子所示: 例子6八、使用派生的刪除查詢

interface UserRepository extends Repository<User, Long> {

  void deleteByRoleId(long roleId);

  @Modifying
  @Query("delete from User u where user.role.id = ?1")
  void deleteInBulkByRoleId(long roleId);
}

    儘管deleteByRoleId(...)方法看起來向deleteInBulkRoleId(...)方法,這兩個方法在執行方式的聲明上有不一樣點。顧名思義,後面的方法針對數據庫發出一個JPQL查詢(定義在註釋裏的查詢)。這意味着即便當前已經加載的User也看不到執行後的聲明週期的回調。

    要保證生命週期會真正的執行,deleteByRoleId(...)調用會執行一個查詢,而且一個一個的刪除返回的實例,所以它的持久化提供者能夠在entities上面真正的執行@PreRemove回調。

    事實上,派生的刪除查詢是一個快捷方式來執行查詢而且在結果上調用CrudRepository.delete(Iterale<User>)而且保持行爲和CrudRepository裏面實現的其餘delete(...)方法一直。

5.3.9應用查詢的提示

    應用JPA查詢提示到你repository接口的查詢聲明裏,你可使用@QueryHints註釋。當處理分頁時,它帶着一個JPA的@QueryHint註釋數組加上默認爲false的boolean標誌應用到額外的總數查詢的觸發,以下例子所示:
*例子6九、在repository方法上面使用QueryHints

public interface UserRepository extends Repository<User, Long> {

  @QueryHints(value = { @QueryHint(name = "name", value = "value")},
              forCounting = false)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

    上面的聲明會給實際的查詢應用一個配置@QueryHints但,可是不能把它應用到一個觸發計算頁面總數的數量查詢上面。

5.3.十、

相關文章
相關標籤/搜索