1.分页插件的资源
PageHelper中文说明
PageHelper使用手册
PageHelper使用时的注意事项
2.使用方法
测试代码参考
pom.xml中加入:
com.github.pagehelper
pagehelper
5.1.4
mybatis-config.xml中加入:
测试代码:
// 多参数查询
@Test
public void testManyParamQuery() {
// 2.获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3.获取对应mapper
TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
String email = "qq.com";
Byte sex = 1;
// 第一种方式使用map
Map params = new HashMap();
params.put("email", email);
params.put("sex", sex);
Page startPage = PageHelper.startPage(1, 2);
List list1 = mapper.selectByEmailAndSex1(params);
System.out.println(list1.size());
System.out.println(startPage);
// // 第二种方式直接使用参数
// List list2 = mapper.selectByEmailAndSex2(email, sex);
// System.out.println(list2.size());
//
// // 第三种方式用对象
// EmailSexBean esb = new EmailSexBean();
// esb.setEmail(email);
// esb.setSex(sex);
// List list3 = mapper.selectByEmailAndSex3(esb);
// System.out.println(list3.size());
}
结果:
2018-09-29 17:53:40.994 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - ==> Preparing: SELECT count(0) FROM t_user a WHERE a.email LIKE CONCAT('%', ?, '%') AND a.sex = ?
2018-09-29 17:53:41.022 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - ==> Parameters: qq.com(String), 1(Byte)
2018-09-29 17:53:41.035 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - <== Total: 1
sql语句:“com.mysql.jdbc.JDBC4PreparedStatement@498d318c: SELECT count(0) FROM t_user a WHERE a.email LIKE CONCAT('%', 'qq.com', '%') AND a.sex = 1”执行时间为:14毫秒,已经超过阈值!
2018-09-29 17:53:41.039 [main] DEBUG com.enjoylearning.mybatis.mapper.TUserMapper - Cache Hit Ratio [com.enjoylearning.mybatis.mapper.TUserMapper]: 0.0
2018-09-29 17:53:41.040 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - ==> Preparing: select id, user_name, real_name, sex, mobile, email, note, position_id from t_user a where a.email like CONCAT('%', ?, '%') and a.sex = ? LIMIT ?
2018-09-29 17:53:41.040 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - ==> Parameters: qq.com(String), 1(Byte), 2(Integer)
2018-09-29 17:53:41.042 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - <== Total: 2
2
Page{count=true, pageNum=1, pageSize=2, startRow=0, endRow=2, total=2, pages=1, reasonable=false, pageSizeZero=true}[TUser [id=1, userName=lison, realName=李小宇, sex=1, mobile=186995587422, email=lison@qq.com, note=lison的备注, positionId=], TUser [id=2, userName=james, realName=陈大雷, sex=1, mobile=18677885200, email=james@qq.com, note=james的备注, positionId=]]
2.1 测试时发生了莫名其妙的错误
找不到mybatis-config.xml文件,是由于setting配置有问题,javac/jvm的版本配置的乱七八糟,统一更改为1.8之后,就可以了!
3.PageHelper源码解析
@SuppressWarnings({"rawtypes", "unchecked"})
@Intercepts(
{
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
}
)
public class PageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
...
}
@Override
public Object plugin(Object target) {
//TODO Spring bean 方式配置时,如果没有配置属性就不会执行下面的 setProperties 方法,就不会初始化,因此考虑在这个方法中做一次判断和初始化
//TODO https://github.com/pagehelper/Mybatis-PageHelper/issues/26
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
//缓存 count ms
msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties);
String dialectClass = properties.getProperty("dialect");
if (StringUtil.isEmpty(dialectClass)) {
dialectClass = default_dialect_class;
}
try {
Class aClass = Class.forName(dialectClass);
dialect = (Dialect) aClass.newInstance();
} catch (Exception e) {
throw new PageException(e);
}
dialect.setProperties(properties);
String countSuffix = properties.getProperty("countSuffix");
if (StringUtil.isNotEmpty(countSuffix)) {
this.countSuffix = countSuffix;
}
try {
//反射获取 BoundSql 中的 additionalParameters 属性
additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters");
additionalParametersField.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new PageException(e);
}
}
3.1 关键1——数据总条数怎么查到的
private Long executeAutoCount(Executor executor, MappedStatement countMs,
Object parameter, BoundSql boundSql,
RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException, SQLException {
Map additionalParameters = (Map) additionalParametersField.get(boundSql);
//创建 count 查询的缓存 key
CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
//调用方言获取 count sql
String countSql = dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);
//countKey.update(countSql);
BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter);
//当使用动态 SQL 时,可能会产生临时的参数,这些参数需要手动设置到新的 BoundSql 中
for (String key : additionalParameters.keySet()) {
countBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
//执行 count 查询
Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
Long count = (Long) ((List) countResultList).get(0);
return count;
}
在executeAutoCount中计算总数:
2018-09-29 18:08:12.701 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - ==> Preparing: SELECT count(0) FROM t_user a WHERE a.email LIKE CONCAT('%', ?, '%') AND a.sex = ?
2018-09-29 18:08:12.912 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - ==> Parameters: qq.com(String), 1(Byte)
2018-09-29 18:08:13.049 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - <== Total: 1
3.2 关键2——分页怎么做到的
调用方言,获取分页sql
//调用方言获取分页 sql
String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);
BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter);
//设置动态参数
for (String key : additionalParameters.keySet()) {
pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
//执行分页查询
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
} else {
//不执行分页的情况下,也不执行内存分页
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
}
分页使用LIMIT实现的:
2018-09-29 18:14:14.152 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - ==> Preparing: select id, user_name, real_name, sex, mobile, email, note, position_id from t_user a where a.email like CONCAT('%', ?, '%') and a.sex = ? LIMIT ?
2018-09-29 18:14:14.160 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - ==> Parameters: qq.com(String), 1(Byte), 2(Integer)
2018-09-29 18:14:14.174 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - <== Total: 2
4.为什么不支持嵌套结果集查询
分页插件不支持嵌套结果映射
由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。
原因:
- 1)互联网应用不推荐使用嵌套结果集查询
- 2)数据库查询的总数与业务java查询的总数不同,因为业务java查询的总数会有合并
参考
- 1)享学课堂Lison老师笔记