格式化打印mybatis的SQL语句

痛点:默认的mybatis打印的SQL语句格式不带参数,使用占位符代替,有==》等多余字符,不可定制。影响阅读(个人觉得)。

解决:打印的mybatis的SQL语句根个人在图形化工具中写的一样。可自定义输出的格式。

先介绍一下我的环境,传统的SSM项目、日志打印使用的logback。

格式化mybatis打印SQL语句步骤:

1、导入jar包   通过maven的形式添加依赖

<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.7.0</version>
</dependency>

2、编写配置p6spy的配置文件:spy.properties

logMessageFormat:指向的是自己定义的日志格式类,下面有我的一个使用案例,可以参考使用

其他配置使用默认就好,具体配置的含义文末附上,或可以参考给出的参考文章

reloadproperties=true
appender=com.p6spy.engine.spy.appender.Slf4JLogger
#P6SpyLogger \u7C7B\u5168\u8DEF\u5F84\u540D
logMessageFormat=com.ciitc.utils.P6SpyLogger
databaseDialectDateFormat=yyyy-MM-dd hh:mm:ss
excludecategories=info,debug,result,resultset

3、在配置数据源的配置文件中添加:

bean:数据源对象的id

id:其他需要使用数据源时使用此ID  感觉就是p6spy将数据源进行了包装

<bean id="dataSource" class="com.p6spy.engine.spy.P6DataSource">
    	<constructor-arg>
			<ref bean="dataSourceDefault"/>    	
    	</constructor-arg>
</bean>

4、创建属于自己的日志格式类:可参考使用

此类就是填补第二点需要自定义日志格式的类的坑

MySQLFormatter:自定义的格式SQL语句的类,其实是借鉴另一篇文章从hibernate中抽取出来的类(hibernate用来格式化日志的类),随后附上

package com.ciitc.utils;

import com.p6spy.engine.spy.appender.MessageFormattingStrategy;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * p6spy打印日志输出格式修改 1.只打印最终执行的sql. 2.sql换到下一行 3.结尾处增加分号,以标示sql结尾 Created by
 * odelia on 2016/1/4.
 */
public class P6SpyLogger implements MessageFormattingStrategy {
	private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");

	public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared,
			String sql) {
		if (!sql.trim().equals("")) {
			return format.format(new Date()) + " | took " + elapsed + "ms | " + category + " | connection "
					+ connectionId + new MySQLFormatter().format(sql) + ";";
			
		}
		return "";
	}
}

5、MySQLFormatter:格式化SQL的类

package com.ciitc.utils;
 
import java.util.HashSet;  
import java.util.LinkedList;  
import java.util.Set;  
import java.util.StringTokenizer;  

/**
 * 
 * sql格式化工具(从hibernate中弄出来的)
 */
public class MySQLFormatter {  
  
    private static final Set<String> BEGIN_CLAUSES = new HashSet<String>();  
    private static final Set<String> END_CLAUSES = new HashSet<String>();  
    private static final Set<String> LOGICAL = new HashSet<String>();  
    private static final Set<String> QUANTIFIERS = new HashSet<String>();  
    private static final Set<String> DML = new HashSet<String>();  
    private static final Set<String> MISC = new HashSet<String>();  
    public static final String WHITESPACE = " \n\r\f\t";  
    static {  
        BEGIN_CLAUSES.add( "left" );  
        BEGIN_CLAUSES.add( "right" );  
        BEGIN_CLAUSES.add( "inner" );  
        BEGIN_CLAUSES.add( "outer" );  
        BEGIN_CLAUSES.add( "group" );  
        BEGIN_CLAUSES.add( "order" );  
  
        END_CLAUSES.add( "where" );  
        END_CLAUSES.add( "set" );  
        END_CLAUSES.add( "having" );  
        END_CLAUSES.add( "join" );  
        END_CLAUSES.add( "from" );  
        END_CLAUSES.add( "by" );  
        END_CLAUSES.add( "join" );  
        END_CLAUSES.add( "into" );  
        END_CLAUSES.add( "union" );  
  
        LOGICAL.add( "and" );  
        LOGICAL.add( "or" );  
        LOGICAL.add( "when" );  
        LOGICAL.add( "else" );  
        LOGICAL.add( "end" );  
  
        QUANTIFIERS.add( "in" );  
        QUANTIFIERS.add( "all" );  
        QUANTIFIERS.add( "exists" );  
        QUANTIFIERS.add( "some" );  
        QUANTIFIERS.add( "any" );  
  
        DML.add( "insert" );  
        DML.add( "update" );  
        DML.add( "delete" );  
  
        MISC.add( "select" );  
        MISC.add( "on" );  
    }  
  
