问题现象
mybatis的org.apache.ibatis.session.RowBounds类含有offset和limit属性,限制从数据库结果集的哪一行开始,最多返回多少行。
但在mapper接口的方法中,如果方法被SelectProvider注解,传递RowBounds及其子类型的参数会出错。
示例:
mapper接口的代码如下,其中selectManyUsers方法用SelectProvider注解,并且含有一个RowBounds 类型的参数:
package com.thb.mapper;
import java.util.List;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.jdbc.SQL;
import org.apache.ibatis.session.RowBounds;
import com.thb.model.User;
public interface UserMapper {
@Results(value = {
@Result(property = "id", column = "id", id = true),
@Result(property = "userName", column = "user_name"),
@Result(property = "homeTown", column = "home_town"),
@Result(property = "type", column = "type"),
})
@SelectProvider(type = UserSqlBuilder.class, method = "selectManyUsers")
List<User> selectManyUsers(RowBounds rowBounds);
public static class UserSqlBuilder {
public static String selectManyUsers(final RowBounds rowBounds) {
return new SQL() {{
SELECT("id, user_name, home_town, type");
FROM("user");
WHERE("type = #{type}");
//WHERE("home_town = #{homeTown}");
OFFSET(rowBounds.getOffset());
LIMIT(rowBounds.getLimit());
}}.toString();
}
}
}
调用代码:
// 取得一个SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
RowBounds rowBounds = new RowBounds(0, 1);
List<User> users = userMapper.selectManyUsers(rowBounds);
for (User user : users) {
System.out.println("*".repeat(60));
System.out.println("id = " + user.getId());
System.out.println("userName = " + user.getUserName());
System.out.println("homeTown = " + user.getHomeTown());
System.out.println("type = " + user.getType());
}
运行报错,提示参数为null,引用的时候出现了空指针异常:
原因分析
用debug模式,通过堆栈分析,发现mybatis在处理的过程中,会检查方法有没有RowBounds及其子类型的参数,有的话会单独提取出来,没有传递给sql provider的方法,所以在sql provider的方法内无法使用RowBounds及其子类型的参数的值。
执行到CachingExecutor的query方法的时候,RowBounds参数的值还是存在的:
将RowBounds及其子类型的参数提取出来:
调用sql privider方法的时候,没有传递RowBounds及其子类型的参数:
参数为null:
参数为null:
等执行到sql provider的方法的时候,RowBounds类型的参数为null,所以在方法内使用抛出空指针异常:
解决方法
可以在sql provider的方法中,使用两个整型参数offset和limit,绕过这个问题。
或者自己写一个类,里边有两个属性,分别是offset和limit。但注意自己写的类不要继承RowBounds。