Spring AOP的应用: 给NamedParameterJdbcTemplate和JdbcTemplate的输出日志加上参数值

  众所周知,通过JdbcTemplate/NamedParameterJdbcTemplate执行的查询, log里面打印的SQL参数都是?占位符, 对于调试SQL非常不友好, 所以利用AOP将参数代入,然后再打印出来.

   首先我们先写一个SQLLogger,用于拦截JdbcTemplate/NamedParameterJdbcTemplate的查询动作(于动作前拦截,用Before).


package com.freestyle.common.hibernate.dao.support;

import java.util.Map;
import java.util.Map.Entry;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import com.freestyle.utils.Util;
/*****
 * SQL日志打印工具,可将jdbctemplate的参数值一起显示(依赖spring环境)
 * @author dgmislrh
 * 14/May/2018
 */
@Aspect
public class SqlLogger {
    private static final Logger log = Logger.getLogger(SqlLogger.class);
   
    @Before("execution(* org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.*(..))")
    public void logNamedJdbcTemplate(JoinPoint jp) throws Throwable {
    	Object[] methodArgs = jp.getArgs();
        Map<String,Object> sqlArgs = null;

       // get the SQL statement
       String statement = methodArgs[0].toString();

       // find the SQL arguments (parameters)
       for (int i = 1, n = methodArgs.length; i < n; i++) {
           Object arg = methodArgs[i];
           if (arg instanceof Map) {
               sqlArgs = (Map<String,Object>)arg;
               break;
           }
       }
       // fill in any SQL parameter place-holders (?'s)
       String completedStatement = (sqlArgs == null ? statement : fillNamedParameters(statement, sqlArgs));

       // log it
       log.debug(jp.getSignature().toShortString()+ "\n------FULL SQL------\n"+completedStatement+"\n---------------------");
    }
    private String fillNamedParameters(String statement,Map<String,Object> sqlArgs){
    	StringBuilder sb=new StringBuilder(statement);    	    	
    	for (Entry<String, Object>item:sqlArgs.entrySet()){
    		int lvnSeek=sb.indexOf(":"+item.getKey());
    		if (lvnSeek==-1)continue;
    		String lvsValue=null;
    		if (item.getValue()==null){
    			lvsValue="null";
    		}
    		else if (item.getValue() instanceof String){
    			lvsValue=String.format("'%s'", item.getValue());
    		}    		
    		else if (item.getValue() instanceof Boolean){
    			lvsValue=((Boolean) item.getValue()).booleanValue()?"true":"false";
    		}
    		else if (item.getValue() instanceof java.util.Date){
    			java.util.Date lvdDt=(java.util.Date) item.getValue();
    			if (lvdDt.getHours()==0&&lvdDt.getMinutes()==0&& lvdDt.getSeconds()==0){
    				lvsValue="timestamp '"+Util.datetimeToString((java.util.Date)item.getValue(),Util.c_java_datefmt)+"'";
    			}
    			else{
    				lvsValue="timestamp '"+Util.datetimeToString((java.util.Date)item.getValue(),Util.c_java_dttfmt)+"'";
    			}
    		}
    		else {
    			lvsValue=item.getValue().toString();
    		}    		
    		sb.replace(lvnSeek, lvnSeek+item.getKey().length()+1, lvsValue);
    	}
    	return sb.toString();
    }
    @Before("execution(* org.springframework.jdbc.core.JdbcOperations.*(..))")
    //@Before("execution(* org.springframework.jdbc.core.namedparam.*(..))")
    public void logJdbcTemplate(JoinPoint jp) throws Throwable {    	
        Object[] methodArgs = jp.getArgs(),
                 sqlArgs = null;
 
        // get the SQL statement
        String statement = methodArgs[0].toString();
 
        // find the SQL arguments (parameters)
        for (int i = 1, n = methodArgs.length; i < n; i++) {
            Object arg = methodArgs[i];
            if (arg instanceof Object[]) {
                sqlArgs = (Object[])arg;
                break;
            }
        }
        // fill in any SQL parameter place-holders (?'s)
        String completedStatement = (sqlArgs == null ? statement : fillParameters(statement, sqlArgs));
 
        // log it
        log.debug(jp.getSignature().toShortString()+"\n------FULL SQL------\n"+completedStatement+"\n---------------------");
    }
    
 
    private String fillParameters(String statement, Object[] sqlArgs) {
        // initialize a StringBuilder with a guesstimated final length
        StringBuilder completedSqlBuilder = new StringBuilder(Math.round(statement.length() * 1.2f));
        int index, // will hold the index of the next ?
            prevIndex = 0; // will hold the index of the previous ? + 1
 
        // loop through each SQL argument
        for (Object arg : sqlArgs) {
            index = statement.indexOf("?", prevIndex);
            if (index == -1)
                break; // bail out if there's a mismatch in # of args vs. ?'s
 
            // append the chunk of SQL coming before this ?
            completedSqlBuilder.append(statement.substring(prevIndex, index));
            if (arg == null)
                completedSqlBuilder.append("null");
            else if (arg instanceof String) {
                // wrap the String in quotes and escape any quotes within
                completedSqlBuilder.append('\'')
                   .append(arg.toString().replace("'", "''"))
                    .append('\'');
            }
            else if (arg instanceof java.sql.Date) {      
            	java.util.Date lvdDt=new java.sql.Date(((java.sql.Date)arg).getTime());
            	String lvsValue="timestamp '"+Util.datetimeToString(lvdDt,Util.c_java_dttfmt)+"'";
                completedSqlBuilder.append(lvsValue);       
            }
            else
                completedSqlBuilder.append(arg.toString());
 
            prevIndex = index + 1;
        }
 
        // add the rest of the SQL if any
        if (prevIndex != statement.length())
            completedSqlBuilder.append(statement.substring(prevIndex));
 
        return completedSqlBuilder.toString();
    }
}