    static final String indentString = "    ";  
    static final String initial = "\n    ";  
  
    public String format(String source) {  
        return new FormatProcess( source ).perform();  
    }  
  
    private static class FormatProcess {  
        boolean beginLine = true;  
        boolean afterBeginBeforeEnd = false;  
        boolean afterByOrSetOrFromOrSelect = false;  
        boolean afterValues = false;  
        boolean afterOn = false;  
        boolean afterBetween = false;  
        boolean afterInsert = false;  
        int inFunction = 0;  
        int parensSinceSelect = 0;  
        private LinkedList<Integer> parenCounts = new LinkedList<Integer>();  
        private LinkedList<Boolean> afterByOrFromOrSelects = new LinkedList<Boolean>();  
  
        int indent = 1;  
  
        StringBuilder result = new StringBuilder();  
        StringTokenizer tokens;  
        String lastToken;  
        String token;  
        String lcToken;  
  
        public FormatProcess(String sql) {  
            tokens = new StringTokenizer(  
                    sql,  
                    "()+*/-=<>'`\"[]," + WHITESPACE,  
                    true  
            );  
        }  
  
        public String perform() {  
  
            result.append( initial );  
  
            while ( tokens.hasMoreTokens() ) {  
                token = tokens.nextToken();  
                lcToken = token.toLowerCase();  
  
                if ( "'".equals( token ) ) {  
                    String t;  
                    do {  
                        t = tokens.nextToken();  
                        token += t;  
                    }  
                    while ( !"'".equals( t ) && tokens.hasMoreTokens() ); // cannot handle single quotes  
                }  
                else if ( "\"".equals( token ) ) {  
                    String t;  
                    do {  
                        t = tokens.nextToken();  
                        token += t;  
                    }  
                    while ( !"\"".equals( t ) );  
                }  
  
                if ( afterByOrSetOrFromOrSelect && ",".equals( token ) ) {  
                    commaAfterByOrFromOrSelect();  
                }  
                else if ( afterOn && ",".equals( token ) ) {  
                    commaAfterOn();  
                }  
  
                else if ( "(".equals( token ) ) {  
                    openParen();  
                }  
                else if ( ")".equals( token ) ) {  
                    closeParen();  
                }  
  
                else if ( BEGIN_CLAUSES.contains( lcToken ) ) {  
                    beginNewClause();  
                }  
  
                else if ( END_CLAUSES.contains( lcToken ) ) {  
                    endNewClause();  
                }  
  
                else if ( "select".equals( lcToken ) ) {  
                    select();  
                }  
  
                else if ( DML.contains( lcToken ) ) {  
                    updateOrInsertOrDelete();  
                }  
  
                else if ( "values".equals( lcToken ) ) {  
                    values();  
                }  
  
                else if ( "on".equals( lcToken ) ) {  
                    on();  
                }  
  
                else if ( afterBetween && lcToken.equals( "and" ) ) {  
                    misc();  
                    afterBetween = false;  
                }  
  
                else if ( LOGICAL.contains( lcToken ) ) {  
                    logical();  
                }  
  
                else if ( isWhitespace( token ) ) {  
                    white();  
                }  
  
                else {  
                    misc();  
                }  
  
                if ( !isWhitespace( token ) ) {  
                    lastToken = lcToken;  
                }  
  
            }  
            return result.toString();  
        }  
  
        private void commaAfterOn() {  
            out();  
            indent--;  
            newline();  
            afterOn = false;  
            afterByOrSetOrFromOrSelect = true;  
        }  
  
        private void commaAfterByOrFromOrSelect() {  
            out();  
            newline();  
        }  
  
        private void logical() {  
            if ( "end".equals( lcToken ) ) {  
                indent--;  
            }  
            newline();  
            out();  
            beginLine = false;  
        }  
  
        private void on() {  
            indent++;  
            afterOn = true;  
            newline();  
            out();  
            beginLine = false;  
        }  
  
        private void misc() {  
            out();  
            if ( "between".equals( lcToken ) ) {  
                afterBetween = true;  
            }  
            if ( afterInsert ) {  
                newline();  
                afterInsert = false;  
            }  
            else {  
                beginLine = false;  
                if ( "case".equals( lcToken ) ) {  
                    indent++;  
                }  
            }  
        }  
  
        private void white() {  
            if ( !beginLine ) {  
                result.append( " " );  
            }  
        }  
  
        private void updateOrInsertOrDelete() {  
            out();  
            indent++;  
            beginLine = false;  
            if ( "update".equals( lcToken ) ) {  
                newline();  
            }  
            if ( "insert".equals( lcToken ) ) {  
                afterInsert = true;  
            }  
        }  
  
