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.十、

相关文章
相关标签/搜索