前些日子遇到一個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<String, Object> map = (Map<String, Object>) 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<String, Object> map = (Map<String, Object>) 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<String, Object> map = (Map<String, Object>) 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<ParameterMapping> 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 <T> Class<T> 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