使用拦截器
Web开发中我们经常会碰到分页操作,一个项目中或许有多处使用到分页,这时如果Java后台使用MyBatis作为持久层,我们就可以使用MyBatis的拦截器功能来完成整个项目中多处的分页操作,减少代码的冗余。
拦截器代码:
//拦截StatementHandler中参数类型为Connection的prepare方法
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
public class PageInterceptor implements Interceptor {
private String test; // 获取xml中配置的属性
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
//通过MetaObject优雅访问对象的属性,这里是访问statementHandler的属性
MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
//先拦截到RoutingStatementHandler,里面有个StatementHandler类型的delegate变量,其实现类是BaseStatementHandler,然后就到BaseStatementHandler的成员变量mappedStatement
MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
// 配置文件中SQL语句的ID
String id = mappedStatement.getId();
if(id.matches(".+ByPage$")) { //需要拦截的ID(正则匹配)
BoundSql boundSql = statementHandler.getBoundSql();
// 原始的SQL语句
String sql = boundSql.getSql();
// 查询总条数的SQL语句
String countSql = "select count(*) from (" + sql + ")a";
//执行总条数SQL语句的查询
Connection connection = (Connection)invocation.getArgs()[0];
PreparedStatement countStatement = connection.prepareStatement(countSql);
////获取参数信息即where语句的条件信息,注意上面拿到的sql中参数还是用?代替的
ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler");
parameterHandler.setParameters(countStatement);
ResultSet rs = countStatement.executeQuery();
Map<?,?> parameter = (Map<?,?>)boundSql.getParameterObject();
Page page = (Page)parameter.get("page");
if(rs.next()) {
page.setTotalNumber(rs.getInt(1));
}
// 改造后带分页查询的SQL语句
String pageSql = sql + " limit " + page.getDbIndex() + "," + page.getDbNumber();
metaObject.setValue("delegate.boundSql.sql", pageSql);
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
System.out.println(this.test);
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
this.test = properties.getProperty("test");
// TODO Auto-generated method stub
}
}
MetaObject是Mybatis提供的一个用于方便、优雅访问对象属性的对象,通过它可以简化代码、不需要try/catch各种reflect异常,同时它支持对JavaBean、Collection、Map三种类型对象的操作。获取MetaObject对象需要使用静态方法MetaObject.forObject,并且需要指定ObjectFactory , ObjectWrapperFactory , ReflectorFactory(3.3.0之前不需要)。
配置拦截器:
<plugins>
<plugin interceptor="com.chm.inteceptor.PageInterceptor">
<property name="test" value="abc"/>
</plugin>
</plugins>
这里配置test的值为abc,那么上面拦截器中test的就会被赋值为abc。
查询SQL:
<select id="queryMessageListByPage" parameterType="java.util.Map" resultMap="MessageResult">
select <include refid="columns"/> from MESSAGE
<where>
<if test="message.command != null and !"".equals(message.command.trim())">
and COMMAND=#{message.command}
</if>
<if test="message.description != null and !"".equals(message.description.trim())">
and DESCRIPTION like '%' #{message.description} '%'
</if>
</where>
order by ID
</select>
<sql id="columns">ID,COMMAND,DESCRIPTION,CONTENT</sql>
调用示例:
/**
* 根据查询条件分页查询消息列表
*/
public List<Message> queryMessageListByPage(String command,String description,Page page) {
Map<String,Object> parameter = new HashMap<String, Object>();
// 组织消息对象
Message message = new Message();
message.setCommand(command);
message.setDescription(description);
parameter.put("message", message);
parameter.put("page", page);
MessageDao messageDao = new MessageDao();
// 分页查询并返回结果
return messageDao.queryMessageListByPage(parameter);
}