用注解的方式拦截JdbcTemplate的查询动作,让其先执行logJdbcTemplate, 拦截NamedParameterJdbcTemplate的查询动作,让其先执行logNamedJdbcTemplate 。接着无非就是将?占位符依然换成object[]里面的值, 将命名点位符换成map里面的value,这些就不细讲了。


     第二步,修改spring配置文件,在里面加上:

<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
      <constructor-arg name="dataSource" ref="dataSource">         
      </constructor-arg>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
      <constructor-arg name="dataSource" ref="dataSource">         
      </constructor-arg>
    </bean>
    	
    <aop:aspectj-autoproxy  proxy-target-class="true"/>            
	<bean id="sqlLogger" class="com.freestyle.common.hibernate.dao.support.SqlLogger"></bean>

就是将namedParameterJdbcTemplate和jdbcTemplate也在spring里面占一实例,然后给下面的TransJdbcTemplate调用。

TransJdbcTemplate是我写的一个带事务管理的JdbcTemplate类,一般就它来实现原始SQL。

package com.freestyle.common.spring;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.springframework.context.annotation.DependsOn;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

/****
 * 带有事务管理的JdbcTemplate
 * @author dgmislrh
 *
 */
@DependsOn({"dsTransactionManager"})
//@Repository("transJdbcTemplate")
public class TransJdbcTemplate extends org.springframework.jdbc.core.JdbcTemplate{
	@Resource(name="dsTransactionManager")	
	private org.springframework.jdbc.datasource.DataSourceTransactionManager dsTm;
	
	public static interface TransCallback{
		public void onExecute(JdbcTemplate pvJt,NamedParameterJdbcTemplate pvNamedJt) throws Exception;
	}
	
	public static interface NamedTransCallback{
		public void onExecute(NamedParameterJdbcTemplate pvJt) throws Exception;
	}
	
	public TransJdbcTemplate(DataSource dataSource) {
		super (dataSource);
	}
	/****
	 * 若需要回滚事务,在onExecute里面throw runtimeexception即可
	 * @param lvCallback
	 */
	public Exception doNamedTransactionBatch(final NamedTransCallback lvCallback){
		if (dsTm==null){
			dsTm=(DataSourceTransactionManager) ContextHolder.getBean("dsTransactionManager");
		}
		TransactionTemplate  lvTt = new TransactionTemplate (dsTm);
		return lvTt.execute(new TransactionCallback<Exception>() {

			@Override
			public Exception doInTransaction(TransactionStatus arg0) {
				try {  
					//NamedParameterJdbcTemplate lvNamedJt=new NamedParameterJdbcTemplate(getDataSource());
					NamedParameterJdbcTemplate lvNamedJt=(NamedParameterJdbcTemplate) ContextHolder.getBean("namedParameterJdbcTemplate");
					lvCallback.onExecute(lvNamedJt);
					return null;
                } catch (Exception e) {  
                    // 抛出异常时进行回滚  
                    arg0.setRollbackOnly();
                    return e;
                }
			}
		});		  
					
	}
	
	/****
	 * 若需要回滚事务,在onExecute里面throw runtimeexception即可
	 * @param lvCallback
	 */
	public Exception doTransactionBatch(final TransCallback pvCallback){
		if (dsTm==null){
			dsTm=(DataSourceTransactionManager) ContextHolder.getBean("dsTransactionManager");
		}
		TransactionTemplate  lvTt = new TransactionTemplate (dsTm);
		return lvTt.execute(new TransactionCallback<Exception>() {			
			@Override
			public Exception doInTransaction(TransactionStatus arg0) {
				try {  										
					//JdbcTemplate lvJt=new JdbcTemplate(getDataSource());
					JdbcTemplate lvJt=(JdbcTemplate) ContextHolder.getBean("jdbcTemplate");
					//NamedParameterJdbcTemplate lvNamedJt=new NamedParameterJdbcTemplate(getDataSource());
					NamedParameterJdbcTemplate lvNamedJt=(NamedParameterJdbcTemplate) ContextHolder.getBean("namedParameterJdbcTemplate");
					
					pvCallback.onExecute(lvJt,lvNamedJt);
					return null;
                } catch (Exception e) {  
                    // 抛出异常时进行回滚  
                    arg0.setRollbackOnly();
                    return e;
                }  
			}
		});			
	}

}

将里面的lvJt换为从spring容器里取得,因为用new新建的jdbctemplate,并未被spring aop拦截。


测试结果:

17:59:28,405 DEBUG SqlLogger:43 - NamedParameterJdbcTemplate.queryForMap(..)
------FULL SQL------
select * from fun_getrecseq_range(timestamp '01/Apr/2018') as t(seqStart varchar,seqEnd varchar)

---------------------

18:00:01,280 DEBUG SqlLogger:43 - NamedParameterJdbcTemplate.queryForMap(..)
------FULL SQL------
select * from fun_getrecseq_range(timestamp '30/Apr/2018') as t(seqStart varchar,seqEnd varchar)
---------------------


猜你喜欢

转载自blog.csdn.net/rocklee/article/details/80312844