比如可以使用自定义插件来添加分页参数,或统计sql
的执行时间,实现方式为:
- 创建插件类并实现
Interceptor
接口
intercept()
:编写拦截逻辑,在目标方法前后执行自定义操作。plugin()
:返回目标对象的代理对象。setProperties()
:接收配置文件中的参数(可选)。
java
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.sql.Connection;
import java.util.Properties;
@Intercepts({
@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}
)
})
public class PaginationPlugin implements Interceptor {
private String dialect = "mysql"; // 默认数据库类型
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler handler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = SystemMetaObject.forObject(handler);
// 获取原始SQL和分页参数
BoundSql boundSql = handler.getBoundSql();
String originalSql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
// 检查是否包含分页参数
if (parameterObject instanceof Page<?>) {
Page<?> page = (Page<?>) parameterObject;
// 修改SQL添加分页
String paginatedSql = buildPaginationSql(originalSql, page);
metaObject.setValue("delegate.boundSql.sql", paginatedSql);
// 设置分页参数(LIMIT和OFFSET)
metaObject.setValue("delegate.boundSql.parameterObject", page.getData());
}
return invocation.proceed();
}
// 构建分页SQL
private String buildPaginationSql(String sql, Page<?> page) {
int offset = (page.getPageNum() - 1) * page.getPageSize();
if ("mysql".equals(dialect)) {
return sql + " LIMIT " + page.getPageSize() + " OFFSET " + offset;
} else if ("oracle".equals(dialect)) {
// Oracle分页逻辑(使用ROWNUM)
return "SELECT * FROM ( SELECT tmp.*, ROWNUM rn FROM (" + sql + ") tmp ) WHERE rn > "
+ offset + " AND rn <= " + (offset + page.getPageSize());
}
return sql;
}
@Override
public Object plugin(Object target) {
// 包装目标对象,返回代理
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 提取参数
this.dialect = properties.getProperty("dialect", "mysql");
}
}
- 使用
@Intercepts
注解指定拦截目标
@Signature
注解定义要拦截的类、方法及参数类型。例如:
type
:拦截的接口(如Executor
、StatementHandler
)。method
:方法名。args
:方法的参数类型数组。
- 注册插件到
MyBatis
配置
xml
<plugins>
<plugin interceptor="com.example.PaginationPlugin">
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
mapper
层使用示例
less
public interface UserMapper {
@Pageable
List<User> selectByCondition(@Param("page") Page<User> page, @Param("name") String name);
}
注意事项:
- 仅支持拦截四大接口 :
Executor
、StatementHandler
、ParameterHandler
、ResultSetHandler
。 - 方法签名匹配 :需确保
@Signature
中的参数类型与目标方法完全一致。 - 性能影响:过度使用插件可能影响性能,建议仅在必要时使用。