前言
想看实际产生的SQL,在一个数据源的情况下,最简单的方式是使用Log4jdbc。
但在spring-data-jpa通过Atomikos实现JTA事务中,我们通过Atomikos实现了分布式事务,配置的是支持XA的DataSource,Log4jdbc这种在Driver上做文章的方法肯定不行。
这里使用jdbcdslog的衍生项目jdbcdslog-exp来实现这个目标。jdbcdslog-exp比jdbcdslog更进了一步,输出的SQL,可以直接拷贝到PL/SQL等工具下执行。
依赖
jdbcdslog的依赖如下
<dependency> <groupId>com.googlecode.usc</groupId> <artifactId>jdbcdslog</artifactId> <version>1.0.6.2</version> </dependency>
配置方式
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事务
<bean id="dataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="XA1DBMS" /> <property name="xaDataSourceClassName" value="${dev.jdbc.dataSource}" /> <property name="xaProperties"> <props> <prop key="URL">${dev.jdbc.url}</prop> <prop key="user">${dev.jdbc.username}</prop> <prop key="password">${dev.jdbc.password}</prop> </props> </property> <property name="poolSize" value="10" /> <property name="minPoolSize" value="10" /> <property name="maxPoolSize" value="30" /> </bean>
问题
实际运行时会发生错误,原因是由于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配置文件
<logger name="org.jdbcdslog.ConnectionLogger" level="INFO" />
<logger name="org.jdbcdslog.StatementLogger" level="INFO" />
<logger name="org.jdbcdslog.ResultSetLogger" level="INFO" />
看名就知道输出的是哪一部分数据,如果只看SQL,用org.jdbcdslog.StatementLogger就够了。
至此,Atomikos+jdbcdslog配置成功
相关推荐
使用spring + atomikos+druid配置的分布式事务demo,两种数据源配置方式都可以,使用junit测试没问题,案例中使用的mysql数据库是8.0.11版本,版本不同请自行修改pom.xml和jdbc.properties
本示例"spring+atomikos+druid分布式事务Demo"聚焦于如何在Spring框架中利用Atomikos和Druid来处理分布式事务。接下来,我们将深入探讨这三个组件以及它们在实现分布式事务中的作用。 Spring是一个广泛使用的Java...
本教程将聚焦于如何利用Spring 3.0、Hibernate ORM框架以及Atomikos这个开源事务管理器来实现高效、可靠的多数据源分布式事务处理。 **Spring 3.0**: Spring是Java开发中最广泛使用的轻量级框架之一,它提供了一个...
Atomikos是一款开源的JTA事务管理器,它支持分布式事务处理,使得在多个数据源之间进行事务操作成为可能。JTA是Java平台中的标准API,用于管理跨越多个资源(如数据库或消息队列)的事务。Atomikos通过实现JTA规范,...
Spring框架提供了强大的支持来处理分布式事务,而Atomikos是一个开源的事务管理器,专门用于处理JTA(Java Transaction API)事务,尤其适用于微服务和分布式环境。本教程将探讨如何结合Spring和Atomikos来实现...
在分布式事务场景中,MySQL作为数据存储,需要与事务管理器Atomikos配合,共同保证事务的正确性。 要实现Spring Boot、Atomikos、JPA和MySQL的集成,你需要进行以下步骤: 1. 添加依赖:在Spring Boot的`pom.xml`...
SpringBoot+Mybatis+Atomikos+Mysql+Oracle 多数据源分布式事物后台搭建 完整demo包,直接下下来解压,数据库配成自己的库,表自己的表,修改下脚本直接跑,网上大把资料,没一个能直接用的,这里花了点时间稍做...
本项目使用Spring Boot、Atomikos、JTA(Java Transaction API)、Hibernate和MySQL来实现分布式事务处理和多数据源管理,以确保在多个数据库操作之间保持事务的ACID特性。 首先,Spring Boot作为微服务开发的主流...
本项目"java+spring+mybatis+mysql+RuoYi-atomikos-实现分布式事务.zip"是一个基于若依(RuoYi)框架改造的多模块分布式事务解决方案,它利用了Atomikos这一强大的分布式事务管理器。以下将详细解析这个项目的知识点...
总之,Spring + JTA + Atomikos的组合提供了一种强大而灵活的方式,用于处理分布式环境中的事务管理。通过理解JTA的基本原理,掌握Spring的事务管理机制,以及熟悉Atomikos的使用,开发者可以构建出高可用、强一致性...
Git 版本控制的文件、Atomikos 事务日志锁文件、事务日志文件、Maven 命令行脚本、Eclipse 项目配置、Maven 的 POM 配置文件以及项目构建输出目录。这些文件共同构成了一个标准的 Spring Boot 项目结构,其中 `pom....
至于`demo4.ini`文件,这可能是Atomikos的配置文件,其中包含了Atomikos如何初始化和配置事务管理器的详细信息,例如日志位置、最大并发事务数等。配置文件的具体内容因项目需求而异,但通常会包括以下部分: ```...
SSM(SpringMVC、Spring、MyBatis)框架是Java Web开发中常见的技术栈,而Atomikos则是一个强大的开源事务管理器,主要用于处理分布式事务。本案例以非Maven方式构建,展示了如何在不依赖Maven构建系统的情况下,...
Spring Boot作为轻量级的Java开发框架,结合Atomikos这样的分布式事务管理器,可以有效地解决这些问题。本文将深入探讨如何在Spring Boot项目中实现Atomikos分布式事务以及动态数据源切换的两种示例。 首先,我们...
Spring框架提供了多种实现分布式事务管理的方式,其中一种是通过集成第三方工具如Atomikos来实现。Atomikos是一个开源的事务处理服务,支持JTA(Java Transaction API),能很好地处理分布式环境中的ACID(原子性、...
Spring 中使用 Atomikos+Druid 实现经典分布式事务的方法 在分布式系统中,经典分布式事务是相对互联网中的柔性分布式事务而言,其特性为 ACID 原则,包括原子性(Atomictiy)、一致性(Consistency)、隔离性...
Spring Boot:mybatis-plus + atomikos + druid 实现不同实例数据库的多数据源配置和分布式事务管理(demo项目),想到工作上可能会用到多数据源,但是自己在这方面并不是很熟悉,于是在网上查阅了很多文章,结果...
我们需配置Atomikos的事务管理器,并将其集成到Spring中,使得MyBatis的SQL操作能够在分布式事务的上下文中进行。 具体实现步骤如下: 1. 引入相关依赖:包括Spring、Atomikos和MyBatis的库,以及可能的数据库驱动...
将基于Spring4.1.7+atomikos+mybaits 实现两阶段的分布式事务处理,通过AOP面向切面实现动态实现数据源的切换 http://www.dczou.com/viemall/407.html
本主题将深入探讨如何利用SpringBoot结合Atomikos实现动态多数据源以及事务管理,并介绍两种切换数据源的方法。 首先,SpringBoot简化了传统Spring应用的初始化过程,它通过自动配置和starter包让开发者快速搭建...