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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package com.dy.common.mybatis;
 
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;
 
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
 
/**
 * 拦截执行SQL发生异常的场景,并将执行错误,执行SQL和参数打印出来
 * 拦截Executor里面的query和update方法
 */
@Intercepts({
        @Signature(
            method = "query",
            type = Executor.class,
            args = {
                MappedStatement.class,
                Object.class,
                RowBounds.class,
                ResultHandler.class
            }
        ),
        @Signature(
            type = Executor.class,
            method = "update",
            args = {
                MappedStatement.class,
                Object.class
            }
        )
})
@Slf4j
public class PrintExceptionSqlInterceptor implements Interceptor {
 
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取执行方法的MappedStatement参数
        Object[] args = invocation.getArgs();
        if(args != null && args.length > 1){
            MappedStatement mappedStatement = (MappedStatement) args[0];
            Object parameter = args[1];
 
            String sqlId = mappedStatement.getId();
            BoundSql boundSql = mappedStatement.getBoundSql(parameter);
            Configuration configuration = mappedStatement.getConfiguration();
            Object response;
            try {
                response = invocation.proceed();
            } catch (Exception e) {
                // 输出SQL异常信息
                log.error("SQL ErrorException:", e);
                log.error("SQL Id: {}", sqlId);
                log.error("SQL Parameters: {}", boundSql.getParameterObject());
                log.error("SQL: {}", PrintSqlHelp.getFullSql(configuration, boundSql));
                // 根据源异常类型进行返回
                if (e instanceof InvocationTargetException) {
                    throw new InvocationTargetException(e);
                } else if (e instanceof IllegalAccessException) {
                    throw new IllegalAccessException(e.getMessage());
                } else {
                    throw new RuntimeException(e);
                }
            }
            return response;
        }else{
            return invocation.proceed();
        }
    }
 
    /**
     * 通过该方法决定要返回的对象是目标对象还是对应的代理
     * 不要想的太复杂,一般就两种情况:
     * <p>
     * 1. return target;  直接返回目标对象,相当于当前Interceptor没起作用,不会调用上面的intercept()方法
     * 2. return Plugin.wrap(target, this);  返回代理对象,会调用上面的intercept()方法
     *
     * @param target 目标对象
     * @return 目标对象或者代理对象
     */
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
 
    /**
     * 用于获取在Configuration初始化当前的Interceptor时时候设置的一些参数
     *
     * @param properties Properties参数
     */
    @Override
    public void setProperties(Properties properties) {
    }
 
 
}