mybatis分页拦截器可以传递参数(可以复制直接使用)

总目录

1.在spring-mybatis.xml配置拦截器的代码

2.在controller层的代码(使用map传递自定义参数)

3.分页拦截器代码

4.page类

可以复制直接使用

1.在spring-mybatis.xml配置拦截器的代码

配置拦截器,设置数据库方言dialect,设置需要拦截的目标pageSqlId

<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

<property name="dataSource" ref="dataSource" />

<!-- 自动扫描mapping.xml文件 -->

<property name="mapperLocations" value="classpath:com/org/mapping/*.xml"></property>

<property name="plugins">

<array>

<bean class="com.org.interceptor.PageInterceptor">

<property name="dialect" value="mysql" />

<property name="pageSqlId" value=".*Page$" />

</bean>

</array>

</property>

</bean>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

2.在controller层的代码(使用map传递自定义参数)

传递wirterId 作者编号,pageNo页码给后台分页取值

/**

* 获取用户书柜列表

*

* @param request

* wirterId 作者编号,pageNo页码

* @param response

* @param page

* @throws IOException

*/

@RequestMapping(value = "/listByPage.do", method = RequestMethod.POST)

@ResponseBody

public void listByPage(HttpServletRequest request, HttpServletResponse response,

@ModelAttribute Page<Bookcase> page, @RequestParam("writerId") String writerId) throws IOException {

JSONObject obj = new JSONObject();

try {

Map<Object, Object> map = new HashMap<Object, Object>();

map.put("writerId", writerId);

map.put("page", page);

List<Bookcase> bookcaseList = bookcaseService.findListByPage(map);

System.out.println(" 当前页:" + page.getPageNo() + "");

obj.put("state", 1);

obj.put("bookcaseList", JSONArray.toJSON(bookcaseList));

obj.put("toltalPage", page.getTotalPage());

obj.put("message", "获取成功");

} catch (Exception e) {

e.printStackTrace();

obj.put("state", 0);

obj.put("message", "获取失败");

}

response.setContentType("text/html;charset=utf-8");

response.getWriter().print(obj.toString());

System.out.println(obj.toString());

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

3.分页拦截器代码

package com.org.interceptor;

import java.lang.reflect.Field;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.List;

import java.util.Map;

import java.util.Properties;

import org.apache.ibatis.executor.parameter.ParameterHandler;

import org.apache.ibatis.executor.statement.RoutingStatementHandler;

import org.apache.ibatis.executor.statement.StatementHandler;

import org.apache.ibatis.mapping.BoundSql;

import org.apache.ibatis.mapping.MappedStatement;

import org.apache.ibatis.mapping.ParameterMapping;

import org.apache.ibatis.plugin.Interceptor;

import org.apache.ibatis.plugin.Intercepts;

import org.apache.ibatis.plugin.Invocation;

import org.apache.ibatis.plugin.Plugin;

import org.apache.ibatis.plugin.Signature;

import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;

import com.org.util.Page;

/**

*

* 分页拦截器,用于拦截需要进行分页查询的操作,然后对其进行分页处理。 利用拦截器实现Mybatis分页的原理:

* 要利用JDBC对数据库进行操作就必须要有一个对应的Statement对象,Mybatis在执行Sql语句前就会产生一个包含Sql语句的Statement对象,而且对应的Sql语句

* 是在Statement之前产生的,所以我们就可以在它生成Statement之前对用来生成Statement的Sql语句下手。在Mybatis中Statement语句是通过RoutingStatementHandler对象的

* prepare方法生成的。所以利用拦截器实现Mybatis分页的一个思路就是拦截StatementHandler接口的prepare方法,然后在拦截器方法中把Sql语句改成对应的分页查询Sql语句,之后再调用

* StatementHandler对象的prepare方法,即调用invocation.proceed()。

* 对于分页而言,在拦截器里面我们还需要做的一个操作就是统计满足当前条件的记录一共有多少,这是通过获取到了原始的Sql语句后,把它改为对应的统计语句再利用Mybatis封装好的参数和设

* 置参数的功能把Sql语句中的参数进行替换,之后再执行查询记录数的Sql语句进行总记录数的统计。

*

*/

@Intercepts({ @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class }) })

