13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 1/19
被 孙 瑞鸿添加,被 孙 瑞鸿最后更新于十二月 17, 2013
页面 / 产品研发部Java架构组 / 技术预研
Spring Data JPA中文文档[1.4.3]
前言
作者 & 译者
第一部分:文档
1.使用 Spring Data Repositories
1.1核心概念
1.2查询方法
1.2.1 声明Repository接口
1.2.2 定义查询方法
构建查询
属性表达式
特殊参数处理
1.2.3 创建Repository实体
XML配置
使用过滤器
JavaConfig
独立使用
1.3 自定义Repository实现
1.3.1 在repository中添加自定义方法
配置
人工装载
1.3.2 为所有的repository添加自定义方法
1.4 Spring Data扩展
1.4.1 Web支持
基本的web支持
DomainClassConverter
HandlerMethodArgumentResolver分页排序
超媒体分页
1.4.2 Repository填充
1.4.3 Legacy Web Support
在SpringMVC中绑定领域类(Domain class)
属性编辑器
转换服务
Web分页
表格1.2 请求参数
配置通用的默认参数
2.JPA Repositories
2.1介绍
2.1.1Spring命名空间
自定义命名空间属性
2.1.2 基于注解的配置
2.2 持久实体
2.2.1 保存实体
实体状态监测策略
表格2.2 监测方式
2.3 查询方法
2.3.1 查询策略
声明查询语句
2.3.2 查询创建器
表格2.3 支持的关键字
2.3.3 使用JPA命名查询
XML命名查询定义
注解方式
声明接口
2.3.4 使用@Query
LIKE查询
原生查询
2.3.5 使用命名参数
2.3.6 使用SpELl表达式
表格2.4 在SpELl中支持的变量
2.3.7 修改语句
2.3.8 使用QueryHints
2.4 Specifications
2.5 事务
2.5.1 事务性查询方法
2.6 锁
2.7 审计
2.7.1 基础知识
注解方式
基于接口的审计
审计织入
2.7.2 通用审计配置
前言
反正也没人看,省略吧!
本文档对应的是Spring Data JPA 1.4.3 RELEASE
作者 & 译者
作者:Oliver Gierke, Thomas Darimont
译者:大熊 QQ:30485398813-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 2/19
Copyright © 2008-2013
由于本人利用闲暇时间翻译,再加上本人水平有限,翻译可能过于粗糙,未能翻译出Spring Data JPA原文档的意思,请各位谅解,如果有什么问题,可以联系本人!
本翻译文档仍未做任何校对(PS:这是翻译第一版,先出炉),请大家多多包含!
最后,请大家尊重本人的劳动成果,本译文可用户私人或者拷贝予他人免费使用,但不允许用于任何商业用途。
第一部分:文档
1.使用 Spring Data Repositories
Spring Data Repository的存在,是为了把你从大量重复、繁杂的数据库层操作中解放出来。
1.1核心概念
Spring Data Repository的核心接口是Repository(好像也没什么好惊讶的)。这个接口需要领域类(Domain Class)跟领域类的ID类型作为参数。这个接口主要是让你能知道继承这个类的接
口的类型。CrudRepository提供了对被管理的实体类的一些常用CRUD方法。
例1.1 CrudRepository接口
① 保存给定的实体。
②返回指定ID的实体。
③返回全部实体。
④返回实体的总数。
⑤删除指定的实体。
⑥判断给定的ID是否存在。
通常我们要扩展功能的方法,那么我们就需要在接口上做子接口。那么我们要添加功能的时候,就在CrudRepository的基础上去增加。
P a g i n g A n d S o r t i n g R e p o s i t o r y 是一个继承CrudRepository的接口,他扩展了分页与排序的功能。
例1.2 PagingAndSortingRepository
如果我们需要查询第二页的用户数据(每页包含20条数据),那么我们可以简单的这么做:
用户分页查询
1.2查询方法
一般的增删改查功能都会有一些查询语句去查询数据库,在Spring Data,你只需要简单的做四个步骤即可实现!
1.声明一个继承与Repository或者它的子接口的接口,并且输入类型参数,如下:
1
2
3
4
5
6
7
8
9
1 0
1 1
1 2
1 3
1 4
1 5
1 6
1 7
p u b l i c i n t e r f a c e C r u d R e p o s i t o r y < T , I D e x t e n d s S e r i a l i z a b l e >
e x t e n d s R e p o s i t o r y < T , I D > {
< S e x t e n d s T > S s a v e ( S e n t i t y ) ; ①
T f i n d O n e ( I D p r i m a r y K e y ) ; ②
I t e r a b l e < T > f i n d A l l ( ) ; ③
L o n g c o u n t ( ) ; ④
v o i d d e l e t e ( T e n t i t y ) ; ⑤
b o o l e a n e x i s t s ( I D p r i m a r y K e y ) ; ⑥
/ / … 省略其他方法
}
1
2
3
4
5
6
7
p u b l i c i n t e r f a c e P a g i n g A n d S o r t i n g R e p o s i t o r y < T , I D e x t e n d s S e r i a l i z a b l e >
e x t e n d s C r u d R e p o s i t o r y < T , I D > {
I t e r a b l e < T > f i n d A l l ( S o r t s o r t ) ;
P a g e < T > f i n d A l l ( P a g e a b l e p a g e a b l e ) ;
}
1
2
P a g i n g A n d S o r t i n g R e p o s i t o r y < U s e r , L o n g > r e p o s i t o r y = / / … g e t a c c e s s t o a b e a n
P a g e < U s e r > u s e r s = r e p o s i t o r y . f i n d A l l ( n e w P a g e R e q u e s t ( 1 , 2 0 ) ) ;13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 3/19
声明接口
2.声明查询的方法在接口上
声明方法
你没有看错,你只要声明,不需要些实现!SpringData会创建代理对象帮你完成那些繁琐的事情。
3.在Spring上配置
Spring配置
注意,上面的命名空间使用了JPA的命名空间
4.在业务中使用
调用数据操作
这部分的代码将在下部分中解释。
1.2.1 声明Repository接口
在上面的第一步操作中定义了接口,这些接口必须都继承与Repository或者其子类,并且标注领域类(Domain Class)以及ID类型。如果你想暴露CRUD方法,那么你可以直接继承
CrudRepository接口。
通常,我们的Repository会继承R e p o s i t o r y , C r u d R e p o s i t o r y 或者P a g i n g A n d S o r t i n g R e p o s i t o r y 中的一个。但是你如果不想用S p r i n g D a t a 的接口的话,你也可以把自己的接口声明
@ R e p o s i t o r y 即可。继承C r u d R e p o s i t o r y 接口可以让你暴露出很多方法去操作你的实体类。如果你仅仅想暴露几个接口给其他人使用,那么你只需要从C r u d R e p o s i t o r y 中拷贝几
个需要的方法到自己的R e p o s i t o r y 中。
例1.3 选择性的暴露接口
在这里我们只暴露出findOne(...)跟save(...)两个方法出来。对于UserRepository,他除了有根据ID查询的方法、保存实体的方法之外,还有根据Email地址查询用户的方法。
p u b l i c i n t e r f a c e P e r s o n R e p o s i t o r y e x t e n d s R e p o s i t o r y < U s e r , L o n g > { … }
L i s t < P e r s o n > f i n d B y L a s t n a m e ( S t r i n g l a s t n a m e ) ;
< ? x m l v e r s i o n = " 1 . 0 " e n c o d i n g = " U T F - 8 " ? >
< b e a n s : b e a n s x m l n s : b e a n s = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s "
x m l n s : x s i = " h t t p : / / w w w . w 3 . o r g / 2 0 0 1 / X M L S c h e m a - i n s t a n c e "
x m l n s = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / j p a "
x s i : s c h e m a L o c a t i o n = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s / s p r i n g - b e a n s . x s d
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / j p a
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / j p a / s p r i n g - j p a . x s d " >
< r e p o s i t o r i e s b a s e - p a c k a g e = " c o m . a c m e . r e p o s i t o r i e s " / >
< / b e a n s >
p u b l i c c l a s s S o m e C l i e n t {
@ A u t o w i r e d
p r i v a t e P e r s o n R e p o s i t o r y r e p o s i t o r y ;
p u b l i c v o i d d o S o m e t h i n g ( ) {
L i s t < P e r s o n > p e r s o n s = r e p o s i t o r y . f i n d B y L a s t n a m e ( " M a t t h e w s " ) ;
}
}
1
2
3
4
5
6
7
8
9
i n t e r f a c e M y B a s e R e p o s i t o r y < T , I D e x t e n d s S e r i a l i z a b l e > e x t e n d s R e p o s i t o r y < T , I D > {
T f i n d O n e ( I D i d ) ;
T s a v e ( T e n t i t y ) ;
}
i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s M y B a s e R e p o s i t o r y < U s e r , L o n g > {
U s e r f i n d B y E m a i l A d d r e s s ( E m a i l A d d r e s s e m a i l A d d r e s s ) ;
}13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 4/19
1.2.2 定义查询方法
SpringData通过方法名有两种方式去解析出用户的查询意图:一种是直接通过方法的命名规则去解析,第二种是通过Query去解析,那么当同时存在几种方式时,SpringData怎么去选择这两
种方式呢?好了,SpringData有一个策略去决定到底使用哪种方式:
查询策略:
接下来我们将介绍策略的信息,你可以通过配置
CREATE
通过解析方法名字来创建查询。这个策略是删除方法中固定的前缀,然后再来解析其余的部分。
USE_DECLARED_QUERY
它会根据已经定义好的语句去查询,如果找不到,则会抛出异常信息。这个语句可以在某个注解或者方法上定义。根据给定的规范来查找可用选项,如果在方法被调用时没有找到定义的查
询,那么会抛出异常。
CREATE_IF_NOT_FOUND(默认)
这个策略结合了以上两个策略。他会优先查询是否有定义好的查询语句,如果没有,就根据方法的名字去构建查询。这是一个默认策略,如果不特别指定其他策略,那么这个策略会在项目
中沿用。
构建查询
查询构造器是内置在SpringData中的,他是非常强大的,这个构造器会从方法名中剔除掉类似find...By, read...By, 或者get...By的前缀,然后开始解析其余的名字。你可以在方法名中加入更
多的表达式,例如你需要Distinct的约束,那么你可以在方法名中加入Distinct即可。在方法中,第一个By表示着查询语句的开始,你也可以用And或者Or来关联多个条件。
例1.4 通过方法名字构建查询
根据方法名解析的查询结果跟数据库是相关,但是,还有几个问题需要注意:
多个属性的查询可以通过连接操作来完成,例如And,Or。当然还有其他的,例如Between,LessThan,GreaterThan,Like。这些操作时跟数据库相关的,当然你还需要看看相关的
数据库文档是否支持这些操作。
你可以使用IngoreCase来忽略被标记的属性的大小写,也可以使用AllIgnoreCase来忽略全部的属性,当然这个也是需要数据库支持才允许的。
你可以使用OrderBy来进行排序查询,排序的方向是Asc跟Desc,如果需要动态排序,请看后面的章节。
属性表达式
好了,将了那么多了,具体的方法名解析查询需要怎样的规则呢?这种方法名查询只能用在被管理的实体类上,就好像之前的案例。假设一个类Person中有个Address,并且Address还有
ZipCode,那么根据ZipCode来查询这个Person需要怎么做呢?
在上面的例子中,我们用x.address.zipCode去检索属性,这种解析算法会在方法名中先找出实体属性的完整部分(AddressZipCode),检查这部分是不是实体类的属性,如果解析成功,则按
照驼峰式从右到左去解析属性,如:AddressZipCode将分为AddressZip跟Code,在这个时候,我们的属性解析不出Code属性,则会在此用同样的方式切割,分为Address跟ZipCode(如果
第一次分割不能匹配,解析器会向左移动分割点),并继续解析。
为了避免这种解析的问题,你可以用“_”去区分,如下所示:
特殊参数处理
上面的例子已经展示了绑定简单的参数,那么除此之外,我们还可以绑定一些指定的参数,如Pageable和Sort来动态的添加分页、排序查询。
在查询方法中使用分页和排序
1
2
3
4
5
6
7
8
9
1 0
1 1
1 2
1 3
1 4
1 5
1 6
1 7
p u b l i c i n t e r f a c e P e r s o n R e p o s i t o r y e x t e n d s R e p o s i t o r y < U s e r , L o n g > {
L i s t < P e r s o n > f i n d B y E m a i l A d d r e s s A n d L a s t n a m e ( E m a i l A d d r e s s e m a i l A d d r e s s , S t r i n g l a s t n a m e ) ;
/ / 需要在语句中使用D i s t i n c t 关键字,你需要做的是如下
L i s t < P e r s o n > f i n d D i s t i n c t P e o p l e B y L a s t n a m e O r F i r s t n a m e ( S t r i n g l a s t n a m e , S t r i n g f i r s t n a m e ) ;
L i s t < P e r s o n > f i n d P e o p l e D i s t i n c t B y L a s t n a m e O r F i r s t n a m e ( S t r i n g l a s t n a m e , S t r i n g f i r s t n a m e ) ;
/ / 如果你需要忽略大小写,那么你要用I g n o r e C a s e 关键字,你需要做的是如下
L i s t < P e r s o n > f i n d B y L a s t n a m e I g n o r e C a s e ( S t r i n g l a s t n a m e ) ;
/ / 所有属性都忽略大小写呢?A l l I g n o r e C a s e 可以帮到您
L i s t < P e r s o n > f i n d B y L a s t n a m e A n d F i r s t n a m e A l l I g n o r e C a s e ( S t r i n g l a s t n a m e , S t r i n g f i r s t n a m e ) ;
/ / 同样的,如果需要排序的话,那你需要:O r d e r B y
L i s t < P e r s o n > f i n d B y L a s t n a m e O r d e r B y F i r s t n a m e A s c ( S t r i n g l a s t n a m e ) ;
L i s t < P e r s o n > f i n d B y L a s t n a m e O r d e r B y F i r s t n a m e D e s c ( S t r i n g l a s t n a m e ) ;
}
L i s t < P e r s o n > f i n d B y A d d r e s s Z i p C o d e ( Z i p C o d e z i p C o d e ) ;
L i s t < P e r s o n > f i n d B y A d d r e s s _ Z i p C o d e ( Z i p C o d e z i p C o d e ) ;
1
2
P a g e < U s e r > f i n d B y L a s t n a m e ( S t r i n g l a s t n a m e , P a g e a b l e p a g e a b l e ) ;
13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 5/19
第一个方法通过传递org.springframework.data.domain.Pageable来实现分页功能,排序也绑定在里面。如果需要排序功能,那么需要添加参数org.springframework.data.domain.Sort,如第
二行中,返回的对象可以是List,当然也可以是Page类型的。
1.2.3 创建Repository实体
创建已定义的Repository接口,最简单的方式就是使用Spring配置文件,当然,需要JPA的命名空间。
XML配置
你可以使用JPA命名空间里面的repositories去自动检索路径下的repositories元素:
XML配置
在本例中,Spring能够通过base-package检测出指定路径下所有继承Repository或者其子接口的接口(有点绕口)。每找到一个接口的时候,FactoryBean就会创建一个合适的代理去处理以及
调用里面的查询方法。每个注册的Bean的名称都是源于接口名称,例如:UserRepository将会被注册为userRepository。base-package允许使用通配符作为扫描格式。
使用过滤器
在默认的设置中,将使用全路径扫描的方式去检索接口,当然,你在业务上可能需要更细致的操作,这时候,你可以在
你可以指定扫描的路径包含或者不包含指定的路径。
例如我们现在想过滤掉一些指定的接口,那么你可以这么做:
例1.6 使用排除过滤
这个例子中,我们排除了所有以SomeRepository结尾的接口。
JavaConfig
你可以在JavaConfig中使用@Enable${store}Repositories注解来实现。那么代码就是如下:
例1.7 使用JavaConfig
独立使用
你可以不在Spring容器里面使用repository。但是你还需要Spring的依赖包在你的classpath中,你需要使用RepositoryFactory来实现,代码如下:
例1.8 独立模式下使用
1.3 自定义Repository实现
我们可以自己实现repository的方法。
3
4
5
L i s t < U s e r > f i n d B y L a s t n a m e ( S t r i n g l a s t n a m e , S o r t s o r t ) ;
L i s t < U s e r > f i n d B y L a s t n a m e ( S t r i n g l a s t n a m e , P a g e a b l e p a g e a b l e ) ;
< ? x m l v e r s i o n = " 1 . 0 " e n c o d i n g = " U T F - 8 " ? >
< b e a n s : b e a n s x m l n s : b e a n s = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s "
x m l n s : x s i = " h t t p : / / w w w . w 3 . o r g / 2 0 0 1 / X M L S c h e m a - i n s t a n c e "
x m l n s = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / j p a "
x s i : s c h e m a L o c a t i o n = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s / s p r i n g - b e a n s . x s d
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / j p a
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / j p a / s p r i n g - j p a . x s d " >
< r e p o s i t o r i e s b a s e - p a c k a g e = " c o m . a c m e . r e p o s i t o r i e s " / >
< / b e a n s : b e a n s >
< r e p o s i t o r i e s b a s e - p a c k a g e = " c o m . a c m e . r e p o s i t o r i e s " >
< c o n t e x t : e x c l u d e - f i l t e r t y p e = " r e g e x " e x p r e s s i o n = " . * S o m e R e p o s i t o r y " / >
< / r e p o s i t o r i e s >
1
2
3
4
5
6
7
8
9
@ C o n f i g u r a t i o n
@ E n a b l e J p a R e p o s i t o r i e s ( " c o m . a c m e . r e p o s i t o r i e s " )
c l a s s A p p l i c a t i o n C o n f i g u r a t i o n {
@ B e a n
p u b l i c E n t i t y M a n a g e r F a c t o r y e n t i t y M a n a g e r F a c t o r y ( ) {
/ / …
}
}
1
2
R e p o s i t o r y F a c t o r y S u p p o r t f a c t o r y = … / / 初始化
U s e r R e p o s i t o r y r e p o s i t o r y = f a c t o r y . g e t R e p o s i t o r y ( U s e r R e p o s i t o r y . c l a s s ) ;13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 6/19
1.3.1 在repository中添加自定义方法
为了丰富我们的接口我们通常会自定义自己的接口以及对应的实现类。
例1.9 自定义接口
自定义接口的实现类
扩展CRUDRepository
这样的话,就能够在常用的Repository中实现自己的方法。
配置
在XML的配置里面,框架会自动搜索base-package里面的实现类,这些实现类的后缀必须满足repository-impl-postfix中指定的命名规则,默认的规则是:Impl
例1.12 配置实例
第一个配置我们将找到com.acme.repository.UserRepositoryImpl,而第二个配置我们将找到com.acme.repository.UserRepositoryFooBar。
人工装载
前面的代码中,我们使用了注释以及配置去自动装载。如果你自己定义的实现类需要特殊的装载,那么你可以跟普通bean一样声明出来就可以了,框架会手工的装载起来,而不是创建本
身。
例1.13 人工装载实现类(I)
1.3.2 为所有的repository添加自定义方法
假如你要为所有的repository添加一个方法,那么前面的方法都不可行。你可以这样做:
1. 你需要先声明一个中间接口,然后让你的接口来继承这个中间接口而不是Repository接口,代码如下:
例1.14 中间接口
2. 这时候,我们需要创建我们的实现类,这个实现类是基于Repository中的基类的,这个类会作为Repository代理的自定义类来执行。
例1.15 自定义基类
1
2
3
4
i n t e r f a c e U s e r R e p o s i t o r y C u s t o m {
p u b l i c v o i d s o m e C u s t o m M e t h o d ( U s e r u s e r ) ;
}
1
2
3
4
5
6
c l a s s U s e r R e p o s i t o r y I m p l i m p l e m e n t s U s e r R e p o s i t o r y C u s t o m {
p u b l i c v o i d s o m e C u s t o m M e t h o d ( U s e r u s e r ) {
/ / 实现
}
}
1
2
3
4
p u b l i c i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s C r u d R e p o s i t o r y < U s e r , L o n g > , U s e r R e p o s i t o r y C u s t o m {
/ / 声明查询方法
}
< r e p o s i t o r i e s b a s e - p a c k a g e = " c o m . a c m e . r e p o s i t o r y " / >
< r e p o s i t o r i e s b a s e - p a c k a g e = " c o m . a c m e . r e p o s i t o r y " r e p o s i t o r y - i m p l - p o s t f i x = " F o o B a r " / >
< r e p o s i t o r i e s b a s e - p a c k a g e = " c o m . a c m e . r e p o s i t o r y " / >
< b e a n s : b e a n i d = " u s e r R e p o s i t o r y I m p l " c l a s s = " … " >
< ! - - 其他配置 - - >
< / b e a n s : b e a n >
1
2
3
4
5
p u b l i c i n t e r f a c e M y R e p o s i t o r y < T , I D e x t e n d s S e r i a l i z a b l e >
e x t e n d s J p a R e p o s i t o r y < T , I D > {
v o i d s h a r e d C u s t o m M e t h o d ( I D i d ) ;
}
1
2
3
4
5
6
p u b l i c c l a s s M y R e p o s i t o r y I m p l < T , I D e x t e n d s S e r i a l i z a b l e >
e x t e n d s S i m p l e J p a R e p o s i t o r y < T , I D > i m p l e m e n t s M y R e p o s i t o r y < T , I D > {
p r i v a t e E n t i t y M a n a g e r e n t i t y M a n a g e r ;
/ / 可以选择两个构造函数中的一个13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 7/19
3. 我们需要创建一个自定义的FactoryBean去替代默认的工厂类。代码如下:
例1.16 自定义工厂类
4. 最后,在XML中配置factory-class即可:
例1.17 配置
1.4 Spring Data扩展
这部分我们将会把SpringData扩展到其他框架中,目前我们继承的目标是SpringMVC。
1.4.1 Web支持
SpringData支持很多web功能。当然你的应用也要有SpringMVC的Jar包,有的还需要继承Spring HATEOAS。
通常来说,你可以在你的JavaConfig配置类中加入@EnableSpringDataWebSupport即可:
例1.18 启用web支持
这个注解注册了几个功能,我们稍后会说,他也能检测Spring HATEOAS,并且注册他们。
如果你用XML配置的话,那么你可以用下面的配置:
例1.19 在XML中配置
7
8
9
1 0
1 1
1 2
1 3
1 4
1 5
1 6
1 7
p u b l i c M y R e p o s i t o r y I m p l ( C l a s s < T > d o m a i n C l a s s , E n t i t y M a n a g e r e n t i t y M a n a g e r ) {
s u p e r ( d o m a i n C l a s s , e n t i t y M a n a g e r ) ;
/ / T h i s i s t h e r e c o m m e n d e d m e t h o d f o r a c c e s s i n g i n h e r i t e d c l a s s d e p e n d e n c i e s .
t h i s . e n t i t y M a n a g e r = e n t i t y M a n a g e r ;
}
p u b l i c v o i d s h a r e d C u s t o m M e t h o d ( I D i d ) {
/ / i m p l e m e n t a t i o n g o e s h e r e
}
}
1
2
3
4
5
6
7
8
9
1 0
1 1
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
2 0
2 1
2 2
2 3
2 4
2 5
2 6
2 7
2 8
2 9
3 0
3 1
p u b l i c c l a s s M y R e p o s i t o r y F a c t o r y B e a n < R e x t e n d s J p a R e p o s i t o r y < T , I > , T , I e x t e n d s S e r i a l i z a b l e >
e x t e n d s J p a R e p o s i t o r y F a c t o r y B e a n < R , T , I > {
p r o t e c t e d R e p o s i t o r y F a c t o r y S u p p o r t c r e a t e R e p o s i t o r y F a c t o r y ( E n t i t y M a n a g e r e n t i t y M a n a g e r ) {
r e t u r n n e w M y R e p o s i t o r y F a c t o r y ( e n t i t y M a n a g e r ) ;
}
p r i v a t e s t a t i c c l a s s M y R e p o s i t o r y F a c t o r y < T , I e x t e n d s S e r i a l i z a b l e > e x t e n d s J p a R e p o s i t o r y F a c t o r y {
p r i v a t e E n t i t y M a n a g e r e n t i t y M a n a g e r ;
p u b l i c M y R e p o s i t o r y F a c t o r y ( E n t i t y M a n a g e r e n t i t y M a n a g e r ) {
s u p e r ( e n t i t y M a n a g e r ) ;
t h i s . e n t i t y M a n a g e r = e n t i t y M a n a g e r ;
}
p r o t e c t e d O b j e c t g e t T a r g e t R e p o s i t o r y ( R e p o s i t o r y M e t a d a t a m e t a d a t a ) {
r e t u r n n e w M y R e p o s i t o r y I m p l < T , I > ( ( C l a s s < T > ) m e t a d a t a . g e t D o m a i n C l a s s ( ) , e n t i t y M a n a g e r ) ;
}
p r o t e c t e d C l a s s < ? > g e t R e p o s i t o r y B a s e C l a s s ( R e p o s i t o r y M e t a d a t a m e t a d a t a ) {
/ / T h e R e p o s i t o r y M e t a d a t a c a n b e s a f e l y i g n o r e d , i t i s u s e d b y t h e J p a R e p o s i t o r y F a c t o r y
/ / t o c h e c k f o r Q u e r y D s l J p a R e p o s i t o r y ' s w h i c h i s o u t o f s c o p e .
r e t u r n M y R e p o s i t o r y . c l a s s ;
}
}
}
< r e p o s i t o r i e s b a s e - p a c k a g e = " c o m . a c m e . r e p o s i t o r y "
f a c t o r y - c l a s s = " c o m . a c m e . M y R e p o s i t o r y F a c t o r y B e a n " / >
@ C o n f i g u r a t i o n
@ E n a b l e W e b M v c
@ E n a b l e S p r i n g D a t a W e b S u p p o r t
c l a s s W e b C o n f i g u r a t i o n { }
< b e a n c l a s s = " o r g . s p r i n g f r a m e w o r k . d a t a . w e b . c o n f i g . S p r i n g D a t a W e b C o n f i g u r a t i o n " / >13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 8/19
基本的web支持
上面的配置注册了一下的几个功能:
D o m a i n C l a s s C o n v e r t e r 将会让S p r i n g M V C 能从请求参数或者路径参数中解析出来。
H a n d l e r M e t h o d A r g u m e n t R e s o l v e r 能让S p r i n g M V C 从请求参数中解析出P a g e a b l e ( 分页) 与S o r t ( 排序) 。
D o m a i n C l a s s C o n v e r t e r
这个类允许你在S p r i n g M V C 控制层的方法中直接使用你的领域类型( D o m a i n t y p e s ) ,如下:
例1.20 使用领域类型
正如你所见,上面的方法直接接收了一个User对象,你不需要做任何的搜索操作,这个转换器自动的设id的值进去对象中,并且最终调用了findOne方法查询出实体。(注:当前的Repository
必须实现CrudRepository)
H a n d l e r M e t h o d A r g u m e n t R e s o l v e r 分页排序
这个配置项同时注册了PageableH a n d l e r M e t h o d A r g u m e n t R e s o l v e r 和 SortH a n d l e r M e t h o d A r g u m e n t R e s o l v e r ,使得P a g e a b l e 跟S o r t 能作为控制层的参数使用:
使用分页作为控制层参数
这个配置会让SpringMVC传递一个Pageable实体参数,下面是默认的参数:
page 你要获取的页数
size 一页中最大的数据量
sort 需要被排序的属性(格式:属性1,属性2(, ASC | DESC)),默认是asc,使用多个字段排序,你可以使用sort=first&sort=last,asc
如果你需要对多个表写多个分页或排序,那么你需要用@Qualifier来区分,请求参数的前缀是${qualifire}_,那么你的方法可能变成这样:
多个分页
你需要填写foo_page和bar_page等。
默认的Pageable相当于new PageRequest(0,20),你可以用@PageableDefaults注解来放在Pageable上。
超媒体分页
Spring HATEOAS有一个PagedResources类,他丰富了Page实体以及一些让用户更容易导航到资源的请求方式。Page转换到PagedResources是由一个实现了Spring HATEOAS
ResourceAssembler接口的实现类:PagedResourcesAssembler提供转换的。
使用PagedResourcesAssembler作为参数
< ! - - I f y o u ' r e u s i n g S p r i n g H A T E O A S a s w e l l r e g i s t e r t h i s o n e * i n s t e a d * o f t h e f o r m e r - - >
< b e a n c l a s s = " o r g . s p r i n g f r a m e w o r k . d a t a . w e b . c o n f i g . H a t e o a s A w a r e S p r i n g D a t a W e b C o n f i g u r a t i o n " / >
1
2
3
4
5
6
7
8
9
1 0
1 1
@ C o n t r o l l e r
@ R e q u e s t M a p p i n g ( " / u s e r s " )
p u b l i c c l a s s U s e r C o n t r o l l e r {
@ R e q u e s t M a p p i n g ( " / { i d } " )
p u b l i c S t r i n g s h o w U s e r F o r m ( @ P a t h V a r i a b l e ( " i d " ) U s e r u s e r , M o d e l m o d e l ) {
m o d e l . a d d A t t r i b u t e ( " u s e r " , u s e r ) ;
r e t u r n " u s e r F o r m " ;
}
}
1
2
3
4
5
6
7
8
9
1 0
1 1
1 2
1 3
@ C o n t r o l l e r
@ R e q u e s t M a p p i n g ( " / u s e r s " )
p u b l i c c l a s s U s e r C o n t r o l l e r {
@ A u t o w i r e d U s e r R e p o s i t o r y r e p o s i t o r y ;
@ R e q u e s t M a p p i n g
p u b l i c S t r i n g s h o w U s e r s ( M o d e l m o d e l , P a g e a b l e p a g e a b l e ) {
m o d e l . a d d A t t r i b u t e ( " u s e r s " , r e p o s i t o r y . f i n d A l l ( p a g e a b l e ) ) ;
r e t u r n " u s e r s " ;
}
}
p u b l i c S t r i n g s h o w U s e r s ( M o d e l m o d e l ,
@ Q u a l i f i e r ( " f o o " ) P a g e a b l e f i r s t ,
@ Q u a l i f i e r ( " b a r " ) P a g e a b l e s e c o n d ) { … }
1
2
3
4
5
6
@ C o n t r o l l e r
c l a s s P e r s o n C o n t r o l l e r {
@ A u t o w i r e d P e r s o n R e p o s i t o r y r e p o s i t o r y ;
@ R e q u e s t M a p p i n g ( v a l u e = " / p e r s o n s " , m e t h o d = R e q u e s t M e t h o d . G E T ) 13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 9/19
上面的toResources方法会执行以下的几个步骤:
Page对象的内容会转换成为PagedResources对象。
PagedResources会的到一个PageMetadata的实体附加,包含Page跟PageRequest。
PagedResources会根据状态得到prev跟next链接,这些链接指向URI所匹配的方法中。分页参数会根据P a g e a b l e H a n d l e r M e t h o d A r g u m e n t R e s o l v e r 配置,以让其在后面的方法中
解析使用。
假设我们现在有一个3 0 个人的信息在数据库中,你现在可以触发一个G E T 请求h t t p : / / l o c a l h o s t : 8 0 8 0 / p e r s o n s 然后你就会得到类似于如下的数据:
1 . 4 . 2 R e p o s i t o r y 填充
如果你用过S p r i n g J D B C ,那么你肯定很熟悉使用S Q L 去填写数据源( D a t a S o u r c e ) ,在这里,我们可以使用X M L 或者J s o n 去填写数据,而不再使用S Q L 填充。
假如你有一个d a t a . j s o n 的文件,如下:
Json数据
要P e r s o n R e p o s i t o r y 填充这些数据进去,你需要做如下的声明:
例1.24 声明jackson repository填充
这个声明使得d a t a . j s o n 能够通过J a c k s o n O b j e c t M a p p e r 被其他地方读取,反序列化。
如果你要用X M L 的话,你需要使用u n m a r s h a l l e r - p o p u l a t o r ,你可以使用S p r i n g O X M 提供的组件:
使用JAXB
7
8
9
1 0
1 1
1 2
1 3
H t t p E n t i t y < P a g e d R e s o u r c e s < P e r s o n > > p e r s o n s ( P a g e a b l e p a g e a b l e ,
P a g e d R e s o u r c e s A s s e m b l e r a s s e m b l e r ) {
P a g e < P e r s o n > p e r s o n s = r e p o s i t o r y . f i n d A l l ( p a g e a b l e ) ;
r e t u r n n e w R e s p o n s e E n t i t y < > ( a s s e m b l e r . t o R e s o u r c e s ( p e r s o n s ) , H t t p S t a t u s . O K ) ;
}
}
{ " l i n k s " : [ { " r e l " : " n e x t " ,
" h r e f " : " h t t p : / / l o c a l h o s t : 8 0 8 0 / p e r s o n s ? p a g e = 1 & s i z e = 2 0 }
] ,
" c o n t e n t " : [
… / / 2 0 P e r s o n i n s t a n c e s r e n d e r e d h e r e
] ,
" p a g e M e t a d a t a " : {
" s i z e " : 2 0 ,
" t o t a l E l e m e n t s " : 3 0 ,
" t o t a l P a g e s " : 2 ,
" n u m b e r " : 0
}
}
[ { " _ c l a s s " : " c o m . a c m e . P e r s o n " ,
" f i r s t n a m e " : " D a v e " ,
" l a s t n a m e " : " M a t t h e w s " } ,
{ " _ c l a s s " : " c o m . a c m e . P e r s o n " ,
" f i r s t n a m e " : " C a r t e r " ,
" l a s t n a m e " : " B e a u f o r d " } ]
< ? x m l v e r s i o n = " 1 . 0 " e n c o d i n g = " U T F - 8 " ? >
< b e a n s x m l n s = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s "
x m l n s : x s i = " h t t p : / / w w w . w 3 . o r g / 2 0 0 1 / X M L S c h e m a - i n s t a n c e "
x m l n s : r e p o s i t o r y = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / r e p o s i t o r y "
x s i : s c h e m a L o c a t i o n = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s / s p r i n g - b e a n s . x s d
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / r e p o s i t o r y
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / r e p o s i t o r y / s p r i n g - r e p o s i t o r y . x s d " >
< r e p o s i t o r y : j a c k s o n - p o p u l a t o r l o c a t i o n = " c l a s s p a t h : d a t a . j s o n " / >
< / b e a n s >
< ? x m l v e r s i o n = " 1 . 0 " e n c o d i n g = " U T F - 8 " ? >
< b e a n s x m l n s = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s "
x m l n s : x s i = " h t t p : / / w w w . w 3 . o r g / 2 0 0 1 / X M L S c h e m a - i n s t a n c e "
x m l n s : r e p o s i t o r y = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / r e p o s i t o r y "
x m l n s : o x m = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / o x m "
x s i : s c h e m a L o c a t i o n = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s / s p r i n g - b e a n s . x s d
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / r e p o s i t o r y
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / r e p o s i t o r y / s p r i n g - r e p o s i t o r y . x s d
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / o x m
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / o x m / s p r i n g - o x m . x s d " >
13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 10/19
1 . 4 . 3 L e g a c y W e b S u p p o r t
在S p r i n g M V C 中绑定领域类( D o m a i n c l a s s )
你在开发w e b 项目的时候,你经常需要从U R L 或者请求参数中解析领域类中的I D ,你可能是这么做得:
首先你要注入一个UserRepository ,然后通过findOne查询出结果。幸运的是,Spring提供了自定义组件允许你从String类型到任意类型的转换。
属性编辑器
在Spring3.0之前,Java的PropertyEditor已经被使用。现在我们要集成它,SpringData提供了一个DomainClassPropertyEditorRegistrar类,他能在ApplicationContext中查找SpringData的
Repositories,并且注册自定义的PropertyEditor。
如果你做了上面的工作,那么你在前面的例子中,会大大减少工作量:
转换服务
在Spring3以后,PropertyEditor已经被转换服务取代了,SpringData现在用DomainClassConverter模仿
D o m a i n C l a s s P r o p e r t y E d i t o r R e g i s t r a r 中的实现。你可以使用如下的配置:
< r e p o s i t o r y : u n m a r s h a l l e r - p o p u l a t o r l o c a t i o n = " c l a s s p a t h : d a t a . j s o n " u n m a r s h a l l e r - r e f = " u n m a r s h a l l e r " / >
< o x m : j a x b 2 - m a r s h a l l e r c o n t e x t P a t h = " c o m . a c m e " / >
< / b e a n s >
1
2
3
4
5
6
7
8
9
1 0
1 1
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
2 0
2 1
2 2
2 3
@ C o n t r o l l e r
@ R e q u e s t M a p p i n g ( " / u s e r s " )
p u b l i c c l a s s U s e r C o n t r o l l e r {
p r i v a t e f i n a l U s e r R e p o s i t o r y u s e r R e p o s i t o r y ;
@ A u t o w i r e d
p u b l i c U s e r C o n t r o l l e r ( U s e r R e p o s i t o r y u s e r R e p o s i t o r y ) {
A s s e r t . n o t N u l l ( r e p o s i t o r y , " R e p o s i t o r y m u s t n o t b e n u l l ! " ) ;
u s e r R e p o s i t o r y = u s e r R e p o s i t o r y ;
}
@ R e q u e s t M a p p i n g ( " / { i d } " )
p u b l i c S t r i n g s h o w U s e r F o r m ( @ P a t h V a r i a b l e ( " i d " ) L o n g i d , M o d e l m o d e l ) {
/ / D o n u l l c h e c k f o r i d
U s e r u s e r = u s e r R e p o s i t o r y . f i n d O n e ( i d ) ;
/ / D o n u l l c h e c k f o r u s e r
m o d e l . a d d A t t r i b u t e ( " u s e r " , u s e r ) ;
r e t u r n " u s e r " ;
}
}
< b e a n c l a s s = " … . w e b . s e r v l e t . m v c . a n n o t a t i o n . A n n o t a t i o n M e t h o d H a n d l e r A d a p t e r " >
< p r o p e r t y n a m e = " w e b B i n d i n g I n i t i a l i z e r " >
< b e a n c l a s s = " … . w e b . b i n d . s u p p o r t . C o n f i g u r a b l e W e b B i n d i n g I n i t i a l i z e r " >
< p r o p e r t y n a m e = " p r o p e r t y E d i t o r R e g i s t r a r s " >
< b e a n c l a s s = " o r g . s p r i n g f r a m e w o r k . d a t a . r e p o s i t o r y . s u p p o r t . D o m a i n C l a s s P r o p e r t y E d i t o r R e g i s t r a r " / >
< / p r o p e r t y >
< / b e a n >
< / p r o p e r t y >
< / b e a n >
@ C o n t r o l l e r
@ R e q u e s t M a p p i n g ( " / u s e r s " )
p u b l i c c l a s s U s e r C o n t r o l l e r {
@ R e q u e s t M a p p i n g ( " / { i d } " )
p u b l i c S t r i n g s h o w U s e r F o r m ( @ P a t h V a r i a b l e ( " i d " ) U s e r u s e r , M o d e l m o d e l ) {
m o d e l . a d d A t t r i b u t e ( " u s e r " , u s e r ) ;
r e t u r n " u s e r F o r m " ;
}
}
< m v c : a n n o t a t i o n - d r i v e n c o n v e r s i o n - s e r v i c e = " c o n v e r s i o n S e r v i c e " / >
< b e a n c l a s s = " o r g . s p r i n g f r a m e w o r k . d a t a . r e p o s i t o r y . s u p p o r t . D o m a i n C l a s s C o n v e r t e r " >
< c o n s t r u c t o r - a r g r e f = " c o n v e r s i o n S e r v i c e " / >13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 11/19
如果你是用JavaConfig,你可以集成SpringMVC的WebMvcConfigurationSupport并且处理FormatingConversionService,那么你可以这么做:
Web分页
当你在写页面分页的时候,你需要填写大量的重复的代码区完成这个功能,可是现在你只需要传递一个HttpServletRequest就可以完成了,下面的例子省略了让你程序更加茁壮的异常处理代
码:
现在我觉得上面的配置还是麻烦了,那么SpringData使用PageableHandlerArgumentResolver帮你简化你的工作,你可以在JavaConfig中做如下配置:
在XML配置中:
那你现在的工作可以简化成为:
P a g e a b l e H a n d l e r A r g u m e n t R e s o l v e r 会自动解析请求参数中的字段:
表格1.2 请求参数
< / b e a n >
1
2
3
4
5
6
7
8
9
c l a s s W e b C o n f i g u r a t i o n e x t e n d s W e b M v c C o n f i g u r a t i o n S u p p o r t {
/ / 省略其他配置
@ B e a n
p u b l i c D o m a i n C l a s s C o n v e r t e r < ? > d o m a i n C l a s s C o n v e r t e r ( ) {
r e t u r n n e w D o m a i n C l a s s C o n v e r t e r < F o r m a t t i n g C o n v e r s i o n S e r v i c e > ( m v c C o n v e r s i o n S e r v i c e ( ) ) ;
}
}
@ C o n t r o l l e r
@ R e q u e s t M a p p i n g ( " / u s e r s " )
p u b l i c c l a s s U s e r C o n t r o l l e r {
/ / D I c o d e o m i t t e d
@ R e q u e s t M a p p i n g
p u b l i c S t r i n g s h o w U s e r s ( M o d e l m o d e l , H t t p S e r v l e t R e q u e s t r e q u e s t ) {
i n t p a g e = I n t e g e r . p a r s e I n t ( r e q u e s t . g e t P a r a m e t e r ( " p a g e " ) ) ;
i n t p a g e S i z e = I n t e g e r . p a r s e I n t ( r e q u e s t . g e t P a r a m e t e r ( " p a g e S i z e " ) ) ;
P a g e a b l e p a g e a b l e = n e w P a g e R e q u e s t ( p a g e , p a g e S i z e ) ;
m o d e l . a d d A t t r i b u t e ( " u s e r s " , u s e r S e r v i c e . g e t U s e r s ( p a g e a b l e ) ) ;
r e t u r n " u s e r s " ;
}
}
@ C o n f i g u r a t i o n
p u b l i c c l a s s W e b C o n f i g e x t e n d s W e b M v c C o n f i g u r a t i o n S u p p o r t {
@ O v e r r i d e
p u b l i c v o i d c o n f i g u r e M e s s a g e C o n v e r t e r s ( L i s t < H t t p M e s s a g e C o n v e r t e r < ? > > c o n v e r t e r s ) {
c o n v e r t e r s . a d d ( n e w P a g e a b l e H a n d l e r A r g u m e n t R e s o l v e r ( ) ) ;
}
}
< b e a n c l a s s = " … . w e b . s e r v l e t . m v c . m e t h o d . a n n o t a t i o n . R e q u e s t M a p p i n g H a n d l e r A d a p t e r " >
< p r o p e r t y n a m e = " c u s t o m A r g u m e n t R e s o l v e r s " >
< l i s t >
< b e a n c l a s s = " o r g . s p r i n g f r a m e w o r k . d a t a . w e b . P a g e a b l e H a n d l e r A r g u m e n t R e s o l v e r " / >
< / l i s t >
< / p r o p e r t y >
< / b e a n >
1
2
3
4
5
6
7
8
9
1 0
1 1
@ C o n t r o l l e r
@ R e q u e s t M a p p i n g ( " / u s e r s " )
p u b l i c c l a s s U s e r C o n t r o l l e r {
@ R e q u e s t M a p p i n g
p u b l i c S t r i n g s h o w U s e r s ( M o d e l m o d e l , P a g e a b l e p a g e a b l e ) {
m o d e l . a d d A t t r i b u t e ( " u s e r s " , u s e r R e p o s i t o r y . f i n d A l l ( p a g e a b l e ) ) ;
r e t u r n " u s e r s " ;
}
}13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 12/19
page 你要获取的页数
page.size 每页最大数据量
page.sort 排序
page.sort.dir 排序的方向
通用的,多个分页参数的话,你可以这么做:
配置通用的默认参数
P a g e a b l e A r g u m e n t R e s o l v e r 默认会请求第一页,1 0 条数据。但是我们可能需要更多的数据,那么我们可以使用@ P a g e a b l e D e f a u l t s 来设置页码以及数据量,排序,以及排序方向:
2.JPA Repositories
本章节包含了JPA Repository的具体实现。
2.1介绍
2.1.1Spring命名空间
SpringData使用了自定义的命名空间去定义repository。通常我们会使用repositories元素:
例2.1 设置命名空间
这个配置中启用了持久化异常处理,所有标志了@Repository的Bean将会被转换成为Spring的DataAccessException。
自定义命名空间属性
除了repositories,JPA命名空间还提供了其他的属性去控制:
表格2.1 JPA的其他属性
entity-manager-factory-
ref
默认的话,是使用ApplicationContext中找到的EntityManagerFactory,如果有多个的时候,则需要特别指明这个属性,他将会对repositories路径中找到的类
进行处理。
transaction-manager-ref 默认使用系统定义的PlatformTransactionManager,如果有多个事务管理器的话,则需特别指定。
2.1.2 基于注解的配置
SpringData JPA支持JavaConfig方式的配置:
例2.2 JavaConfig方式配置
p u b l i c S t r i n g s h o w U s e r s ( M o d e l m o d e l ,
@ Q u a l i f i e r ( " f o o " ) P a g e a b l e f i r s t ,
@ Q u a l i f i e r ( " b a r " ) P a g e a b l e s e c o n d ) { … }
p u b l i c S t r i n g s h o w U s e r s ( M o d e l m o d e l ,
@ P a g e a b l e D e f a u l t s ( p a g e N u m b e r = 0 , v a l u e = 3 0 ) P a g e a b l e p a g e a b l e ) { … }
< ? x m l v e r s i o n = " 1 . 0 " e n c o d i n g = " U T F - 8 " ? >
< b e a n s x m l n s = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s "
x m l n s : x s i = " h t t p : / / w w w . w 3 . o r g / 2 0 0 1 / X M L S c h e m a - i n s t a n c e "
x m l n s : j p a = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / j p a "
x s i : s c h e m a L o c a t i o n = " h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / b e a n s / s p r i n g - b e a n s . x s d
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / j p a
h t t p : / / w w w . s p r i n g f r a m e w o r k . o r g / s c h e m a / d a t a / j p a / s p r i n g - j p a . x s d " >
< j p a : r e p o s i t o r i e s b a s e - p a c k a g e = " c o m . a c m e . r e p o s i t o r i e s " / >
< / b e a n s >
1
2
3
4
5
6
7
8
9
1 0
1 1
1 2
1 3
@ C o n f i g u r a t i o n
@ E n a b l e J p a R e p o s i t o r i e s
@ E n a b l e T r a n s a c t i o n M a n a g e m e n t
c l a s s A p p l i c a t i o n C o n f i g {
@ B e a n
p u b l i c D a t a S o u r c e d a t a S o u r c e ( ) {
E m b e d d e d D a t a b a s e B u i l d e r b u i l d e r = n e w E m b e d d e d D a t a b a s e B u i l d e r ( ) ;
r e t u r n b u i l d e r . s e t T y p e ( E m b e d d e d D a t a b a s e T y p e . H S Q L ) . b u i l d ( ) ;
}
@ B e a n13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 13/19
上面的配置中,我们设置了一个内嵌的HSQL数据库,我们也配置了EntityManagerFactory,并且使用Hibernate作为持久层。最后也定义了JPATransactionManager。最上面我们还使用了
@EnableJpaRepositories注解。
2.2 持久实体
2.2.1 保存实体
保存实体,我们之前使用了CrudRepository.save(...)方法。他会使用相关的JPA EntityManager来调用persist或者merge,如果数据没存在于数据库中,则调用entityManager.persist(..),否
则调用entityManager.merge(...)。
实体状态监测策略
SpringData JPA提供三种策略去监测实体是否存在:
表格2.2 监测方式
ID属性检测(默认) 默认的会通过ID来监测是否新数据,如果ID属性是空的,则认为是新数据,反则认为旧数据
实现Persistable 如果实体实现了Persistable接口,那么就会通过isNew的方法来监测。详细看JavaDoc
实现EntityInfomation 这个是很少用的,详细请查看JavaDoc
2.3 查询方法
2.3.1 查询策略
你可以写一个语句或者从方法名中查询。
声明查询语句
虽然说方法名查询的方式很方便,可是你可能会遇到方法名查询规则不支持你所要查询的关键字或者方法名写的很长,不方便,或者很丑陋。那么你就需要通过命名查询或者在方法上使用
@Query来解决。
2.3.2 查询创建器
通常我们可以使用方法名来解析查询语句,例如:
例2.3 方法名查询
这个查询方法相当于如下的语句:
以下表格中展示出方法名中支持的关键字:
表格2.3 支持的关键字
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Between findByStartDateBetween … where x.startDate between 1? and ?2
关键字 例子 JPQL 片段
1 4
1 5
1 6
1 7
1 8
1 9
2 0
2 1
2 2
2 3
2 4
2 5
2 6
2 7
2 8
2 9
3 0
3 1
3 2
3 3
3 4
3 5
p u b l i c E n t i t y M a n a g e r F a c t o r y e n t i t y M a n a g e r F a c t o r y ( ) {
H i b e r n a t e J p a V e n d o r A d a p t e r v e n d o r A d a p t e r = n e w H i b e r n a t e J p a V e n d o r A d a p t e r ( ) ;
v e n d o r A d a p t e r . s e t G e n e r a t e D d l ( t r u e ) ;
L o c a l C o n t a i n e r E n t i t y M a n a g e r F a c t o r y B e a n f a c t o r y = n e w L o c a l C o n t a i n e r E n t i t y M a n a g e r F a c t o r y B e a n ( ) ;
f a c t o r y . s e t J p a V e n d o r A d a p t e r ( v e n d o r A d a p t e r ) ;
f a c t o r y . s e t P a c k a g e s T o S c a n ( " c o m . a c m e . d o m a i n " ) ;
f a c t o r y . s e t D a t a S o u r c e ( d a t a S o u r c e ( ) ) ;
f a c t o r y . a f t e r P r o p e r t i e s S e t ( ) ;
r e t u r n f a c t o r y . g e t O b j e c t ( ) ;
}
@ B e a n
p u b l i c P l a t f o r m T r a n s a c t i o n M a n a g e r t r a n s a c t i o n M a n a g e r ( ) {
J p a T r a n s a c t i o n M a n a g e r t x M a n a g e r = n e w J p a T r a n s a c t i o n M a n a g e r ( ) ;
t x M a n a g e r . s e t E n t i t y M a n a g e r F a c t o r y ( e n t i t y M a n a g e r F a c t o r y ( ) ) ;
r e t u r n t x M a n a g e r ;
}
}
1
2
3
4
p u b l i c i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s R e p o s i t o r y < U s e r , L o n g > {
L i s t < U s e r > f i n d B y E m a i l A d d r e s s A n d L a s t n a m e ( S t r i n g e m a i l A d d r e s s , S t r i n g l a s t n a m e ) ;
}
s e l e c t u f r o m U s e r u w h e r e u . e m a i l A d d r e s s = ? 1 a n d u . l a s t n a m e = ? 213-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 14/19
LessThan findByAgeLessThan … where x.age < ?1
GreaterThan findByAgeGreaterThan … 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
NotIn findByAgeNotIn(Collection
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
2.3.3 使用JPA命名查询
XML命名查询定义
在XML中配置的话,需要在JPA在META-INF目录中的配置文件orm.xml中使用
例2.4 XML命名查询配置
上面的Query定义了名称,那么将在运行时解析使用。
注解方式
使用注解方式,你不再需要使用配置文件,减少你的开发成本,接下来我们用注解的方式来做:
例2.5 基于注解的命名查询
声明接口
要使用上面的命名查询,我们的接口需要这么声明:
例2.6 声明接口
SpringData会先从域类中查询配置,根据”.(原点)“区分方法名,而不会使用自动方法名解析的方式去创建查询。
2.3.4 使用@Query
命名查询适合用于小数量的查询,我们可以使用@Query来替代:
例2.7 使用@Query查询
< n a m e d - q u e r y n a m e = " U s e r . f i n d B y L a s t n a m e " >
< q u e r y > s e l e c t u f r o m U s e r u w h e r e u . l a s t n a m e = ? 1 < / q u e r y >
< / n a m e d - q u e r y >
@ E n t i t y
@ N a m e d Q u e r y ( n a m e = " U s e r . f i n d B y E m a i l A d d r e s s " ,
q u e r y = " s e l e c t u f r o m U s e r u w h e r e u . e m a i l A d d r e s s = ? 1 " )
p u b l i c c l a s s U s e r {
}
p u b l i c i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s J p a R e p o s i t o r y < U s e r , L o n g > {
L i s t < U s e r > f i n d B y L a s t n a m e ( S t r i n g l a s t n a m e ) ;
U s e r f i n d B y E m a i l A d d r e s s ( S t r i n g e m a i l A d d r e s s ) ;
}
p u b l i c i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s J p a R e p o s i t o r y < U s e r , L o n g > {
@ Q u e r y ( " s e l e c t u f r o m U s e r u w h e r e u . e m a i l A d d r e s s = ? 1 " )
U s e r f i n d B y E m a i l A d d r e s s ( S t r i n g e m a i l A d d r e s s ) ;13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 15/19
LIKE查询
在表达式中使用Like查询,例子如下:
@Query中使用LIKE
这个例子中,我们使用了%,当然,你的参数就没必要加入这个符号了。
原生查询
我们可以在@Query中使用本地查询,当然,你需要设置nativeQuery=true,必须说明的是,这样的话,就不再支持分页以及排序。
例2.9 声明本地查询
2.3.5 使用命名参数
使用命名查询,我们需要用到@Param来注释到指定的参数上,如下:
例2.10 使用命名参数
2.3.6 使用SpELl表达式
在Spring Data JPA 1.4以后,我们支持在@Query中使用SpELl表达式来接收变量。
表格2.4 在SpELl中支持的变量
变量 用途 描述
entityName select x from #{#entityName}
x
根据给定的Repository自动插入相关的entityName。有两种方式能被解析出来:如果域类型定义了@Entity属性名称。或者直接使用类名
称。
以下的例子中,我们在查询语句中插入表达式(你也可以用@Entity(name = "MyUser")。
例2.11 使用SpELl表达式
如果你想写一个通用的Repository接口,那么你也可以用这个表达式来处理:
例2.12 使用SpELl表达式
}
p u b l i c i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s J p a R e p o s i t o r y < U s e r , L o n g > {
@ Q u e r y ( " s e l e c t u f r o m U s e r u w h e r e u . f i r s t n a m e l i k e % ? 1 " )
L i s t < U s e r > f i n d B y F i r s t n a m e E n d s W i t h ( S t r i n g f i r s t n a m e ) ;
}
p u b l i c i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s J p a R e p o s i t o r y < U s e r , L o n g > {
@ Q u e r y ( v a l u e = " S E L E C T * F R O M U S E R S W H E R E E M A I L _ A D D R E S S = ? 0 " , n a t i v e Q u e r y = t r u e )
U s e r f i n d B y E m a i l A d d r e s s ( S t r i n g e m a i l A d d r e s s ) ;
}
p u b l i c i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s J p a R e p o s i t o r y < U s e r , L o n g > {
@ Q u e r y ( " s e l e c t u f r o m U s e r u w h e r e u . f i r s t n a m e = : f i r s t n a m e o r u . l a s t n a m e = : l a s t n a m e " )
U s e r f i n d B y L a s t n a m e O r F i r s t n a m e ( @ P a r a m ( " l a s t n a m e " ) S t r i n g l a s t n a m e ,
@ P a r a m ( " f i r s t n a m e " ) S t r i n g f i r s t n a m e ) ;
}
1
2
3
4
5
6
7
8
9
1 0
1 1
1 2
@ E n t i t y
p u b l i c c l a s s U s e r {
@ I d @ G e n e r a t e d V a l u e L o n g i d ;
S t r i n g l a s t n a m e ;
}
p u b l i c i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s J p a R e p o s i t o r y < U s e r , L o n g > {
@ Q u e r y ( " s e l e c t u f r o m # { # e n t i t y N a m e } u w h e r e u . l a s t n a m e = ? 1 " )
L i s t < U s e r > f i n d B y L a s t n a m e ( S t r i n g l a s t n a m e ) ;
}
1
2
3
4
5
6
7
@ M a p p e d S u p e r c l a s s
p u b l i c a b s t r a c t c l a s s A b s t r a c t M a p p e d T y p e {
…
S t r i n g a t t r i b u t e
}
@ E n t i t y13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 16/19
2.3.7 修改语句
之前我们演示了如何去声明查询语句,当然我们还有修改语句。修改语句的实现,我们只需要在加多一个注解@Modify:
例2.13 声明修改语句
这样一来,我们就使用了update操作来代替select操作。当我们发起update操作后,可能会有一些过期的数据产生,我们不需要自动去清除它们,因为EntityManager会有效的丢掉那些未提
交的变更,如果你想EntityManager自动清除,那么你可以在@Modify上添加clearAutomatically属性(true);
2.3.8 使用QueryHints
你可以使用@QueryHints来启用:
例2.14 使用QueryHints
2.4 Specifications
JPA2 引入了criteria API 去建立查询,Spring Data JPA使用Specifications来实现这个API。在Repository中,你需要继承J p a S p e c i f i c a t i o n E x e c u t o r :
下面先给个例子,演示如何利用findAll方法返回所有符合条件的对象:
S p e c i f i c a t i o n 接口定义如下:
好了,那么我们如何去实现这个接口呢?代码如下:
例2.15 实现Specifications
8
9
1 0
1 1
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
p u b l i c c l a s s C o n c r e t e T y p e e x t e n d s A b s t r a c t M a p p e d T y p e { … }
@ N o R e p o s i t o r y B e a n
p u b l i c i n t e r f a c e M a p p e d T y p e R e p o s i t o r y < T e x t e n d s A b s t r a c t M a p p e d T y p e >
e x t e n d s R e p o s i t o r y < T , L o n g > {
@ Q u e r y ( " s e l e c t t f r o m # { # e n t i t y N a m e } t w h e r e t . a t t r i b u t e = ? 1 " )
L i s t < T > f i n d A l l B y A t t r i b u t e ( S t r i n g a t t r i b u t e ) ;
}
p u b l i c i n t e r f a c e C o n c r e t e R e p o s i t o r y
e x t e n d s M a p p e d T y p e R e p o s i t o r y < C o n c r e t e T y p e > { … }
@ M o d i f y i n g
@ Q u e r y ( " u p d a t e U s e r u s e t u . f i r s t n a m e = ? 1 w h e r e u . l a s t n a m e = ? 2 " )
i n t s e t F i x e d F i r s t n a m e F o r ( S t r i n g f i r s t n a m e , S t r i n g l a s t n a m e ) ;
1
2
3
4
5
6
p u b l i c i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s R e p o s i t o r y < U s e r , L o n g > {
@ Q u e r y H i n t s ( v a l u e = { @ Q u e r y H i n t ( n a m e = " n a m e " , v a l u e = " v a l u e " ) } ,
f o r C o u n t i n g = f a l s e )
P a g e < U s e r > f i n d B y L a s t n a m e ( S t r i n g l a s t n a m e , P a g e a b l e p a g e a b l e ) ;
}
p u b l i c i n t e r f a c e C u s t o m e r R e p o s i t o r y e x t e n d s C r u d R e p o s i t o r y < C u s t o m e r , L o n g > , J p a S p e c i f i c a t i o n E x e c u t o r {
…
}
L i s t < T > f i n d A l l ( S p e c i f i c a t i o n < T > s p e c ) ;
p u b l i c i n t e r f a c e S p e c i f i c a t i o n < T > {
P r e d i c a t e t o P r e d i c a t e ( R o o t < T > r o o t , C r i t e r i a Q u e r y < ? > q u e r y ,
C r i t e r i a B u i l d e r b u i l d e r ) ;
}
1
2
3
4
5
6
7
8
9
1 0
1 1
1 2
1 3
1 4
p u b l i c c l a s s C u s t o m e r S p e c s {
p u b l i c s t a t i c S p e c i f i c a t i o n < C u s t o m e r > i s L o n g T e r m C u s t o m e r ( ) {
r e t u r n n e w S p e c i f i c a t i o n < C u s t o m e r > ( ) {
p u b l i c P r e d i c a t e t o P r e d i c a t e ( R o o t < C u s t o m e r > r o o t , C r i t e r i a Q u e r y < ? > q u e r y ,
C r i t e r i a B u i l d e r b u i l d e r ) {
L o c a l D a t e d a t e = n e w L o c a l D a t e ( ) . m i n u s Y e a r s ( 2 ) ;
r e t u r n b u i l d e r . l e s s T h a n ( r o o t . g e t ( C u s t o m e r _ . c r e a t e d A t ) , d a t e ) ;
}
} ;
}
p u b l i c s t a t i c S p e c i f i c a t i o n < C u s t o m e r > h a s S a l e s O f M o r e T h a n ( M o n t a r y A m o u n t v a l u e ) {13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 17/19
接下来,我们如何去调用这个方法呢?
例2.16 调用
好了,那如果有多个需要结合的话,我们可以这么做:
例2.17 多个Specifications
2.5 事务
默认的CRUD操作在Repository里面都是事务性的。如果是只读操作,只需要设置事务readOnly为true,其他的操作则配置为@Transaction。如果你想修改一个Repository的事务性,你只需
要在子接口中重写并且修改他的事务:
例2.18 自定义事务
这样会让findAll方法在10秒内执行否则会超时的非只读事务中。
另一种修改事务行为的方式在于使用门面或者服务层中,他们包含了多个repository。
例2.19 使用门面模式去定义事务
这将会导致在调用addRoleToAllUsers方法的时候,创建一个或者加入一个事务中去。实际在Repository里面定义的事务将会被忽略,而外部定义的事务将会被应用。当然,要使用事务,你
需要声明
2.5.1 事务性查询方法
要让方法在事务中,最简单的方式就是使用@Transactional注解:
例2.20 使用@Transactional
1 5
1 6
1 7
1 8
1 9
2 0
2 1
2 2
2 3
r e t u r n n e w S p e c i f i c a t i o n < C u s t o m e r > ( ) {
p u b l i c P r e d i c a t e t o P r e d i c a t e ( R o o t < T > r o o t , C r i t e r i a Q u e r y < ? > q u e r y ,
C r i t e r i a B u i l d e r b u i l d e r ) {
/ / b u i l d q u e r y h e r e
}
} ;
}
}
L i s t < C u s t o m e r > c u s t o m e r s = c u s t o m e r R e p o s i t o r y . f i n d A l l ( i s L o n g T e r m C u s t o m e r ( ) ) ;
M o n e t a r y A m o u n t a m o u n t = n e w M o n e t a r y A m o u n t ( 2 0 0 . 0 , C u r r e n c i e s . D O L L A R ) ;
L i s t < C u s t o m e r > c u s t o m e r s = c u s t o m e r R e p o s i t o r y . f i n d A l l (
w h e r e ( i s L o n g T e r m C u s t o m e r ( ) ) . o r ( h a s S a l e s O f M o r e T h a n ( a m o u n t ) ) ) ;
1
2
3
4
5
6
7
8
p u b l i c i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s J p a R e p o s i t o r y < U s e r , L o n g > {
@ O v e r r i d e
@ T r a n s a c t i o n a l ( t i m e o u t = 1 0 )
p u b l i c L i s t < U s e r > f i n d A l l ( ) ;
/ / 其他方法
}
1
2
3
4
5
6
7
8
9
1 0
1 1
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
2 0
2 1
2 2
2 3
@ S e r v i c e
c l a s s U s e r M a n a g e m e n t I m p l i m p l e m e n t s U s e r M a n a g e m e n t {
p r i v a t e f i n a l U s e r R e p o s i t o r y u s e r R e p o s i t o r y ;
p r i v a t e f i n a l R o l e R e p o s i t o r y r o l e R e p o s i t o r y ;
@ A u t o w i r e d
p u b l i c U s e r M a n a g e m e n t I m p l ( U s e r R e p o s i t o r y u s e r R e p o s i t o r y ,
R o l e R e p o s i t o r y r o l e R e p o s i t o r y ) {
t h i s . u s e r R e p o s i t o r y = u s e r R e p o s i t o r y ;
t h i s . r o l e R e p o s i t o r y = r o l e R e p o s i t o r y ;
}
@ T r a n s a c t i o n a l
p u b l i c v o i d a d d R o l e T o A l l U s e r s ( S t r i n g r o l e N a m e ) {
R o l e r o l e = r o l e R e p o s i t o r y . f i n d B y N a m e ( r o l e N a m e ) ;
f o r ( U s e r u s e r : u s e r R e p o s i t o r y . f i n d A l l ( ) ) {
u s e r . a d d R o l e ( r o l e ) ;
u s e r R e p o s i t o r y . s a v e ( u s e r ) ;
}
}
1 @ T r a n s a c t i o n a l ( r e a d O n l y = t r u e )13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 18/19
一般的查询操作,你需要设置readOnly=true。在deleteInactiveUsers方法中,我们添加了Modifying注解以及覆盖了Transactional,这样导致了readOnly=false,在这个方法的执行上。
2.6 锁
想要为方法指定锁的类型,你只需要使用@Lock:
例2.21 声明锁
当然你也可以覆盖原有的方法:
例2.22 覆盖原有的CRUD
2.7 审计
2.7.1 基础知识
SpringData为您跟踪谁创建或者修改数据,以及相应的时间提供了复杂的支持。你现在想要这些支持的话,仅仅需要使用几个注解或者实现接口即可。
注解方式
我们提供了@CreatedBy, @LastModifiedBy去捕获谁操作的实体,当然还有相应的时间@CreatedDate和@LastModifiedDate。
例2.23 审计实体
正如你看到的,你可以选择性的使用这些注解。操作时间方面,你可以使用o r g . j o d a . t i m e . D a t e T i m e 或者j a v a . u t i l . D a t e 或者l o n g / L o n g 表示。
基于接口的审计
如果你不想用注解来做审计的话,那么你可以实现Auditable接口。他暴露了审计属性的get/set方法。
如果你不想实现接口,那么你可以继承AbstractAuditable,通常来说,注解方式时更加方便的。
审计织入
如果你在用@CreatedBy或者@LastModifiedBy的时候,想织入当前的业务操作者,那你可以使用我们提供的AuditorAware
下面给出一个案例,我们将结合SpringSecurity来做:
例2.24 实现AuditorAware接口
2
3
4
5
6
7
8
9
1 0
p u b l i c i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s J p a R e p o s i t o r y < U s e r , L o n g > {
L i s t < U s e r > f i n d B y L a s t n a m e ( S t r i n g l a s t n a m e ) ;
@ M o d i f y i n g
@ T r a n s a c t i o n a l
@ Q u e r y ( " d e l e t e f r o m U s e r u w h e r e u . a c t i v e = f a l s e " )
v o i d d e l e t e I n a c t i v e U s e r s ( ) ;
}
1
2
3
4
5
6
i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s R e p o s i t o r y < U s e r , L o n g > {
/ / P l a i n q u e r y m e t h o d
@ L o c k ( L o c k M o d e T y p e . R E A D )
L i s t < U s e r > f i n d B y L a s t n a m e ( S t r i n g l a s t n a m e ) ;
}
1
2
3
4
5
6
i n t e r f a c e U s e r R e p o s i t o r y e x t e n d s R e p o s i t o r y < U s e r , L o n g > {
/ / R e d e c l a r a t i o n o f a C R U D m e t h o d
@ L o c k ( L o c k M o d e T y p e . R E A D ) ;
L i s t < U s e r > f i n d A l l ( ) ;
}
1
2
3
4
5
6
7
8
9
1 0
c l a s s C u s t o m e r {
@ C r e a t e d B y
p r i v a t e U s e r u s e r ;
@ C r e a t e d D a t e
p r i v a t e D a t e T i m e c r e a t e d D a t e ;
/ / … 省略其他属性
}
1
2
3
4
5
6
7
c l a s s S p r i n g S e c u r i t y A u d i t o r A w a r e i m p l e m e n t s A u d i t o r A w a r e < U s e r > {
p u b l i c U s e r g e t C u r r e n t A u d i t o r ( ) {
A u t h e n t i c a t i o n a u t h e n t i c a t i o n = S e c u r i t y C o n t e x t H o l d e r . g e t C o n t e x t ( ) . g e t A u t h e n t i c a t i o n ( ) ;
i f ( a u t h e n t i c a t i o n = = n u l l | | ! a u t h e n t i c a t i o n . i s A u t h e n t i c a t e d ( ) ) {13-12-17 Spring Data JPA中文文档[1.4.3] - 产品研发部Java架构组 - 产品研发部Wik i
192.168.0.242:8090/pages/viewpage.action?pageId=1802242 19/19
赞同 成为第一个赞同者
在这个实现类中,我们使用SpringSecurity内置的Authentication来查找用户的UserDetails。
2.7.2 通用审计配置
SpringData JPA有一个实体监听器,他可以用于触发捕获审计信息。要用之前,你需要在orm.xml里面注册AuditingEntityListener,当然你还需要spring-sapects.jar引入:
例2.25 审计配置
要启用这个审计,我们还需要在配置文件里面配置多一条:
例2.26 启用审计
正如你所见,你还需要提供一个实现AuditorAware接口的类:
例2.27 AuditorAware接口
通常你的系统中会有认证模块,那么你的认证模块里面最好织入这个AuditorAware方便审计。
8
9
1 0
1 1
1 2
1 3
r e t u r n n u l l ;
}
r e t u r n ( ( M y U s e r D e t a i l s ) a u t h e n t i c a t i o n . g e t P r i n c i p a l ( ) ) . g e t U s e r ( ) ;
}
}
< p e r s i s t e n c e - u n i t - m e t a d a t a >
< p e r s i s t e n c e - u n i t - d e f a u l t s >
< e n t i t y - l i s t e n e r s >
< e n t i t y - l i s t e n e r c l a s s = " … . d a t a . j p a . d o m a i n . s u p p o r t . A u d i t i n g E n t i t y L i s t e n e r " / >
< / e n t i t y - l i s t e n e r s >
< / p e r s i s t e n c e - u n i t - d e f a u l t s >
< / p e r s i s t e n c e - u n i t - m e t a d a t a >
< j p a : a u d i t i n g a u d i t o r - a w a r e - r e f = " y o u r A u d i t o r A w a r e B e a n " / >
1
2
3
4
p u b l i c i n t e r f a c e A u d i t o r A w a r e < T , I D e x t e n d s S e r i a l i z a b l e > {
T g e t C u r r e n t A u d i t o r ( ) ;
}
无标签