目录
1 定义PikaqiuXADataSource
自定义PikaqiuXADataSource如下:
public class PikaqiuXADataSource extends PikaqiuDataSource implements XADataSource {
// 实现获取XAConnection
@Override
public XAConnection getXAConnection() throws SQLException {
// PikaqiuDataSource中获取返回PikaqiuPooledConnection连接
PikaqiuPooledConnection pikaqiuPooledConnection = this.getConnection();
// 通过实际的物理连接返回一个XAConnection
XAConnection physicalXAConnection = createPhysicalXAConnection(pikaqiuPooledConnection.conn);
// 包装PikaqiuPooledConnection和XAConnection返回PikaqiuPooledXAConnection
return new PikaqiuPooledXAConnection(pikaqiuPooledConnection,physicalXAConnection);
}
@Override
public XAConnection getXAConnection(String s, String s1) throws SQLException {
return null;
}
private XAConnection createPhysicalXAConnection(Connection physicalConn) throws SQLException {
return MysqlUtils.createXAConnection(driver, physicalConn);
}
}
通过实际的物理连接创建返回一个XAConnection对象代码逻辑处理如下:
public class MysqlUtils {
static Class<?> utilClass;
static boolean utilClassError = false;
static boolean utilClass_isJdbc4 = false;
static Class<?> class_5_connection = null;
static Method method_5_getPinGlobalTxToPhysicalConnection = null;
static Class<?> class_5_suspendableXAConnection = null;
static Constructor<?> constructor_5_suspendableXAConnection = null;
static Class<?> class_5_JDBC4SuspendableXAConnection = null;
static Constructor<?> constructor_5_JDBC4SuspendableXAConnection = null;
static Class<?> class_5_MysqlXAConnection = null;
static Constructor<?> constructor_5_MysqlXAConnection = null;
static Class<?> class_ConnectionImpl = null;
static Method method_getId = null;
static boolean method_getId_error = false;
static Class<?> class_6_ConnectionImpl = null;
static Method method_6_getId = null;
volatile static Class<?> class_6_connection = null;
volatile static Method method_6_getPropertySet = null;
volatile static Method method_6_getBooleanReadableProperty = null;
volatile static Method method_6_getValue = null;
volatile static boolean method_6_getValue_error = false;
volatile static Class<?> class_6_suspendableXAConnection = null;
volatile static Method method_6_getInstance = null;
volatile static boolean method_6_getInstance_error = false;
volatile static Method method_6_getInstanceXA = null;
volatile static boolean method_6_getInstanceXA_error = false;
volatile static Class<?> class_6_JDBC4SuspendableXAConnection = null;
// 根据物理连接返回XAConnection
public static XAConnection createXAConnection(Driver driver, Connection physicalConn) throws SQLException {
final int major = driver.getMajorVersion();
if (major == 5) {
if (utilClass == null && !utilClassError) {
try {
utilClass = Class.forName("com.mysql.jdbc.Util");
Method method = utilClass.getMethod("isJdbc4");
utilClass_isJdbc4 = (Boolean) method.invoke(null);
// 返回mysql的连接
class_5_connection = Class.forName("com.mysql.jdbc.Connection");
// 检测物理连接
method_5_getPinGlobalTxToPhysicalConnection = class_5_connection.getMethod("getPinGlobalTxToPhysicalConnection");
// 返回optional.SuspendableXAConnection类
class_5_suspendableXAConnection = Class.forName("com.mysql.jdbc.jdbc2.optional.SuspendableXAConnection");
// 返回构造函数
constructor_5_suspendableXAConnection = class_5_suspendableXAConnection.getConstructor(class_5_connection);
// 返回optional.JDBC4SuspendableXAConnection类
class_5_JDBC4SuspendableXAConnection = Class.forName("com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection");
// 返回构造函数
constructor_5_JDBC4SuspendableXAConnection = class_5_JDBC4SuspendableXAConnection.getConstructor(class_5_connection);
class_5_MysqlXAConnection = Class.forName("com.mysql.jdbc.jdbc2.optional.MysqlXAConnection");
constructor_5_MysqlXAConnection = class_5_MysqlXAConnection.getConstructor(class_5_connection, boolean.class);
} catch (Exception ex) {
ex.printStackTrace();
utilClassError = true;
}
}
try {
boolean pinGlobTx = (Boolean) method_5_getPinGlobalTxToPhysicalConnection.invoke(physicalConn);
if (pinGlobTx) {
if (!utilClass_isJdbc4) {
return (XAConnection) constructor_5_suspendableXAConnection.newInstance(physicalConn);
}
// 实例化返回XAConnection
return (XAConnection) constructor_5_JDBC4SuspendableXAConnection.newInstance(physicalConn);
}
return (XAConnection) constructor_5_MysqlXAConnection.newInstance(physicalConn, Boolean.FALSE);
} catch (Exception e) {
e.printStackTrace();
}
} else if (major == 6 || major == 8) {
if (method_6_getValue == null && !method_6_getValue_error) {
try {
class_6_connection = Class.forName("com.mysql.cj.api.jdbc.JdbcConnection");
} catch (Throwable t) {
}
try {
// maybe 8.0.11 or higher version, try again with com.mysql.cj.jdbc.JdbcConnection
if (class_6_connection == null) {
class_6_connection = Class.forName("com.mysql.cj.jdbc.JdbcConnection");
method_6_getPropertySet = class_6_connection.getMethod("getPropertySet");
Class<?> propertySetClass = Class.forName("com.mysql.cj.conf.PropertySet");
NoSuchMethodException noSuchMethodException = null;
try {
method_6_getBooleanReadableProperty = propertySetClass
.getMethod("getBooleanReadableProperty", String.class);
method_6_getValue = Class.forName("com.mysql.cj.conf.ReadableProperty")
.getMethod("getValue");
} catch (NoSuchMethodException error) {
noSuchMethodException = error;
}
if (method_6_getBooleanReadableProperty == null) {
method_6_getBooleanReadableProperty = propertySetClass
.getMethod("getBooleanProperty", String.class);
method_6_getValue = Class.forName("com.mysql.cj.conf.RuntimeProperty")
.getMethod("getValue");
}
} else {
method_6_getPropertySet = class_6_connection.getMethod("getPropertySet");
method_6_getBooleanReadableProperty = Class.forName("com.mysql.cj.api.conf.PropertySet").getMethod("getBooleanReadableProperty", String.class);
method_6_getValue = Class.forName("com.mysql.cj.api.conf.ReadableProperty").getMethod("getValue");
}
} catch (Exception ex) {
ex.printStackTrace();
method_6_getValue_error = true;
}
}
try {
// pinGlobalTxToPhysicalConnection
Boolean pinGlobTx = (Boolean) method_6_getValue.invoke(
method_6_getBooleanReadableProperty.invoke(
method_6_getPropertySet.invoke(physicalConn)
, "pinGlobalTxToPhysicalConnection"
)
);
if (pinGlobTx != null && pinGlobTx) {
try {
if (method_6_getInstance == null && !method_6_getInstance_error) {
class_6_suspendableXAConnection = Class.forName("com.mysql.cj.jdbc.SuspendableXAConnection");
method_6_getInstance = class_6_suspendableXAConnection.getDeclaredMethod("getInstance", class_6_connection);
method_6_getInstance.setAccessible(true);
}
} catch (Throwable ex) {
ex.printStackTrace();
method_6_getInstance_error = true;
}
return (XAConnection) method_6_getInstance.invoke(null, physicalConn);
} else {
try {
if (method_6_getInstanceXA == null && !method_6_getInstanceXA_error) {
class_6_JDBC4SuspendableXAConnection = Class.forName("com.mysql.cj.jdbc.MysqlXAConnection");
method_6_getInstanceXA = class_6_JDBC4SuspendableXAConnection.getDeclaredMethod("getInstance", class_6_connection, boolean.class);
method_6_getInstanceXA.setAccessible(true);
}
} catch (Throwable ex) {
ex.printStackTrace();
method_6_getInstanceXA_error = true;
}
return (XAConnection) method_6_getInstanceXA.invoke(null, physicalConn, Boolean.FALSE);
}
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
} catch (Exception e) {
e.printStackTrace();
method_6_getInstance_error = true;
}
}
throw new SQLFeatureNotSupportedException();
}
}
2 定义PikaqiuPooledXAConnection
自定义PikaqiuPooledXAConnection代码如下:
public class PikaqiuPooledXAConnection implements XAConnection {
private PikaqiuPooledConnection pooledConnection;
private XAConnection xaConnection;
// 包装PikaqiuPooledConnection和XAConnection返回
public PikaqiuPooledXAConnection(PikaqiuPooledConnection pooledConnection, XAConnection xaConnection){
this.pooledConnection = pooledConnection;
this.xaConnection = xaConnection;
}
// 根据xaConnection返回XAResource,该对象定义了各关系型数据库的XA实现
@Override
public XAResource getXAResource() throws SQLException {
return xaConnection.getXAResource();
}
// 返回Druid的pooledConnection
@Override
public Connection getConnection() throws SQLException {
return pooledConnection;
}
// 关闭连接
@Override
public void close() throws SQLException {
pooledConnection.close();
}
// 该方法以及下面几个方法会Connection事件监听器,当关闭连接或者异常中断连接会触发监听器的调用,这里不做处理,Druid源码也并未实现
@Override
public void addConnectionEventListener(ConnectionEventListener connectionEventListener) {
}
@Override
public void removeConnectionEventListener(ConnectionEventListener connectionEventListener) {
}
@Override
public void addStatementEventListener(StatementEventListener statementEventListener) {
}
@Override
public void removeStatementEventListener(StatementEventListener statementEventListener) {
}
}
3 Atomikos集成Pikaqiu实现XA管理
整体目录结构如下:
MybatisConfigurer配置如下:
/**
* 配置扫描com.yrz.pikaqiu.mapper.ds1目录下的mapper将连接数据源1操作
* 配置sqlSessionFactoryRef为sqlSessionTemplate1
*/
@Configuration
@MapperScan(basePackages = {"com.yrz.pikaqiu.mapper.ds1"},sqlSessionTemplateRef = "sqlSessionTemplate1")
public class MybatisConfigurer1 {
/**
* jdbc.ds1获取配置信息,初始化pikaqiuDataSource1
* @return
*/
@Bean(name="pikaqiuDataSource1")
@ConfigurationProperties(prefix = "jdbc.ds1")
public PikaqiuXADataSource dataSource0(){
PikaqiuXADataSource dataSource = new PikaqiuXADataSource();
return dataSource;
}
/**
* 实例化AtomikosDataSourceBean,并且set Pikaqiu初始化的PikaqiuXADataSource
* @param pikaqiuDataSource1
* @return
*/
@Primary
@Bean(name = "dataSource1")
public AtomikosDataSourceBean dataSource(@Qualifier("pikaqiuDataSource1") PikaqiuXADataSource pikaqiuDataSource1){
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
try {
xaDataSource.setXaDataSource(pikaqiuDataSource1);
xaDataSource.setMaxPoolSize(5);
xaDataSource.setMinPoolSize(5);
xaDataSource.setUniqueResourceName("dataSource1");
} catch (Exception e) {
System.out.println("dataSource1 init error!"+e);
}
return xaDataSource;
}
/**
* 将AtomikosDataSourceBean交给SqlSessionFactory
* @param dataSource
* @return
* @throws Exception
*/
@Bean(name = "sqlSessionFactory1")
public SqlSessionFactory sqlSessionFactory1(@Qualifier("dataSource1") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
/**
* 将sqlSessionFactory交给SqlSessionTemplate管理
* @param sqlSessionFactory
* @return
*/
@Bean(name = "sqlSessionTemplate1")
public SqlSessionTemplate sqlSessionTemplate1(
@Qualifier("sqlSessionFactory1") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
/**
* 配置扫描com.yrz.pikaqiu.mapper.ds2目录下的mapper将连接数据源2操作
* 配置sqlSessionFactoryRef为sqlSessionTemplate2
*/
@Configuration
@MapperScan(basePackages = {"com.yrz.pikaqiu.mapper.ds2"},sqlSessionTemplateRef = "sqlSessionTemplate2")
public class MybatisConfigurer2 {
/**
* jdbc.ds1获取配置信息,初始化pikaqiuDataSource2
* @return
*/
@Bean(name="pikaqiuDataSource2")
@ConfigurationProperties(prefix = "jdbc.ds2")
public PikaqiuXADataSource dataSource0(){
PikaqiuXADataSource dataSource = new PikaqiuXADataSource();
return dataSource;
}
/**
* 实例化AtomikosDataSourceBean,并且set Pikaqiu初始化的PikaqiuXADataSource
* @param pikaqiuDataSource2
* @return
*/
@Bean(name = "dataSource2")
public AtomikosDataSourceBean dataSource(@Qualifier("pikaqiuDataSource2") PikaqiuXADataSource pikaqiuDataSource2){
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
try {
xaDataSource.setXaDataSource(pikaqiuDataSource2);
xaDataSource.setMaxPoolSize(10);
xaDataSource.setMinPoolSize(5);
xaDataSource.setUniqueResourceName("dataSource2");
} catch (Exception e) {
System.out.println("dataSource2 init error!"+e);
}
return xaDataSource;
}
/**
* 将AtomikosDataSourceBean交给SqlSessionFactory
* @param dataSource
* @return
* @throws Exception
*/
@Bean(name = "sqlSessionFactory2")
public SqlSessionFactory sqlSessionFactory2(@Qualifier("dataSource2") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
/**
* 将sqlSessionFactory交给SqlSessionTemplate管理
* @param sqlSessionFactory
* @return
*/
@Bean(name = "sqlSessionTemplate2")
public SqlSessionTemplate sqlSessionTemplate2(
@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
entity配置如下:
public class PikaqiuTest {
private String key;
private String value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return "DruidTest{" +
"key='" + key + '\'' +
", value='" + value + '\'' +
'}';
}
}
public class SoulTest {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
mapper配置如下:
public interface PikaqiuTestMapper {
@Insert("insert into druid_test values ('4','4')")
void insertOne();
}
public interface SoulTestMapper {
@Insert("insert into soul_test values ('4')")
void insertOne();
}
PikaqiuTestService配置如下:
@Service
public class PikaqiuTestService {
@Autowired
private PikaqiuTestMapper pikaqiuTestMapper;
@Autowired
private SoulTestMapper soulTestMapper;
@Transactional(rollbackFor = Exception.class)
public void insert(){
pikaqiuTestMapper.insertOne();
soulTestMapper.insertOne();
}
}
DemoPikaqiuApplication配置如下:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DemoPikaqiuApplication {
public static void main(String[] args) {
SpringApplication.run(DemoPikaqiuApplication.class, args);
}
}
application.properties配置如下:
#---------------------使用pikaqiu连接池
jdbc.datasources=ds1,ds2
jdbc.ds1.url=jdbc:mysql://localhost:3306/shenyu?useUnicode=true&characterEncoding=utf8
jdbc.ds1.username=root
jdbc.ds1.password=123456
jdbc.ds1.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.ds1.initialSize=5
jdbc.ds1.minIdle=5
jdbc.ds1.maxActive=20
jdbc.ds1.maxWait=6000
jdbc.ds1.timeBetweenEvictionRunsMillis=2000
jdbc.ds1.minEvictableIdleTimeMillis=600000
jdbc.ds1.maxEvictableIdleTimeMillis=900000
jdbc.ds1.validationQuery=select 1
jdbc.ds1.testWhileIdle=true
jdbc.ds1.testOnBorrow=false
jdbc.ds1.testOnReturn=false
jdbc.ds1.keepAlive=true
jdbc.ds1.phyMaxUseCount=1000
jdbc.ds1.filters=stat
jdbc.ds2.url=jdbc:mysql://localhost:3306/soul?useUnicode=true&characterEncoding=utf8
jdbc.ds2.username=root
jdbc.ds2.password=123456
jdbc.ds2.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.ds2.initialSize=5
jdbc.ds2.minIdle=5
jdbc.ds2.maxActive=20
jdbc.ds2.maxWait=6000
jdbc.ds2.timeBetweenEvictionRunsMillis=2000
jdbc.ds2.minEvictableIdleTimeMillis=600000
jdbc.ds2.maxEvictableIdleTimeMillis=900000
jdbc.ds2.validationQuery=select 1
jdbc.ds2.testWhileIdle=true
jdbc.ds2.testOnBorrow=false
jdbc.ds2.testOnReturn=false
jdbc.ds2.keepAlive=true
jdbc.ds2.phyMaxUseCount=1000
jdbc.ds2.filters=stat
PikaqiuApplicationTests测试类如下:
@SpringBootTest
class PikaqiuApplicationTests {
@Autowired
private PikaqiuTestService pikaqiuTestService;
@Test
void test() {
pikaqiuTestService.insert();
}
}
最后运行PikaqiuApplicationTests,查看结果如下,可以查看到已插入成功:
github代码地址:https://github.com/renzhuyan/MY-JAVA/tree/master/pikaqiu
4 总结
到这里分布式事务的研究就告一段落了,通过研究弄明白了分布式事务的原理、流程以及代码的实现,相信接下来工作当中使用它会有非常大的帮助。