mybatis(spring boot集成mybatis,拦截器实现动态sql)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis(spring boot集成mybatis,拦截器实现动态sql)相关的知识,希望对你有一定的参考价值。
程序员都是些折了翼的天使
来看看我们这个项目中是如何对mybatis动态生成sql进行改进的吧
spring boot在配置了MapperScan之后会自动扫描相关的包,并对有@Mapper标记的类进行注册
@Configuration
@EnableAutoConfiguration
@ServletComponentScan
@ComponentScan("xxxx.scm","xxxxx.base")
@MapperScan("xxxxx.dao","xxxxx.mapper")
public class Application
/**
* 主函数入口
*
* @param args 参数
*/
public static void main(String[] args)
Security.addProvider(new BouncyCastleProvider());
SpringApplication.run(Application.class, args);
在dao中可以预定义基础类,比如拦截器、基础Mapper、基础Provider等。
//拦截器
@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),
@Signature(type = Executor.class, method = "update", args = MappedStatement.class, Object.class),
)
@Component
public class BaseInterceptor implements Interceptor
@Override
public Object intercept(Invocation invocation) throws Throwable
StatementUtils.genCommonStatement((MappedStatement)(invocation.getArgs()[0])); // 生成通用查询语句
return invocation.proceed();
@Override
public Object plugin(Object o)
return Plugin.wrap(o, this);
@Override
public void setProperties(Properties properties)
这个是mybatis拦截器的标准写法,实现Interceptor接口的三个方法。可以拦截的接口一共有四种,用@Signature注解进行标记。因为Executor接口的query方法有两种不同的实现方式,所以需要拦截两种。具体拦截的内容实现放在StatementUtils中,避免因代码过长而产生维护上面的困难。
public static synchronized void genCommonStatement(MappedStatement mappedStatement)
Configuration configuration = mappedStatement.getConfiguration();//取出当前sql语句的Configuration
String msId = mappedStatement.getId();//取出当前msid
int lastIndex = msId.lastIndexOf(".");
String methodName = msId.substring(lastIndex + 1);//获取当前sql的方法名
String interfaceName = msId.substring(0, lastIndex);//获取接口名(Mapper)
Class<?> mapperClass = null;
try
mapperClass = Class.forName(interfaceName);
catch (Exception ex)
ex.printStackTrace();
Class entityClass = findEntityFromMapper(mapperClass);//获取实体
List<SqlNode> sqlNodes = null;
// 替换 ResultMap
setResultMap(entityClass, mappedStatement);
// 替换通用查询语句的 sqlSource
switch (methodName)
case "add":
sqlNodes = getInsert(entityClass, mappedStatement);
break;
case "update":
sqlNodes = getUpdate(entityClass, mappedStatement);
break;
case "find":
sqlNodes = getSelect(entityClass, mappedStatement, false);
case "findOne":
sqlNodes = getSelect(entityClass, mappedStatement, true);
break;
case "delete":
sqlNodes = getDelete(entityClass, mappedStatement);
break;
case "count":
sqlNodes = getCount(entityClass, mappedStatement);
break;
default:
break;
// TODO 注入通用参数
// TODO 注入用户自定义参数
if (sqlNodes == null)
return;
DynamicSqlSource dynamicSqlSource = new DynamicSqlSource(configuration, new MixedSqlNode(sqlNodes));
MetaObject metaObject = SystemMetaObject.forObject(mappedStatement);
metaObject.setValue("sqlSource", dynamicSqlSource);
这段代码通过使用反射技术,从传入参数中拿到sqlnode,然后根据条件对sqlnode进行重构,然后重新放入到被拦截的执行语句中。从而实现了动态生成sql。值得一提的是,如果sql事先不存在,将会产生空指针异常的错误。由于生产环境是并发环境,所以该方法是一个同步方法,有点消耗性能。不过,由于同一时间被拦截语句可能有多条,这么做也是为了避免插入混乱的情况。如果后期数据存取方面性能有所影响,这里需要修改。为了避免空指针异常的问题,需要给生成默认的sql,拦截器只能修改不能无中生有,这是需要注意的一点。
用拦截器动态生成sql的缺点:不是所有的情况都很简单,有些情况比较复杂,无法和普通情况下的一起进行拦截
以上是关于mybatis(spring boot集成mybatis,拦截器实现动态sql)的主要内容,如果未能解决你的问题,请参考以下文章