public class PageInterceptor implements Interceptor {

private String dialect ="";// 数据库方言

private String pageSqlId = ""; // mapper.xml中需要拦截的ID(正则匹配)

/**

* 拦截后要执行的方法

*/

public Object intercept(Invocation invocation) throws Throwable {

// 对于StatementHandler其实只有两个实现类,一个是RoutingStatementHandler,另一个是抽象类BaseStatementHandler,

// BaseStatementHandler有三个子类,分别是SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler,

// SimpleStatementHandler是用于处理Statement的,PreparedStatementHandler是处理PreparedStatement的,而CallableStatementHandler是

// 处理CallableStatement的。Mybatis在进行Sql语句处理的时候都是建立的RoutingStatementHandler,而在RoutingStatementHandler里面拥有一个

// StatementHandler类型的delegate属性,RoutingStatementHandler会依据Statement的不同建立对应的BaseStatementHandler,即SimpleStatementHandler、

// PreparedStatementHandler或CallableStatementHandler,在RoutingStatementHandler里面所有StatementHandler接口方法的实现都是调用的delegate对应的方法。

// 我们在PageInterceptor类上已经用@Signature标记了该Interceptor只拦截StatementHandler接口的prepare方法,又因为Mybatis只有在建立RoutingStatementHandler的时候

// 是通过Interceptor的plugin方法进行包裹的,所以我们这里拦截到的目标对象肯定是RoutingStatementHandler对象。

if (invocation.getTarget() instanceof RoutingStatementHandler) {

RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation.getTarget();

StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(statementHandler, "delegate");

BoundSql boundSql = delegate.getBoundSql();

Object obj = boundSql.getParameterObject();

// 返回存在page的obj对象(主要查看pae是否在map对象里面)

obj = findPageObject(obj);

if (obj instanceof Page<?>) {

System.out.println("---------分页-------");

Page<?> page = (Page<?>) obj;

// 通过反射获取delegate父类BaseStatementHandler的mappedStatement属性

MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate,

"mappedStatement");

// 拦截到的prepare方法参数是一个Connection对象

Connection connection = (Connection) invocation.getArgs()[0];

// 获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的Sql语句

String sql = boundSql.getSql();

// 给当前的page参数对象设置总记录数

Object parameterObj = boundSql.getParameterObject();

this.setTotalRecord(page, parameterObj, mappedStatement, connection);

// 获取分页Sql语句

String pageSql = this.getPageSql(page, sql);

// 利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句

ReflectUtil.setFieldValue(boundSql, "sql", pageSql);

}

}

return invocation.proceed();

}

/**

* 查找对象是否包括page类

*

* @param parameterObj

* @return

*/

public Page<?> findPageObject(Object parameterObj) {

if (parameterObj instanceof Page<?>) {

return (Page<?>) parameterObj;

} else if (parameterObj instanceof Map) {

for (Object val : ((Map<?, ?>) parameterObj).values()) {

if (val instanceof Page<?>) {

return (Page<?>) val;

}

}

}

return null;

}

/**

* 拦截器对应的封装原始对象的方法

*/

public Object plugin(Object target) {

return Plugin.wrap(target, this);

}

/**

* 根据page对象获取对应的分页查询Sql语句,这里只做了两种数据库类型,Mysql和Oracle 其它的数据库都 没有进行分页

*

* @param page

* 分页对象

* @param sql

* 原sql语句

* @return

*/

private String getPageSql(Page<?> page, String sql) {

StringBuffer sqlBuffer = new StringBuffer(sql);

if ("mysql".equalsIgnoreCase(dialect)) {

return getMysqlPageSql(page, sqlBuffer);

} else if ("oracle".equalsIgnoreCase(dialect)) {

return getOraclePageSql(page, sqlBuffer);

}

return sqlBuffer.toString();

}

/**

* 获取Mysql数据库的分页查询语句

*

* @param page

* 分页对象

* @param sqlBuffer

* 包含原sql语句的StringBuffer对象

* @return Mysql数据库分页语句

*/

private String getMysqlPageSql(Page<?> page, StringBuffer sqlBuffer) {

// 计算第一条记录的位置,Mysql中记录的位置是从0开始的。

int offset = (page.getPageNo() - 1) * page.getPageSize();

sqlBuffer.append(" limit ").append(offset).append(",").append(page.getPageSize());

return sqlBuffer.toString();

}

/**

* 获取Oracle数据库的分页查询语句

*

* @param page

* 分页对象

* @param sqlBuffer

* 包含原sql语句的StringBuffer对象

* @return Oracle数据库的分页查询语句

*/

private String getOraclePageSql(Page<?> page, StringBuffer sqlBuffer) {

// 计算第一条记录的位置,Oracle分页是通过rownum进行的,而rownum是从1开始的

int offset = (page.getPageNo() - 1) * page.getPageSize() + 1;

sqlBuffer.insert(0, "select u.*, rownum r from (").append(") u where rownum < ")

.append(offset + page.getPageSize());

sqlBuffer.insert(0, "select * from (").append(") where r >= ").append(offset);

// 上面的Sql语句拼接之后大概是这个样子:

// select * from (select u.*, rownum r from (select * from t_user) u

// where rownum < 31) where r >= 16

return sqlBuffer.toString();

}

/**

* 给当前的参数对象page设置总记录数

*

* @param page

* Mapper映射语句对应的参数对象

* @param mappedStatement

* Mapper映射语句

* @param connection

* 当前的数据库连接

*/

private void setTotalRecord(Page<?> page, Object parameterObject, MappedStatement mappedStatement,

Connection connection) {

BoundSql boundSql = mappedStatement.getBoundSql(page);

String sql = boundSql.getSql();

String countSql = this.getCountSql(sql);

List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings,

parameterObject);

ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject,

countBoundSql);

