前些日子遇到一个mybatis中进行分页查询的问题,进行分页查询时查询条件传入了一个列表,在mybatis Mapper.xml中表现为foreach处理,查询记录总数时list可以传值进去,但是查询具体某个分页数据时list中的参数死活传不进去。直到采用下面文章中提到的处理才解决。
MyBatis-3.4.4.jar使用分页插件时并且查询条件包含foreach时,分页插件在执行count语句时会抛出异常,报参数为空异常。分页插件会新增一个COUNT的SQL,并复制原BoundSql对象,然后使用DefaultParameterHandler.setParameters给COUNT语句设值。foreach过程中产生的参数变量是放在AdditionalParameter中,但复制BoundSql时并没有复制其中的additionalParameters字段,而由foreach产生的参数是存放在additionalParameters中,所以导致参数空异常。解决方案就是反射获取到additionalParameters字段的值并设置到新产生的BoundSql中去。
// 只拦截select部分
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class PaginationInterceptor implements Interceptor {
private Logger logger = LoggerFactory.getLogger(PaginationInterceptor.class);
Dialect dialect = new MySql5Dialect();
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Object parameter = invocation.getArgs()[1];
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
String originalSql = boundSql.getSql().trim();
RowBounds rowBounds = (RowBounds) invocation.getArgs()[2];
Object parameterObject = boundSql.getParameterObject();
if (boundSql == null || boundSql.getSql() == null || "".equals(boundSql.getSql())) return null;
// 分页参数--上下文传参
PageInfo page = null;
// map传参每次都将currentPage重置,先判读map再判断context
if (parameterObject instanceof PageInfo) {
page = (PageInfo) parameterObject;
} else if (parameterObject instanceof Map) {
Map map = (Map) parameterObject;
if (map.containsKey("page")) {
page = (PageInfo) map.get("page");
}
} else if (null != parameterObject) {
Field pageField = ReflectionUtil.getFieldByFieldName(parameterObject, "page");
if (pageField != null) {
page = (PageInfo) ReflectionUtil.getValueByFieldName(parameterObject, "page");
}
}
// 后面用到了context的东东
if (page != null && page.isPagination() == true) {
if (page.getPageSize() > 10000) {
// throw new Exception("pageSize不能大于10000");
logger.warn("pageSize建议不要大于10000,当前sqlId:{},page:{}", mappedStatement.getId(), JSON.toJSONString(page));
}
int totalRows = page.getTotalRows();
// 得到总记录数
if (totalRows == 0 && page.isNeedCount()) {
StringBuffer countSql = new StringBuffer();
countSql.append(MySql5PageHepler.getCountString(originalSql));
Connection connection = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection();
PreparedStatement countStmt = connection.prepareStatement(countSql.toString());
BoundSql countBS =
new BoundSql(mappedStatement.getConfiguration(), countSql.toString(), boundSql.getParameterMappings(),
parameterObject);
Field metaParamsField = ReflectionUtil.getFieldByFieldName(boundSql, "metaParameters");
if (metaParamsField != null) {
MetaObject mo = (MetaObject) ReflectionUtil.getValueByFieldName(boundSql, "metaParameters");
ReflectionUtil.setValueByFieldName(countBS, "metaParameters", mo);
}
//-------------------------------------------------------//
//大部分网上的资料缺少对additionalParameters处理,这部分很关键//
//-------------------------------------------------------//
Field additionalField = ReflectionUtil.getFieldByFieldName(boundSql, "additionalParameters");
if (additionalField != null) {
Map map = (Map) ReflectionUtil.getValueByFieldName(boundSql, "additionalParameters");
ReflectionUtil.setValueByFieldName(countBS, "additionalParameters", map);
}
setParameters(countStmt, mappedStatement, countBS, parameterObject);
ResultSet rs = countStmt.executeQuery();
if (rs.next()) {
totalRows = rs.getInt(1);
}
rs.close();
countStmt.close();
connection.close();
}
// 分页计算
page.init(totalRows, page.getPageSize(), page.getCurrentPage());
if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
rowBounds = new RowBounds(page.getPageSize() * (page.getCurrentPage() - 1), page.getPageSize());
}
// 分页查询 本地化对象 修改数据库注意修改实现
String pagesql = dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit());
invocation.getArgs()[2] = new RowBounds(RowBounds.NO_ROW_OFFSET, RowBounds.NO_ROW_LIMIT);
BoundSql newBoundSql =
new BoundSql(mappedStatement.getConfiguration(), pagesql, boundSql.getParameterMappings(), boundSql.getParameterObject());
Field metaParamsField = ReflectionUtil.getFieldByFieldName(boundSql, "metaParameters");
if (metaParamsField != null) {
MetaObject mo = (MetaObject) ReflectionUtil.getValueByFieldName(boundSql, "metaParameters");
ReflectionUtil.setValueByFieldName(newBoundSql, "metaParameters", mo);
}
//-------------------------------------------------------//
//大部分网上的资料缺少对additionalParameters处理,这部分很关键//
//-------------------------------------------------------//
Field additionalField = ReflectionUtil.getFieldByFieldName(boundSql, "additionalParameters");
if (additionalField != null) {
Map map = (Map) ReflectionUtil.getValueByFieldName(boundSql, "additionalParameters");
ReflectionUtil.setValueByFieldName(newBoundSql, "additionalParameters", map);
}
MappedStatement newMs = copyFromMappedStatement(mappedStatement, new BoundSqlSqlSource(newBoundSql));
invocation.getArgs()[0] = newMs;
}
return invocation.proceed();
}
public static class BoundSqlSqlSource implements SqlSource {
BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql) {
this.boundSql = boundSql;
}
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}
public Object plugin(Object arg0) {
return Plugin.wrap(arg0, this);
}
public void setProperties(Properties arg0) {
}
/**
* 对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.DefaultParameterHandler
*
* @param ps
* @param mappedStatement
* @param boundSql
* @param parameterObject
* @throws SQLException
*/
private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
Configuration configuration = mappedStatement.getConfiguration();
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
PropertyTokenizer prop = new PropertyTokenizer(propertyName);
if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX) && boundSql.hasAdditionalParameter(prop.getName())) {
value = boundSql.getAdditionalParameter(prop.getName());
if (value != null) {
value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
}
} else {
value = metaObject == null ? null : metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
if (typeHandler == null) {
throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement "
+ mappedStatement.getId());
}
typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
}
}
}
}
private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
builder.keyProperty(buildKeyProperty(ms.getKeyProperties()));
builder.timeout(ms.getTimeout());
builder.parameterMap(ms.getParameterMap());
builder.resultMaps(ms.getResultMaps());
builder.useCache(ms.isUseCache());
builder.cache(ms.getCache());
MappedStatement newMs = builder.build();
return newMs;
}
private static String buildKeyProperty(String[] props) {
if (null != props && props.length > 0) {
StringBuffer sb = new StringBuffer();
for (String p : props) {
sb.append(p).append(",");
}
return sb.substring(0, sb.length() - 1);
}
return null;
}
}
下面是:ReflectUtil
public class ReflectionUtil
{
private static final Log logger = LogFactory.getLog(ReflectionUtil.class);
public static void setFieldValue(Object object, String fieldName, Object value)
{
Field field = getDeclaredField(object, fieldName);
if (field == null) {
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
}
makeAccessible(field);
try
{
field.set(object, value);
}
catch (IllegalAccessException e)
{
}
}
public static Object getFieldValue(Object object, String fieldName)
{
Field field = getDeclaredField(object, fieldName);
if (field == null) {
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
}
makeAccessible(field);
Object result = null;
try
{
result = field.get(object);
}
catch (IllegalAccessException e)
{
}
return result;
}
public static Object invokeMethod(Object object, String methodName, Class>[] parameterTypes, Object[] parameters)
throws InvocationTargetException
{
Method method = getDeclaredMethod(object, methodName, parameterTypes);
if (method == null) {
throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + object + "]");
}
method.setAccessible(true);
try
{
return method.invoke(object, parameters);
}
catch (IllegalAccessException e)
{
}
return null;
}
protected static Field getDeclaredField(Object object, String fieldName)
{
for (Class superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
{
try
{
return superClass.getDeclaredField(fieldName);
}
catch (NoSuchFieldException e)
{
}
}
return null;
}
protected static void makeAccessible(Field field)
{
if ((!Modifier.isPublic(field.getModifiers())) || (!Modifier.isPublic(field.getDeclaringClass().getModifiers())))
{
field.setAccessible(true);
}
}
protected static Method getDeclaredMethod(Object object, String methodName, Class>[] parameterTypes)
{
for (Class superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
{
try
{
return superClass.getDeclaredMethod(methodName, parameterTypes);
}
catch (NoSuchMethodException e)
{
}
}
return null;
}
public static Class getSuperClassGenricType(Class clazz)
{
return getSuperClassGenricType(clazz, 0);
}
public static Class getSuperClassGenricType(Class clazz, int index)
{
Type genType = clazz.getGenericSuperclass();
if (!(genType instanceof ParameterizedType))
{
logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
return Object.class;
}
Type[] params = ((ParameterizedType)genType).getActualTypeArguments();
if ((index >= params.length) || (index < 0))
{
logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + params.length);
return Object.class;
}
if (!(params[index] instanceof Class))
{
logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
return Object.class;
}
return (Class)params[index];
}
public static IllegalArgumentException convertToUncheckedException(Exception e)
{
if (((e instanceof IllegalAccessException)) || ((e instanceof IllegalArgumentException)) || ((e instanceof NoSuchMethodException))) {
return new IllegalArgumentException("Refelction Exception.", e);
}
return new IllegalArgumentException(e);
}
public static Field getFieldByFieldName(Object obj, String fieldName)
{
for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
{
try
{
return superClass.getDeclaredField(fieldName);
}
catch (NoSuchFieldException e)
{
}
}
return null;
}
public static Object getValueByFieldName(Object obj, String fieldName)
throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException
{
Field field = getFieldByFieldName(obj, fieldName);
Object value = null;
if (field != null)
{
if (field.isAccessible())
{
value = field.get(obj);
}
else
{
field.setAccessible(true);
value = field.get(obj);
field.setAccessible(false);
}
}
return value;
}
public static void setValueByFieldName(Object obj, String fieldName, Object value)
throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException
{
Field field = obj.getClass().getDeclaredField(fieldName);
if (field.isAccessible())
{
field.set(obj, value);
}
else
{
field.setAccessible(true);
field.set(obj, value);
field.setAccessible(false);
}
}
}
参考:
http://blog.csdn.net/synsdeng/article/details/78561139
http://blog.csdn.net/flamingsky007/article/details/7195399
http://fred-han.iteye.com/blog/1771395
可以在github上看,作者对这个问题的回复
https://github.com/mybatis/mybatis-3/issues/1074
转自:https://www.cnblogs.com/chn58/p/8125705.html