c语言编程笔录

首页 >   > 笔记大全

笔记大全

如何使用SpringAop动态获取mapper执行的SQL并保存SQL到Log表中

更新时间:2023-10-30

什么是Spring AOP?

Spring AOP (Aspect-Oriented Programming)是Spring Framework中的一个重要特性,它是基于AOP思想实现的。它提供了一种使应用程序代码横向切割对应关注点的方式,主要用于统一处理应用程序中的横切关注点(Crosscutting Concerns)。

Spring AOP采用代理模式实现对JavaBean的增强,可以在程序员定义的位置(连接点)织入增强代码(advise),而不需要代码动态修改。

// 示例一:定义一个切面
public aspect LogAspect{
    //切点,可以根据需要设置不同的切点
    pointcut executeDao(): execution(* com.example.*.mapper.*.*(..));
    //advice,定义在切点执行前后需要增强的操作,例如打印SQL语句
    before():executeDao(){
        Signature sig = thisJoinPointStaticPart.getSignature();
        String className = sig.getDeclaringType().getName();
        String methodName = sig.getName();
        String sql = "";
        Object[] args = thisJoinPoint.getArgs();
        if(args != null && args.length>0 && args[0] instanceof org.apache.ibatis.mapping.BoundSql){
            org.apache.ibatis.mapping.BoundSql boundSql = (org.apache.ibatis.mapping.BoundSql)args[0];
            sql = boundSql.getSql();
            System.out.println("[LogAspect] "+className+"."+methodName+"() method SQL:"+sql);
            //此处可以将SQL插入到数据库或者日志文件中
        }
    }
}

什么是Mybatis?

Mybatis是一款优秀的开源持久层框架,它支持定制化SQL、存储过程以及高级映射。Mybatis避免了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装,使得开发人员可以专注于SQL的开发本身而不是对象的映射。

对于Mybatis框架,主要的配置文件是mybatis-config.xml,映射文件一般是以Mapper.xml结尾。



    
        
    

如何在Mybatis加载插件?

Mybatis提供了插件机制,在其中可以通过自定义Interceptor拦截StatementHandler、ParameterHandler、ResultSetHandler、Executor等对象的方法调用。

// 示例三:定义一个Mybatis插件
@Intercepts({
    @Signature(
        type=StatementHandler.class,
        method="prepare",
        args={Connection.class, Integer.class}
    )
})
public class MybatisPlugin implements Interceptor{
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        //检索到日志的方式与示例一类似,不再重复
        return invocation.proceed();
    }
    //在Mybatis配置文件中配置插件
    
        
    
}

如何整合上述功能?

我们可以结合示例一和示例三实现保存SQL语句的功能:

// 示例四:整合Spring AOP和Mybatis插件
@Aspect
@Component
public class LogAspect {
    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;

    @Pointcut("execution(* com.example.*.mapper.*.*(..))")
    public void executeDao(){}

    @Before("executeDao()")
    public void doBefore(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        String className = signature.getDeclaringType().getSimpleName();
        String methodName = signature.getName();
        Object[] args = joinPoint.getArgs();
        if(args != null && args.length>0 && args[0] instanceof org.apache.ibatis.mapping.BoundSql){
            org.apache.ibatis.mapping.BoundSql boundSql = (org.apache.ibatis.mapping.BoundSql)args[0];
            String sql = boundSql.getSql();
            String mapperName = className + "." + methodName;
            String sessionId = SqlSessionUtils.getSqlSession(sqlSessionTemplate.getSqlSessionFactory(), sqlSessionTemplate.getExecutorType(), sqlSessionTemplate.getPersistenceExceptionTranslator()).toString();
            //将SQL插入到数据库表中
            sqlSessionTemplate.insert("com.example.mapper.LogMapper.insertLog", new Log(sessionId, mapperName, sql));
        }
    }
}

//Log类定义
public class Log {
    private String sessionId; //每次sqlSession的唯一标识
    private String mapperName; //Mapper的完整类名和方法名
    private String sql; //mapper执行的SQL语句

    public Log(String sessionId, String mapperName, String sql) {
        this.sessionId = sessionId;
        this.mapperName = mapperName;
        this.sql = sql;
    }

    //getter and setter...
}

//log表的DDL
CREATE TABLE log (
	id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
	session_id varchar(64) NOT NULL COMMENT '每次sqlSession的唯一标识',
	mapper_name varchar(256) NOT NULL COMMENT 'Mapper的完整类名和方法名',
	sql_statement varchar(1024) NOT NULL COMMENT 'mapper执行的SQL语句',
	create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
	PRIMARY KEY (id),
	INDEX idx_create_time (create_time) USING BTREE COMMENT '根据时间戳为日志表建立索引'
) COMMENT='系统SQL执行日志表';

总结

在本文中,我们介绍了如何使用Spring AOP和Mybatis插件实现动态获取mapper执行的SQL并保存SQL到Log表中。通过定义切面和插件,可以实现对Mybatis进行拓展和增强,达到日志记录的目的。需要注意的是,日志记录应该避免计算敏感信息(例如用户密码等),同时应该加强数据安全(例如使用加密算法加密敏感信息)。