Mybatis has its own SQL print, but will only appear when throwing exceptions

Like this,

### Error querying database. Cause: java.lang.ArithmeticException: / by zero ### The error may exist in file [/usr/local/userMapper.xml] ### The error may involve com.xxx.xxx.UserMapper.query_COUNT ### The error occurred while handling results ### SQL: SELECT count(0) FROM (SELECT id, username FROM user) table_count ### Cause: java.lang.ArithmeticException: / by zero at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) ~ [mybatis - 3.5.6. Jar: 3.5.6]Copy the code

Turned over the source code, found this method, paste it

@Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); }}Copy the code

Including ExceptionFactory. WrapException (” Error querying the database. The Cause: “+ e, e);

As the name suggests, wrap exceptions

Point into the way

 public static RuntimeException wrapException(String message, Exception e) {
    return new PersistenceException(ErrorContext.instance().message(message).cause(e).toString(), e);
}
Copy the code

The key is the toString() of errorContext.instance ()

Part of the code for this method

// sql if (sql ! = null) { description.append(LINE_SEPARATOR); description.append("### SQL: "); description.append(sql.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').trim()); }Copy the code

As you can also see from the errorContext.instance () method, this class maintains a ThreadLocal that keeps the SQL thread safe

This leads to the following interceptor code

@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}), } ) @Log4j2 public class MybatisSqlLogInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object proceed = invocation.proceed(); try { Field field = ErrorContext.class.getDeclaredField("sql"); field.setAccessible(true); Object sql = field.get(ErrorContext.instance()); if (sql ! = null) { System.out.println("mybatis sql: " + sql.toString()); }} catch (Exception e) {log.error(" SQL print failed ", e); } return proceed; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } }Copy the code

Execute query, SQL print, done

If you want to fill in the parameters as well

You could write it this way

@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}), } ) @Log4j2 public class MybatisSqlLogInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object proceed = invocation.proceed(); / / retrieve a select in the XML/update/insert/delete nodes, MappedStatement MappedStatement = (MappedStatement) Invocation. GetArgs ()[0]; Object parameter = null; If (Invocation.getargs ().length > 1) {parameter = Invocation.getargs ()[1]; } / / BoundSql is encapsulated myBatis eventually produce SQL class BoundSql BoundSql = mappedStatement. GetBoundSql (parameter); / / obtain the node Configuration Configuration Configuration = mappedStatement. GetConfiguration (); String SQL = showSql(configuration, boundSql); System.out.println("mybatis sql: " + sql); return proceed; } /** * if the argument is String, add single quotes; * if the argument is date, convert to time formatter and add single quotes; */ private static String getParameterValue(Object obj) {String value; if (obj instanceof String) { value = "'" + obj.toString() + "'"; } else if (obj instanceof Date) { DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA); value = "'" + formatter.format(new Date()) + "'"; } else { if (obj ! = null) { value = obj.toString(); } else { value = ""; } } return value; } /** *? * * @param boundSql * @return */ public String showSql(Configuration configuration, BoundSql boundSql) throws NoSuchFieldException, IllegalAccessException {/ / to get parameters of the Object parameterObject = boundSql. GetParameterObject (); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); String SQL = getRealSql(). ReplaceAll ("[\\s]+", ""); if (parameterObject ! = null) {// Get the type handler register, Type the function of the processor is to convert the Java types and database type TypeHandlerRegistry TypeHandlerRegistry = configuration. GetTypeHandlerRegistry (); Parameterobject.getclass () ¶ Is to replace the if (typeHandlerRegistry hasTypeHandler (parameterObject getClass ())) {SQL = SQL. ReplaceFirst (" \ \?" , Matcher.quoteReplacement(getParameterValue(parameterObject))); } else {// MetaObject encapsulates originalObject, The get and set methods are used to obtain and set the property values of an originalObject. MetaObject MetaObject = supports operations on Javabeans, Collections, and Maps configuration.newMetaObject(parameterObject); for (ParameterMapping parameterMapping : parameterMappings) { String propertyName = parameterMapping.getProperty(); if (metaObject.hasGetter(propertyName)) { Object obj = metaObject.getValue(propertyName); sql = sql.replaceFirst("\\?" , Matcher.quoteReplacement(getParameterValue(obj))); } else if (boundSql hasAdditionalParameter (propertyName)) {/ / the branch is dynamic SQL Object obj = boundSql.getAdditionalParameter(propertyName); sql = sql.replaceFirst("\\?" , Matcher.quoteReplacement(getParameterValue(obj))); SQL = sql.replaceFirst("\\?"); , "missing "); } } } } return sql; } /** * get real SQL, Mybatis stores it in ThreadLocal * @return * @throws IllegalAccessException * @throws NoSuchFieldException */ private String getRealSql() throws IllegalAccessException, NoSuchFieldException { Field field = ErrorContext.class.getDeclaredField("sql"); field.setAccessible(true); Object sql = field.get(ErrorContext.instance()); if (sql ! = null) { return sql.toString(); } return ""; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } }Copy the code