MyBatis插件必须实现Interceptor接口,仅能拦截Executor、StatementHandler、ParameterHandler、ResultSetHandler四大对象;优先选StatementHandler修改SQL,用MetaObject而非字符串拼接;需Plugin.wrap注册,XML中写全限定名;分页参数须通过ThreadLocal传递,Executor拦截适合统计总数,SQL重写需按数据库方言分支处理。
MyBatis插件必须实现
接口,且只能拦截四大对象之一
MyBatis插件不是随便拦SQL的——它底层靠JDK动态代理,只允许对
、
、
、
这四个对象生效。想改SQL或加
,必须选
(拼SQL前)或
(执行前)。选错对象,插件根本不会触发。
常见错误是直接在
里解析
然后字符串拼
——这不行,因为原始SQL可能带
、子查询、甚至分号,硬拼会语法报错。
优先拦截
,用
修改
内部字段,而不是字符串替换
必须调用
完成代理包装,否则Spring Boot里插件不注册
XML中
配置的
类名要写全限定名,少一个包名就静默失效
分页参数不能从方法签名硬取,得靠
或上下文传递
MyBatis插件拿不到Mapper接口的方法参数,
里只有
和
(后者可能是
)。所以
这类调用,本质是把分页信息塞进
,插件里再取出来。
自己写时别学网上“从
强转Map取pageNo”——那是特定封装框架的hack,原生MyBatis没这约定。
立即学习
“
Java免费学习笔记(深入)
”;
定义
业务代码调用前手动
,插件
里取,用完
防内存泄漏
如果用Spring,注意事务方法内多线程调用会导致
丢失,得换
或显式传参
拦截比
更安全,但要注意
方法重载
拦截
的
看起来简单,但MyBatis 3.4+新增了带
的重载方法,漏掉一个就会跳过插件。而且
默认是
(
),不代表用户没分页需求——很多场景下业务压根没传
,全靠插件自动补。
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
真正要改SQL,还是得穿透到
,但可以在
里先判断是否需要分页(比如检查
非空 +
)。
用
声明多个
,覆盖所有
重载
不要在
里直接改
,因为
在
创建,此时还没生成
拦截
适合做统计总数(
自动追加),但改SQL必须进
MySQL的
和Oracle的
不能共用一套SQL模板
写个通用分页插件时最容易翻车:用
应付所有数据库。这在Oracle、SQL Server、PostgreSQL上全错——Oracle要
嵌套,SQL Server要
,PostgreSQL虽支持
但偏移量语义和MySQL不同。
MyBatis本身不解决方言问题,
只能帮你区分数据库类型,具体SQL构造还得自己分支处理。
在插件里通过
判断,分支写
、
Oracle分页必须两层嵌套:
别信“用
兼容所有库”的博客,那是MySQL专用写法,其他库运行时报
或
分页插件最难的从来不是拦截,而是SQL重写时对目标数据库语法边界的准确把握——少一个括号、错一个别名、漏一个
条件,查出来的数据就少一半或者全乱序。
InterceptorExecutorStatementHandlerParameterHandlerResultSetHandlerLIMITStatementHandlerExecutorintercept()boundSql.getSql()" LIMIT ?,?"ORDER BYStatementHandlerMetaObjectboundSqlPlugin.wrap(target, this)interceptorThreadLocalinvocation.getArgs()MappedStatementObjectnullPageHelper.startPage(1, 10)ThreadLocalargs[1]private static final ThreadLocal PAGE_HOLDER = new ThreadLocal<>() PAGE_HOLDER.set(new Page(1, 20))intercept()remove()ThreadLocalRequestContextHolderExecutorStatementHandlerqueryExecutorquery(MappedStatement, Object, RowBounds, ResultHandler)ResultHandlerRowBoundsRowBounds.DEFAULToffset=0, limit=0RowBoundsStatementHandlerExecutorThreadLocalmappedStatement.getSqlCommandType() == SqlCommandType.SELECTPlugin.signature()@SignaturequeryExecutorboundSqlboundSqlStatementHandlerExecutorcount(*)StatementHandlerLIMITROWNUMString.format("SELECT * FROM (%s) t LIMIT %d, %d", originSql, offset, size)ROWNUMOFFSET-FETCHLIMITDatabaseIdProviderconfiguration.getDatabaseId()if ("mysql".equals(dbId))else if ("oracle".equals(dbId))SELECT * FROM (SELECT ROWNUM rn, t1.* FROM (originSql) t1 WHERE ROWNUM <= ?) WHERE rn > ?limitORA-00933SQLServerExceptionWHERE ROWNUM <=