        @SuppressWarnings( {"UnnecessaryBoxing"})  
        private void select() {  
            out();  
            indent++;  
            newline();  
            parenCounts.addLast( Integer.valueOf( parensSinceSelect ) );  
            afterByOrFromOrSelects.addLast( Boolean.valueOf( afterByOrSetOrFromOrSelect ) );  
            parensSinceSelect = 0;  
            afterByOrSetOrFromOrSelect = true;  
        }  
  
        private void out() {  
            result.append( token );  
        }  
  
        private void endNewClause() {  
            if ( !afterBeginBeforeEnd ) {  
                indent--;  
                if ( afterOn ) {  
                    indent--;  
                    afterOn = false;  
                }  
                newline();  
            }  
            out();  
            if ( !"union".equals( lcToken ) ) {  
                indent++;  
            }  
            newline();  
            afterBeginBeforeEnd = false;  
            afterByOrSetOrFromOrSelect = "by".equals( lcToken )  
                    || "set".equals( lcToken )  
                    || "from".equals( lcToken );  
        }  
  
        private void beginNewClause() {  
            if ( !afterBeginBeforeEnd ) {  
                if ( afterOn ) {  
                    indent--;  
                    afterOn = false;  
                }  
                indent--;  
                newline();  
            }  
            out();  
            beginLine = false;  
            afterBeginBeforeEnd = true;  
        }  
  
        private void values() {  
            indent--;  
            newline();  
            out();  
            indent++;  
            newline();  
            afterValues = true;  
        }  
  
        @SuppressWarnings( {"UnnecessaryUnboxing"})  
        private void closeParen() {  
            parensSinceSelect--;  
            if ( parensSinceSelect < 0 ) {  
                indent--;  
                parensSinceSelect = parenCounts.removeLast().intValue();  
                afterByOrSetOrFromOrSelect = afterByOrFromOrSelects.removeLast().booleanValue();  
            }  
            if ( inFunction > 0 ) {  
                inFunction--;  
                out();  
            }  
            else {  
                if ( !afterByOrSetOrFromOrSelect ) {  
                    indent--;  
                    newline();  
                }  
                out();  
            }  
            beginLine = false;  
        }  
  
        private void openParen() {  
            if ( isFunctionName( lastToken ) || inFunction > 0 ) {  
                inFunction++;  
            }  
            beginLine = false;  
            if ( inFunction > 0 ) {  
                out();  
            }  
            else {  
                out();  
                if ( !afterByOrSetOrFromOrSelect ) {  
                    indent++;  
                    newline();  
                    beginLine = true;  
                }  
            }  
            parensSinceSelect++;  
        }  
  
        private static boolean isFunctionName(String tok) {  
            final char begin = tok.charAt( 0 );  
            final boolean isIdentifier = Character.isJavaIdentifierStart( begin ) || '"' == begin;  
            return isIdentifier &&  
                    !LOGICAL.contains( tok ) &&  
                    !END_CLAUSES.contains( tok ) &&  
                    !QUANTIFIERS.contains( tok ) &&  
                    !DML.contains( tok ) &&  
                    !MISC.contains( tok );  
        }  
  
        private static boolean isWhitespace(String token) {  
            return WHITESPACE.indexOf( token ) >= 0;  
        }  
  
        private void newline() {  
            result.append( "\n" );  
            for ( int i = 0; i < indent; i++ ) {  
                result.append( indentString );  
            }  
            beginLine = true;  
        }  
    }  
  
      public static void main(String[] args) {  
        String sql = new MySQLFormatter().format("select * from t_sss");  
        System.out.println(sql);  
    }  
  
  
}  

输出结果样例:

 

按以上步骤,添加相关文件一定可以实现mybatis的格式化打印,但是有一个小坑,可能会打印两遍日志,因为又一遍是没有格式化的。去掉默认打印的配置即可。

本人是用logback进行日志打印的。我去掉了logback配置文件的如下配置后,就只打印了以便格式化后的SQL语句。供参考。

上一篇文章:https://mp.csdn.net/postedit/91788348。介绍使用logback打印日志,也是填补上一篇说要格式化打印mybatis输出的SQL语句的坑。

参考资料:

https://blog.csdn.net/gnd15732625435/article/details/81626095

https://blog.csdn.net/zknxx/article/details/69070330

https://www.ibm.com/developerworks/cn/java/j-lo-p6spy/  p6spy配置文件详解

https://www.cnblogs.com/luodengxiong/p/6766357.html

https://www.cnblogs.com/plain-heart/p/5012728.html

https://blog.csdn.net/kwy15732621629/article/details/77924398

https://blog.csdn.net/kisscatforever/article/details/78784254

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值