MyBatis Custom SQL Interceptor Example Explanation
- 2021-12-04 10:05:21
- OfStack
Preface
This article mainly talks about the MyBaits Interceptor extension point to MyBatis before SQL to do a logic interception to achieve custom logic insertion execution.
Suitable scenarios: 1. For example, limit the maximum number of accesses to database queries; 2. Restrict the login user's access to the current organization data.
Defines whether to turn on annotations
To define whether to open annotations, the main thing to do is whether to add SQL interceptor.
// Global opening
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyBatisSqlInterceptorConfiguration.class)
public @interface EnableSqlInterceptor {
}
// Custom annotations
@Target({ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DataScope {
}
Register SQL Interceptor
Registering an SQL interceptor will intercept eligible SQL query operations.
public class MyBatisSqlInterceptorConfiguration implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SqlSessionFactory sqlSessionFactory = applicationContext.getBean(SqlSessionFactory.class);
sqlSessionFactory.getConfiguration().addInterceptor(new MyBatisInterceptor());
}
}
Processing logic
In the processing logic, I mainly do a simple limit 1 case, if it is their own need to do other logic needs to be modified
@Slf4j
@Intercepts(
{
@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 MyBatisInterceptor implements Interceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(MyBatisInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
// TODO Auto-generated method stub
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];
Executor executor = (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
// Because of the logical relationship, it will only enter 1 Times
if (args.length == 4) {
//4 Parameters
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
//6 Parameters
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
DataScope dataScope = getDataScope(ms);
if (Objects.nonNull(dataScope)) {
String origSql = boundSql.getSql();
log.info("origSql : {}", origSql);
// Assemble a new sql
// todo you weaving business
String newSql = origSql + " limit 1";
// Re- new1 Query statement object
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql,
boundSql.getParameterMappings(), boundSql.getParameterObject());
// Put the new query in the statement Li
MappedStatement newMs = newMappedStatement(ms, new BoundSqlSource(newBoundSql));
for (ParameterMapping mapping : boundSql.getParameterMappings()) {
String prop = mapping.getProperty();
if (boundSql.hasAdditionalParameter(prop)) {
newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
}
}
args[0] = newMs;
if (args.length == 6) {
args[5] = newMs.getBoundSql(parameter);
}
}
LOGGER.info("mybatis intercept sql:{},Mapper The method is: {}", boundSql.getSql(), ms.getId());
return invocation.proceed();
}
private MappedStatement newMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
MappedStatement.Builder builder = new
MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) {
builder.keyProperty(ms.getKeyProperties()[0]);
}
builder.timeout(ms.getTimeout());
builder.parameterMap(ms.getParameterMap());
builder.resultMaps(ms.getResultMaps());
builder.resultSetType(ms.getResultSetType());
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
}
private DataScope getDataScope(MappedStatement mappedStatement) {
String id = mappedStatement.getId();
// Get Class Method
String clazzName = id.substring(0, id.lastIndexOf('.'));
String mapperMethod = id.substring(id.lastIndexOf('.') + 1);
Class<?> clazz;
try {
clazz = Class.forName(clazzName);
} catch (ClassNotFoundException e) {
return null;
}
Method[] methods = clazz.getMethods();
DataScope dataScope = null;
for (Method method : methods) {
if (method.getName().equals(mapperMethod)) {
dataScope = method.getAnnotation(DataScope.class);
break;
}
}
return dataScope;
}
@Override
public Object plugin(Object target) {
// TODO Auto-generated method stub
LOGGER.info("MysqlInterCeptor plugin>>>>>>>{}", target);
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
String dialect = properties.getProperty("dialect");
LOGGER.info("mybatis intercept dialect:>>>>>>>{}", dialect);
}
/**
* Definition 1 Internal helper classes, which are used to wrap SQL
*/
class BoundSqlSource implements SqlSource {
private BoundSql boundSql;
public BoundSqlSource(BoundSql boundSql) {
this.boundSql = boundSql;
}
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}
}
How to use
We only need to add @ DataScope annotation to the corresponding data operation method in XXXMapper.
@Mapper
public interface OrderMapper {
@Select("select 1 ")
@DataScope
Intger selectOne();
}
References
mybatis. org/mybatis-3/z …