PreparedStatement pstmt = null;

ResultSet rs = null;

try {

pstmt = connection.prepareStatement(countSql);

// 通过parameterHandler给PreparedStatement对象设置参数

parameterHandler.setParameters(pstmt);

// 之后就是执行获取总记录数的Sql语句和获取结果了。

rs = pstmt.executeQuery();

if (rs.next()) {

int totalRecord = rs.getInt(1);

// 给当前的参数page对象设置总记录数

page.setTotalRecord(totalRecord);

}

} catch (SQLException e) {

e.printStackTrace();

} finally {

try {

if (rs != null)

rs.close();

if (pstmt != null)

pstmt.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

}

/**

* 根据原Sql语句获取对应的查询总记录数的Sql语句

*

* @param sql

* @return

*/

private String getCountSql(String sql) {

int index = sql.indexOf("from");

return "select count(*) " + sql.substring(index);

}

/**

* 设置注册拦截器时设定的属性

*/

public void setProperties(Properties p) {

}

public String getDialect() {

return dialect;

}

public void setDialect(String dialect) {

this.dialect = dialect;

}

public String getPageSqlId() {

return pageSqlId;

}

public void setPageSqlId(String pageSqlId) {

this.pageSqlId = pageSqlId;

}

/**

* 利用反射进行操作的一个工具类

*

*/

private static class ReflectUtil {

/**

* 利用反射获取指定对象的指定属性

*

* @param obj

* 目标对象

* @param fieldName

* 目标属性

* @return 目标属性的值

*/

public static Object getFieldValue(Object obj, String fieldName) {

Object result = null;

Field field = ReflectUtil.getField(obj, fieldName);

if (field != null) {

field.setAccessible(true);

try {

result = field.get(obj);

} catch (IllegalArgumentException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

}

return result;

}

/**

* 利用反射获取指定对象里面的指定属性

*

* @param obj

* 目标对象

* @param fieldName

* 目标属性

* @return 目标字段

*/

private static Field getField(Object obj, String fieldName) {

Field field = null;

for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {

try {

field = clazz.getDeclaredField(fieldName);

break;

} catch (NoSuchFieldException e) {

// 这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。

}

}

return field;

}

/**

* 利用反射设置指定对象的指定属性为指定的值

*

* @param obj

* 目标对象

* @param fieldName

* 目标属性

* @param fieldValue

* 目标值

*/

public static void setFieldValue(Object obj, String fieldName, String fieldValue) {

Field field = ReflectUtil.getField(obj, fieldName);

if (field != null) {

try {

field.setAccessible(true);

field.set(obj, fieldValue);

} catch (IllegalArgumentException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

}

}

/**

* 利用反射设置指定对象的指定属性为指定的值

*

* @param obj

* 目标对象

* @param fieldName

* 目标属性

* @param fieldValue

* 目标值

*/

public static void setFieldValue(Object obj, String fieldName, Object fieldValue) {

Field field = ReflectUtil.getField(obj, fieldName);

if (field != null) {

try {

field.setAccessible(true);

field.set(obj, fieldValue);

} catch (IllegalArgumentException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IllegalAccessException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

4.page类

package com.org.util;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

/**

* 对分页的基本数据进行一个简单的封装

*/

public class Page<T> {

private int pageNo = 1;//页码,默认是第一页

private int pageSize = 10;//每页显示的记录数,默认是10

private int totalRecord;//总记录数

private int totalPage;//总页数

private List<T> results;//对应的当前页记录

private Map<String, Object> params = new HashMap<String, Object>();//其他的参数我们把它分装成一个Map对象

public int getPageNo() {

return pageNo;

}

public void setPageNo(int pageNo) {

this.pageNo = pageNo;

}

public int getPageSize() {

return pageSize;

}

public void setPageSize(int pageSize) {

this.pageSize = pageSize;

}

public int getTotalRecord() {

return totalRecord;

}

public void setTotalRecord(int totalRecord) {

this.totalRecord = totalRecord;

//在设置总页数的时候计算出对应的总页数,在下面的三目运算中加法拥有更高的优先级,所以最后可以不加括号。

int totalPage = totalRecord%pageSize==0 ? totalRecord/pageSize : totalRecord/pageSize + 1;

this.setTotalPage(totalPage);

}

public int getTotalPage() {

return totalPage;

}

public void setTotalPage(int totalPage) {

this.totalPage = totalPage;

}

public List<T> getResults() {

return results;

}

public void setResults(List<T> results) {

this.results = results;

}

public Map<String, Object> getParams() {

return params;

}

public void setParams(Map<String, Object> params) {

this.params = params;

}

@Override

public String toString() {

StringBuilder builder = new StringBuilder();

builder.append("Page [pageNo=").append(pageNo).append(", pageSize=")

.append(pageSize).append(", results=").append(results).append(

", totalPage=").append(totalPage).append(

", totalRecord=").append(totalRecord).append("]");

return builder.toString();

}

}

mybatis分页拦截器可以传递参数(可以复制直接使用)

相关推荐