struts2 spring3 mybatis3拦截器分页技术Word下载.docx
《struts2 spring3 mybatis3拦截器分页技术Word下载.docx》由会员分享,可在线阅读,更多相关《struts2 spring3 mybatis3拦截器分页技术Word下载.docx(23页珍藏版)》请在冰豆网上搜索。
21.importorg.apache.ibatis.plugin.Signature;
22.importorg.apache.ibatis.scripting.defaults.DefaultParameterHandler;
23.
24./**
25.*@authorzhouzhenlong
26.*@date2012-11-20下午04:
33:
35
27.*@description不在拦截器中计算总数,影响效率
28.*@versionV1.0
29.*
30.*分页拦截器,用于拦截需要进行分页查询的操作,然后对其进行分页处理。
利用拦截器实现Mybatis分页的原理:
31.*要利用JDBC对数据库进行操作就必须要有一个对应的Statement对象
32.*,Mybatis在执行Sql语句前就会产生一个包含Sql语句的Statement对象,而且对应的Sql语句
33.*是在Statement之前产生的,所以我们就可以在它生成Statement之前对用来生成Statement的Sql语句下手
34.*。
在Mybatis中Statement语句是通过RoutingStatementHandler对象的
35.*prepare方法生成的。
所以利用拦截器实现Mybatis分页的一个思路就是拦截StatementHandler接口的prepare方法
36.*,然后在拦截器方法中把Sql语句改成对应的分页查询Sql语句,之后再调用
37.*StatementHandler对象的prepare方法,即调用invocation.proceed()。
38.*对于分页而言,在拦截器里面我们还需要做的一个操作就是统计满足当前条件的记录一共有多少
39.*,这是通过获取到了原始的Sql语句后,把它改为对应的统计语句再利用Mybatis封装好的参数和设
40.*置参数的功能把Sql语句中的参数进行替换,之后再执行查询记录数的Sql语句进行总记录数的统计。
41.*
42.*/
43.@Intercepts({@Signature(method="
prepare"
type=StatementHandler.class,args={Connection.class})})
44.publicclassPageInterceptorimplementsInterceptor{
45.
46.privateStringdatabaseType;
//数据库类型,不同的数据库有不同的分页方法
47.
48./**
49.*拦截后要执行的方法
50.*/
51.publicObjectintercept(Invocationinvocation)throwsThrowable{
52.//对于StatementHandler其实只有两个实现类,一个是RoutingStatementHandler,另一个是抽象类BaseStatementHandler,
53.//BaseStatementHandler有三个子类,分别是SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler,
54.//SimpleStatementHandler是用于处理Statement的,PreparedStatementHandler是处理PreparedStatement的,而CallableStatementHandler是
55.//处理CallableStatement的。
Mybatis在进行Sql语句处理的时候都是建立的RoutingStatementHandler,而在RoutingStatementHandler里面拥有一个
56.//StatementHandler类型的delegate属性,RoutingStatementHandler会依据Statement的不同建立对应的BaseStatementHandler,即SimpleStatementHandler、
57.//PreparedStatementHandler或CallableStatementHandler,在RoutingStatementHandler里面所有StatementHandler接口方法的实现都是调用的delegate对应的方法。
58.//我们在PageInterceptor类上已经用@Signature标记了该Interceptor只拦截StatementHandler接口的prepare方法,又因为Mybatis只有在建立RoutingStatementHandler的时候
59.//是通过Interceptor的plugin方法进行包裹的,所以我们这里拦截到的目标对象肯定是RoutingStatementHandler对象。
60.finalRoutingStatementHandlerhandler=(RoutingStatementHandler)invocation.getTarget();
61.//通过反射获取到当前RoutingStatementHandler对象的delegate属性
62.finalStatementHandlerdelegate=(StatementHandler)ReflectUtil.getFieldValue(handler,"
delegate"
);
63.//获取到当前StatementHandler的
64.//boundSql,这里不管是调用handler.getBoundSql()还是直接调用delegate.getBoundSql()结果是一样的,因为之前已经说过了
65.//RoutingStatementHandler实现的所有StatementHandler接口方法里面都是调用的delegate对应的方法。
66.finalBoundSqlboundSql=delegate.getBoundSql();
67.//拿到当前绑定Sql的参数对象,就是我们在调用对应的Mapper映射语句时所传入的参数对象
68.finalObjectobj=boundSql.getParameterObject();
69.//这里我们简单的通过传入的是Page对象就认定它是需要进行分页操作的。
70.if(objinstanceofSearchPageUtil){
71.finalSearchPageUtilpage=(SearchPageUtil)obj;
72.//通过反射获取delegate父类BaseStatementHandler的mappedStatement属性
73.//MappedStatementmappedStatement=(MappedStatement)
74.//ReflectUtil.getFieldValue(delegate,"
mappedStatement"
75.//拦截到的prepare方法参数是一个Connection对象
76.//Connectionconnection=(Connection)invocation.getArgs()[0];
77.//获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的Sql语句
78.finalStringsql=boundSql.getSql();
79.//给当前的page参数对象设置总记录数影响性能
80.//this.setTotalRecord(page,mappedStatement,connection);
81.//获取分页Sql语句
82.finalStringpageSql=this.getPageSql(page,sql);
83.//利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句
84.ReflectUtil.setFieldValue(boundSql,"
sql"
pageSql);
85.}
86.returninvocation.proceed();
87.}
88.
89./**
90.*拦截器对应的封装原始对象的方法
91.*/
92.publicObjectplugin(Objecttarget){
93.returnPlugin.wrap(target,this);
94.}
95.
96./**
97.*设置注册拦截器时设定的属性
98.*/
99.publicvoidsetProperties(Propertiesproperties){
100.this.databaseType=properties.getProperty("
databaseType"
101.}
102.
103./**
104.*根据page对象获取对应的分页查询Sql语句,这里只做了两种数据库类型,Mysql和Oracle其它的数据库都没有进行分页
105.*
106.*@parampage
107.*分页对象
108.*@paramsql
109.*原sql语句
110.*@return
111.*/
112.privateStringgetPageSql(SearchPageUtilpage,Stringsql){
113.finalStringBuffersqlBuffer=newStringBuffer(sql);
114.if("
mysql"
.equalsIgnoreCase(databaseType)){
115.returngetMysqlPageSql(page,sqlBuffer);
116.}elseif("
oracle"
117.returngetOraclePageSql(page,sqlBuffer);
118.}
119.returnsqlBuffer.toString();
120.}
121.
122./**
123.*获取Mysql数据库的分页查询语句
124.*
125.*@parampage
126.*分页对象
127.*@paramsqlBuffer
128.*包含原sql语句的StringBuffer对象
129.*@returnMysql数据库分页语句
130.*/
131.privateStringgetMysqlPageSql(SearchPageUtilpage,StringBuffersqlBuffer){
132.//计算第一条记录的位置,Mysql中记录的位置是从0开始的。
133.//intoffset=(page.getPage().getPageIndex()-1)*
134.//page.getPageSize();
135.sqlBuffer.append("
limit"
).append(page.getStartRow()).append("
"
).append(page.getPageSize());
136.returnsqlBuffer.toString();
137.}
138.
139./**
140.*获取Oracle数据库的分页查询语句
141.*
142.*@parampage
143.*分页对象
144.*@paramsqlBuffer
145.*包含原sql语句的StringBuffer对象
146.*@returnOracle数据库的分页查询语句
147.*/
148.privateStringgetOraclePageSql(SearchPageUtilpage,StringBuffersqlBuffer){
149.//计算第一条记录的位置,Oracle分页是通过rownum进行的,而rownum是从1开始的
150.finalintoffset=(page.getPage().getPageIndex()-1)*page.getPageSize()+1;
151.sqlBuffer.insert(0,"
selectu.*,rownumrfrom("
).append("
)uwhererownum<
"
)
152..append(offset+page.getPageSize());
153.sqlBuffer.insert(0,"
select*from("
)wherer>
="
).append(offset);
154.//上面的Sql语句拼接之后大概是这个样子:
155.//select*from(selectu.*,rownumrfrom(select*fromt_user)u
156.//whererownum<
31)wherer>
=16
157.returnsqlBuffer.toString();
158.}
159.
160./**
161.*给当前的参数对象page设置总记录数
162.*
163.*@parampage
164.*Mapper映射语句对应的参数对象
165.*@parammappedStatement
166.*Mapper映射语句
167.*@paramconnection
168.*当前的数据库连接
169.*/
170.privatevoidsetTotalRecord(SearchPageUtilpage,MappedStatementmappedStatement,Connectionconnection){
171.//获取对应的BoundSql,这个BoundSql其实跟我们利用StatementHandler获取到的BoundSql是同一个对象。
172.//delegate里面的boundSql也是通过mappedStatement.getBoundSql(paramObj)方法获取到的。
173.finalBoundSqlboundSql=mappedStatement.getBoundSql(page);
174.//获取到我们自己写在Mapper映射语句中对应的Sql语句
175.finalStringsql=boundSql.getSql();
176.//通过查询Sql语句获取到对应的计算总记录数的sql语句
177.finalStringcountSql=this.getCountSql(sql);
178.//通过BoundSql获取对应的参数映射
179.finalList<
ParameterMapping>
parameterMappings=boundSql.getParameterMappings();
180.//利用Configuration、查询记录数的Sql语句countSql、参数映射关系parameterMappings和参数对象page建立查询记录数对应的BoundSql对象。
181.finalBoundSqlcountBoundSql=newBoundSql(mappedStatement.getConfiguration(),countSql,parameterMappings,
182.page);
183.//通过mappedStatement、参数对象page和BoundSql对象countBoundSql建立一个用于设定参数的ParameterHandler对象
184.finalParameterHandlerparameterHandler=newDefaultParameterHandler(mappedStatement,page,countBoundSql);
185.//通过connection建立一个countSql对应的PreparedStatement对象。
186.PreparedStatementpstmt=null;
187.ResultSetrs=null;
188.try{
189.pstmt=connection.prepareStatement(countSql);
190.//通过parameterHandler给PreparedStatement对象设置参数
191.parameterHandler.setParameters(pstmt);
192.//之后就是执行获取总记录数的Sql语句和获取结果了。
193.rs=pstmt.executeQuery();
194.if(rs.next()){
195.finalinttotalRecord=rs.getInt
(1);
196.//给当前的参数page对象设置总记录数
197.page.getPage().setRowTotal(totalRecord);
198.}
199.}catch(SQLExceptione){
200.e.printStackTrace();
201.}finally{
202.try{
203.if(rs!
=null)
204.rs.close();
205.if(pstmt!
206.pstmt.close();
207.}catch(SQLExceptione){
208.e.printStackTrace();
209.}
210.}
211.}
212.
213./**
214.*根据原Sql语句获取对应的查询总记录数的Sql语句
215.*
216.*@paramsql
217.*@return
218.*/
219.privateStringgetCountSql(Stringsql){
220.finalintindex=sql.indexOf("
from"
221.return"
selectcount(*)"
+sql.substring(index);
222.}
223.
224./**
225.*利用反射进行操作的一个工具类
226.*
227.*/
228.privatestaticclassReflectUtil{
229./**
230.*利用反射获取指定对象的指定属性
231.*
232.*@paramobj
233.*目标对象
234.*@paramfieldName
235.*目标属性
236.*@return目标属性的值
237.*/
238.publicstaticObjectgetFieldValue(Objectobj,StringfieldName){
239.Objectresult=null;
240.finalFieldfield=ReflectUtil.getField(obj,fieldName);
241.if(field!
=null){
242.field.setAccessible(true);
243.try{