Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
This article is mainly about the MyBaits Interceptor expansion point for MyBatis before the execution of SQL to do a logical interception implementation of custom logic insertion execution. Applicable scenarios: 1. For example, limit the maximum number of database query entries; 2. Restrict the login user to access only the current organization data.
Defines whether annotations are enabled
One of the main things you do to define whether to enable annotations is whether to add an SQL interceptor.
// Enable globally
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyBatisSqlInterceptorConfiguration.class)
public @interface EnableSqlInterceptor {
}
// Custom annotations
@Target({ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DataScope {
}
Copy the code
Register SQL interceptors
Register an SQL interceptor that intercepts SQL query operations that meet the criteria.
public class MyBatisSqlInterceptorConfiguration implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SqlSessionFactory sqlSessionFactory = applicationContext.getBean(SqlSessionFactory.class);
sqlSessionFactory.getConfiguration().addInterceptor(newMyBatisInterceptor()); }}Copy the code
Processing logic
In dealing with logic, I mainly made a simple case of limit 1. If I need to do other logic, I need to modify it
@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;
// Only one entry will be entered due to logic
if (args.length == 4) {
//4 arguments
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 the new SQL
// todo you weaving business
String newSql = origSql + " limit 1";
// Re-create a query statement object
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql,
boundSql.getParameterMappings(), boundSql.getParameterObject());
// Place the new query in statement
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 :{}", 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 the 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);
}
/** * defines an internal helper class that wraps SQL */
class BoundSqlSource implements SqlSource {
private BoundSql boundSql;
public BoundSqlSource(BoundSql boundSql) {
this.boundSql = boundSql;
}
public BoundSql getBoundSql(Object parameterObject) {
returnboundSql; }}}Copy the code
How to use
The corresponding data manipulation methods in XXXMapper can be done by adding @datascope annotations.
@Mapper
public interface OrderMapper {
@Select("select 1 ")
@DataScope
Intger selectOne(a);
}
Copy the code
The resources
- Mybatis.org/mybatis-3/z…