转载自:http://blog.csdn.net/zhang89xiao/article/details/61918717
实体状态监测策略
SpringData JPA提供三种策略去监测实体是否存在:
属性名 | 属性值 |
---|---|
Id-Property inspection (default) | 默认的会通过ID来监测是否新数据,如果ID属性是空的,则认为是新数据,反则认为旧数据 |
Implementing Persistable | 如果实体实现了Persistable接口,那么就会通过isNew的方法来监测。 |
Implementing EntityInformation | 这个是很少用的 |
@Query
来解决。 查询创建
通常我们可以使用方法名来解析查询语句,例如:
其所支持的在方法名中可以使用的关键字:
关键字 | 例子 | 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 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection age) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
In
和NotIn
也可以用Collection
的子类.
注解方式
声明接口
要使用上面的命名查询,我们的接口需要这么声明
SpringData会先从域类中查询配置,根据”.(原点)“区分方法名,而不会使用自动方法名解析的方式去创建查询。
@Query
在表达式中使用Like查询,例子如下:
这个例子中,我们使用了%,当然,你的参数就没必要加入这个符号了。
@Query
中使用本地查询,当然,你需要设置nativeQuery=true
,必须说明的是,这样的话,就不再支持分页以及排序。 使用命名参数
使用命名查询,我们需要用到@Param来注释到指定的参数上,如下:
@Query
中使用SpEL表达式
(简介)来接收变量。 变量名 | 使用方式 | 描述 |
---|---|---|
entityName | select x from #{#entityName} x | 根据给定的Repository自动插入相关的entityName。有两种方式能被解析出来:如果域类型定义了@Entity属性名称。或者直接使用类名称。 |
以下的例子中,我们在查询语句中插入表达式(你也可以用@Entity(name = “MyUser”)。
如果你想写一个通用的Repository接口,那么你也可以用这个表达式来处理:
@Modifying
: 这样一来,我们就使用了update操作来代替select操作。当我们发起update操作后,可能会有一些过期的数据产生,我们不需要自动去清除它们,因为EntityManager会有效的丢掉那些未提
交的变更,如果你想EntityManager自动清除,那么你可以在@Modify上添加clearAutomatically属性(true);
使用QueryHints
(查询提示 QueryHits简介)
配置获取和负载图(loadgraph) @NamedEntityGraph
简介
JPA2.1 支持 通过@EntityGraph
及其子类型@NamedEntityGraph
来定义获取和负载.它们可以被直接在实体类上,用来配置查询结果的获取计划.获取的方式(获取/负载)可以通过@EntityGraph
的type
属性来进行配置.
在一个实体类上定义 named entity graph
在repository接口中引用在实体类上定义的named entity graph
它也可以通过@EntityGraph
注解来直接点对点的指定entity graphs
.假如依照EntityGraph
attributePaths
可以被正确的找到,就可以不用在实体类上写@NamedEntityGraph
注解了:
Person
的几个属性:
id
是主键fristName
和lastName
是数据属性.address
链接到其他的domain类型. Spring Data将会返回domain类的所有属性.现在有两种选择去仅仅返回address
属性:
Address
定义一个repository: 在这种情况下 用PersonRepository
将会返回整个Person
对象.使用AddressRepository
仅仅会返回Address
对象.
但是,如果你真的不想暴露address的信息怎么办?你可以提供一个像下边这样的repository,仅仅提供你想暴露的属性:
其中
interface NoAddresses
定义一个接口String getFirstName();
导出firstName
String getLastName();
导出lastName
这个NoAddress
只有firstName
和lastName
的getter方法.它意味着它将不会提供任何的address信息.在定义查询方法的时候,应该用NoAddress
代替Person
其中:
interface RenamedProperty
定义一个接口.String getFirstName();
导出firstName
属性.@Value("#{target.lastName}") String getName();
导出name
属性,由于此属性是虚拟的,因此他需要用`@Value("#{target.lastName}")
来指定数据的来源. 如果你想获得一个人的全称.你可能要用String.format("%s %s", person.getFirstName(), person.getLastName())
来拼接.用虚拟的属性可以这样来实现:
实际上@Value
可以完全访问对象及其内嵌的属性.SpEL
表达式对于施加在投影方法上的定义来说也是非常强大的:
想想你有一个如下的domain模型:
在某些情况下,你想让密码在可能的情况下不让其明文出现.这种情况下你可以用@Value
和SpEL表达式
来创建一个投影:
这个表达式判断当password是null或者empty的时候返回null,否则返回’******’
存储过程
JPA2.1 规格介绍了JPA标准查询API对存储过程调用的支持.下面介绍下@Procedure
注解的使用.
存储过程的元数据可以在实体类上通过@NamedStoredProcedureQuery进行配置.
在repository 方法上可以通过多种方式引用存储过程.存储过程可以直接通过@Procedure
注解的value
或者procedureName
属性调用或者通过name
属性.如果repository方法没有名字,则将其作为后备.
JPA2 引入了criteria API 去建立查询,Spring Data JPA使用Specifications来实现这个API。在Repository中,你需要继承JpaSpecificationExecutor:
下面先给个例子,演示如何利用findAll方法返回所有符合条件的对象:
Specification 接口定义如下:
好了,那么我们如何去实现这个接口呢?代码如下:
好了,那如果有多个需要结合的话,我们可以这么做:
Query by Example
使用
Query by Example由三部分组成.
Probe
这是填充字段的domain对象的实际范例.ExampleMatcher
ExampleMatcher
描述怎么去匹配特定字段的细节.他可以通过多个Examples进行重复利用.Example
它由ExampleMatcher
和Probe
组成.用来创建一个查询. Query by Example使用与多种情况,但是它也有一些限制.
适用于:
AND
关键字进行条件查询的时候.firstname = ?0 or (firstname = ?1 and lastname = ?2)
仅仅支持starts/contains/ends/regex匹配strings或者精确匹配其他属性类型.
在使用Query by Example之前,你需要有一个domain对象:
这是一个简单的domain对象.你可以使用它去创建一个Example
默认情况下,字段为null
值时会被忽略.
Examples
通过使用of
工厂方法或者ExampleMatcher
来创建.Example
是不可改变的.
其中:
Person person = new Person();
创建一个domain对象实例person.setFirstname("Dave");
设置firstName属性去查询Example<Person> example = Example.of(person);
创建Example
QueryByExampleExecutor<T>
. Example matchers
Example不仅仅局限于默认的设置.你可以给strings定义自己的默认值然后去匹配.使用ExampleMatcher
绑定null和特定属性的设置.
其中:
Person person = new Person();
创建一个domain对象实例.ExampleMatcher matcher = ExampleMatcher.matching()
创建一个ExampleMatcher
让其可以使用,但没有多余的配置项..withIgnorePaths("lastname")
Construct a new ExampleMatcher to ignore the property path lastname..withIncludeNullValues()
Construct a new ExampleMatcher to ignore the property path lastname and to include null values..withStringMatcherEnding();
Construct a new ExampleMatcher to ignore the property path lastname, to include null values, and use perform suffix string matching.Example<Person> example = Example.of(person, matcher);
根据domain对象和配置的ExampleMatcher
对象来创建一个Example
你可以给个别的属性指定行为.(比如.firstname
和lastname
以及domain对象的嵌套属性address.city
)
你可以调整他让他匹配大小写敏感的选项.
另一种配置matcher选项的方式是通过使用Java 8 lambdas表达式.这种方式是一种可以通过询问实现者修改matcher的回调方法.你不需要去返回matcher,因为他已经保留了matcher的实例.(引用)
ExampleMatcher
可以设置的范围
设置 | 范围 |
---|---|
Null-handling | ExampleMatcher |
String matching | ExampleMatcher and property path |
Ignoring properties | Property path |
Case sensitivity | ExampleMatcher and property path |
Value transformation | Property path |
Property path指 这个设置项要跟在需要设置的属性后边,而不是在ExampleMatcher对象上进行设置.
对比ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("firstname", endsWith()).withMatcher("lastname", startsWith().ignoreCase());
和ExampleMatcher matcher = ExampleMatcher.matching().withIgnorePaths("lastname").withIncludeNullValues().withStringMatcherEnding();
执行一个Example
仅仅只有SingularAttribute
(单数属性)可以被属性匹配正确使用.
StringMatcher
选项:
匹配 | 逻辑结果 |
---|---|
DEFAULT (case-sensitive) | firstname = ?0 |
DEFAULT (case-insensitive) | LOWER(firstname) = LOWER(?0) |
EXACT (case-sensitive) | firstname = ?0 |
STARTING (case-sensitive) | firstname like ?0 + ‘%’ |
STARTING (case-insensitive) | LOWER(firstname) like LOWER(?0) + ‘%’ |
ENDING (case-sensitive) | firstname like ‘%’ + ?0 |
ENDING (case-insensitive) | LOWER(firstname) like ‘%’ + LOWER(?0) |
CONTAINING (case-sensitive) | firstname like ‘%’ + ?0 + ‘%’ |
CONTAINING (case-insensitive) | LOWER(firstname) like ‘%’ + LOWER(?0) + ‘%’ |
这样会让findAll方法在10秒内执行否则会超时的非只读事务中。
另一种修改事务行为的方式在于使用门面或者服务层中,他们包含了多个repository。
这将会导致在调用addRoleToAllUsers方法的时候,创建一个或者加入一个事务中去。实际在Repository里面定义的事务将会被忽略,而外部定义的事务将会被应用。当然,要使用事务,你需要声明(这个例子中,假设你已经使用了component-scan)
要让方法在事务中,最简单的方式就是使用@Transactional注解:
一般的查询操作,你需要设置readOnly=true。在deleteInactiveUsers方法中,我们添加了Modifying注解以及覆盖了Transactional,这样这个方法执行的时候readOnly=false了.
@Lock
: 当然你也可以覆盖原有的方法:
基础知识
SpringData为您跟踪谁创建或者修改数据,以及相应的时间提供了复杂的支持。你现在想要这些支持的话,仅仅需要使用几个注解或者实现接口即可。
注解方式: 我们提供了@CreatedBy
, @LastModifiedBy
去捕获谁操作的实体,当然还有相应的时间@CreatedDate
和@LastModifiedDate
。
正如你看到的,你可以选择性的使用这些注解。操作时间方面,你可以使用org.joda.time.DateTime 或者Java.util.Date或者long/Long表示。
基于接口的审计:
如果你不想用注解来做审计的话,那么你可以实现Auditable
接口。他暴露了审计属性的get/set方法。
如果你不想实现接口,那么你可以继承AbstractAuditable,通常来说,注解方式是更加方便的。
在这个实现类中,我们使用SpringSecurity内置的Authentication来查找用户的UserDetails。
spring-aspects.jar
你也可以再每个实体类上使用@EntityListeners
注解来激活AuditingEntityListener
监听.
要启用这个审计,我们还需要在配置文件里面配置多一条:
Spring Data JPA 1.5之后,你也可以使用@EnableJpaAuditing
注解来激活.
其他: 略. 点击查看
附录
附录A: 命名空间引用
<repositories />
的元素:
属性名称 | 描述 |
---|---|
base-package | 定义去扫描哪些继承了*Repository接口的用户自定义Repository接口. |
repository-impl-postfix | 定义用户自定义实现sql语句的实现类以什么结尾,以用来自动发现.默认是Impl |
query-lookup-strategy | 定义查询的策略.默认的是create-if-not-found |
named-queries-location | 定义去哪里寻找已经写好了named-query查询的配置文件 |
consider-nested-repositories | 考虑是否要控制内嵌的Repository接口.默认是false |
附录B:Populators 命名空间引用
<populator />
的元素:
属性名称 | 描述 |
---|---|
locations | 寻找要填入Repository接口的对象的值的文件. |
附录C:Repository 查询关键词
支持的查询关键字:
Logical keyword | Keyword expressions |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
附录D:Repository查询的返回结果类型
支持的查询返回结果类型:
Return type | Description |
---|---|
|
Denotes no return value. |
Primitives |
Java primitives. |
Wrapper types |
Java wrapper types. |
|
An unique entity. Expects the query method to return one result at most. In case no result is found |
|
An |
|
A |
|
A |
|
A Java 8 or Guava |
|
A Java 8 |
|
A |
|
A Java 8 |
|
A |
|
A sized chunk of data with information whether there is more data available. Requires a |
|
A |
|
A result entry with additional information, e.g. distance to a reference location. |
|
A list of |
|
A |