插件原理
MybatisPlus的插件是基于mybatis的intercepter扩展来的,其本质是在mybatis的一个插件维护一组自己的插件组,并挨个执行
其主类为MybatisPlusInterceptor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Intercepts( { @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}), @Signature(type = StatementHandler.class, method = "getBoundSql", args = {}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), } ) public class MybatisPlusInterceptor implements Interceptor { @Setter private List<InnerInterceptor> interceptors = new ArrayList<>();
@Override public Object intercept(Invocation invocation) throws Throwable { } @Override public Object plugin(Object target) {
} public void addInnerInterceptor(InnerInterceptor innerInterceptor) { this.interceptors.add(innerInterceptor); } }
|
从注解上可以看的出来,它将mybatis的所有预留方法都拦截了,然后通过一些列逻辑封装调用自己的接口InnerInterceptor
的相关方法实现Mybatisplus自己的扩展
再来看InnerInterceptor
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| public interface InnerInterceptor {
default boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { return true; }
default void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { }
default boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException { return true; }
default void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException { }
default void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { }
default void beforeGetBoundSql(StatementHandler sh) { } default void setProperties(Properties properties) { } }
|
根据描述自定义自己的方法即可,中文注释就是好,一眼就看懂了
所以我们自定义插件主要关注InnerInterceptor
相关的类和方法就好
插件使用
从以上知道,Mybatisplus主要是MybatisPlusInterceptor
这个类扩展,那么我们扩展的时候首先要把它作为bean注入到容器里,然后再它身上添加InnerInterceptor
1 2 3 4 5 6 7 8 9 10 11
| @Configuration public class PluginConfig {
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor plusInterceptor = new MybatisPlusInterceptor(); return plusInterceptor; } }
|
Mybatisplus自带提供了相应的插件,可以按需引入
- 自动分页: PaginationInnerInterceptor
- 多租户: TenantLineInnerInterceptor
- 动态表名: DynamicTableNameInnerInterceptor
- 乐观锁: OptimisticLockerInnerInterceptor
- SQL 性能规范: IllegalSQLInnerInterceptor
- 防止全表更新与删除: BlockAttackInnerInterceptor
分页插件
Mybatisplus的分页插件是逻辑分页,即把数据查询回来之后再分页,这样需要注意性能问题,因为实际上数据库查询阶段会查询所有的数据,在大量数据面前并不友好
1 2
| plusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
|
租户插件
租户插件主要是用来改写SQL添加租户字段
主要实现TenantLineHandler
添加租户相关定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Component public class CustomTenantHandler implements TenantLineHandler {
@Override public Expression getTenantId() { Long tenantId = TenantContextHolder.getCurrentTenantId(); return new LongValue(tenantId);; } @Override public String getTenantIdColumn() { return "tenant_id"; } @Override public boolean ignoreTable(String tableName) { return false; } }
|
然后注入到插件主体中
1 2 3 4 5 6 7 8 9 10 11
| @Autowired private CustomTenantHandler customTenantHandler;
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor(); tenantInterceptor.setTenantLineHandler(customTenantHandler); interceptor.addInnerInterceptor(tenantInterceptor); return interceptor; }
|
多数据源插件