Atomikos+jdbcdslog——分布式事务管理输出实际log日志

阅读更多

前言

    想看实际产生的SQL,在一个数据源的情况下,最简单的方式是使用Log4jdbc。

    但在spring-data-jpa通过Atomikos实现JTA事务中,我们通过Atomikos实现了分布式事务,配置的是支持XA的DataSource,Log4jdbc这种在Driver上做文章的方法肯定不行。

    这里使用jdbcdslog的衍生项目jdbcdslog-exp来实现这个目标。jdbcdslog-exp比jdbcdslog更进了一步,输出的SQL,可以直接拷贝到PL/SQL等工具下执行。

 

依赖

jdbcdslog的依赖如下


    com.googlecode.usc
    jdbcdslog
    1.0.6.2

 

配置方式

properties配置信息

这里以Oracle为例,properties内容如下,当然dataSource配置信息等需要两份

 

dev.jdbc.dataSource=org.jdbcdslog.ConnectionPoolXADataSourceProxy
dev.jdbc.url=jdbc:oracle:thin:@192.168.3.129:1521:gtf?targetDS=oracle.jdbc.xa.client.OracleXADataSource
dev.jdbc.username=adp_dev
dev.jdbc.password=adp_dev

 其中url相当于url + targetDS ,targetDS的值为普通方式下的dataSource。

 

配置文件

一个数据源的配置方式如下,另一个同理。其它配置参照spring-data-jpa通过Atomikos实现JTA事务

 


    
    
    
        
            ${dev.jdbc.url}
            ${dev.jdbc.username}
            ${dev.jdbc.password}
        
    
    
    
    

 

 

问题

实际运行时会发生错误,原因是由于AtomikosDataSourceBean中doInit方法中在该方法的最后才调用的PropertyUtils.setProperties(xaDataSource, xaProperties );这段代码,导致上面调用setLogWriter的时候缺少参数。

 

解决方法

1.提供一个无视set/get方法的反射工具;

2.AtomikosDataSourceBean中用到的参数AtomikosXAConnectionFactory 没有访问修饰符,无法访问的问题;

 3.重新实现一个AtomikosDataSourceBean类,并重写doInit方法。

 

(1)set/get方法的反射工具

 

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

/**
 * 反射工具类.
 * 
 * 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
 * 
 * @author calvin
 */
public class Reflections {

	private static Logger logger = LoggerFactory.getLogger(Reflections.class);

    /**
     * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
     */
    public static Object getFieldValue(final Object obj, final String fieldName) {
        Field field = getAccessibleField(obj, fieldName);

        if (field == null) {
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
        }

        Object result = null;
        try {
            result = field.get(obj);
        } catch (IllegalAccessException e) {
            logger.error("不可能抛出的异常{}", e.getMessage());
        }
        return result;
    }

    /**
     * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
     *
     * 如向上转型到Object仍无法找到, 返回null.
     */
    public static Field getAccessibleField(final Object obj, final String fieldName) {
        Validate.notNull(obj, "object can't be null");
        Validate.notBlank(fieldName, "fieldName can't be blank");
        for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                Field field = superClass.getDeclaredField(fieldName);
                makeAccessible(field);
                return field;
            } catch (NoSuchFieldException e) {//NOSONAR
                // Field不在当前类定义,继续向上转型
            }
        }
        return null;
    }

    /**
     * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
     */
    public static void makeAccessible(Field field) {
        if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
                .isFinal(field.getModifiers())) && !field.isAccessible()) {
            field.setAccessible(true);
        }
    }
}

 

(2)MyAtomikosXAConnectionFactory

解决自定义的AtomikosDataSourceBean无法访问问题,创建一个MyAtomikosXAConnectionFactory类,代码同AtomikosXAConnectionFactory完全一致;

 

 

(3)MyAtomikosDataSourceBean的doInit()方法

 

    protected com.atomikos.datasource.pool.ConnectionFactory doInit() throws Exception {
        if (xaDataSource == null) {
            if (xaDataSourceClassName == null)
                throwAtomikosSQLException("Property 'xaDataSourceClassName' cannot be null");
            if (xaProperties == null)
                throwAtomikosSQLException("Property 'xaProperties' cannot be null");
        }

        if (LOGGER.isInfoEnabled())
            LOGGER.logInfo(this + ": initializing with [" + " xaDataSourceClassName=" + xaDataSourceClassName
                    + "," + " uniqueResourceName=" + getUniqueResourceName() + "," + " maxPoolSize="
                    + getMaxPoolSize() + "," + " minPoolSize=" + getMinPoolSize() + ","
                    + " borrowConnectionTimeout=" + getBorrowConnectionTimeout() + "," + " maxIdleTime="
                    + getMaxIdleTime() + "," + " reapTimeout=" + getReapTimeout() + ","
                    + " maintenanceInterval=" + getMaintenanceInterval() + "," + " testQuery="
                    + getTestQuery() + "," + " xaProperties=" + printXaProperties() + "," + " loginTimeout="
                    + getLoginTimeout() + "," + " maxLifetime=" + getMaxLifetime() + "]");

        if (xaDataSource == null) {
            Class xadsClass = null;
            try {
                xadsClass = ClassLoadingHelper.loadClass(getXaDataSourceClassName());
            } catch (ClassNotFoundException nf) {
                AtomikosSQLException
                        .throwAtomikosSQLException(
                                "The class '"
                                        + getXaDataSourceClassName()
                                        + "' specified by property 'xaDataSourceClassName' could not be found in the classpath. Please make sure the spelling is correct, and that the required jar(s) are in the classpath.",
                                nf);

            }
            Object driver = xadsClass.newInstance();
            if (!(driver instanceof XADataSource)) {
                AtomikosSQLException
                        .throwAtomikosSQLException("The class '"
                                + getXaDataSourceClassName()
                                + "' specified by property 'xaDataSourceClassName' does not implement the required interface javax.jdbc.XADataSource. Please make sure the spelling is correct, and check your JDBC driver vendor's documentation.");
            }
            xaDataSource = (XADataSource) driver;
            PropertyUtils.setProperties(xaDataSource, xaProperties);
            ConnectionPoolXADataSourceProxy proxy = (ConnectionPoolXADataSourceProxy) xaDataSource;
            XADataSource targetDs = (XADataSource) Reflections.getFieldValue(proxy, "targetDS");
            targetDs.setLoginTimeout(getLoginTimeout());
            // xaDataSource.setLoginTimeout ( getLoginTimeout() );
            targetDs.setLogWriter(getLogWriter());
        }

        JdbcTransactionalResource tr = new JdbcTransactionalResource(getUniqueResourceName(), xaDataSource);
        com.atomikos.datasource.pool.ConnectionFactory cf = new MyAtomikosXAConnectionFactory(xaDataSource,
                tr, this);
        Configuration.addResource(tr);

        return cf;
    }

 

(4)logback配置文件

 

 



看名就知道输出的是哪一部分数据,如果只看SQL,用org.jdbcdslog.StatementLogger就够了。
 

至此,Atomikos+jdbcdslog配置成功

你可能感兴趣的:(Atomikos,JTA,分布式事务,sql,log)