·1.创建连接,是跟普通连接不一样的,拿的XA的连接
像mysql,是这个com.mysql.jdbc.jdbc2.optional.MysqlXAConnection
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); class_5_connection = Class.forName("com.mysql.jdbc.Connection"); method_5_getPinGlobalTxToPhysicalConnection = class_5_connection.getMethod("getPinGlobalTxToPhysicalConnection"); class_5_suspendableXAConnection = Class.forName("com.mysql.jdbc.jdbc2.optional.SuspendableXAConnection"); constructor_5_suspendableXAConnection = class_5_suspendableXAConnection.getConstructor(class_5_connection); 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); } 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.DruidXADataSource除了实现XA的获取连接,其他都是继承DruidDataSource
public class DruidXADataSource extends DruidDataSource implements XADataSource { private final static Log LOG = LogFactory.getLog(DruidXADataSource.class); private static final long serialVersionUID = 1L; private Object h2Factory = null; @Override public XAConnection getXAConnection() throws SQLException { DruidPooledConnection conn = this.getConnection(); Connection physicalConn = conn.unwrap(Connection.class); XAConnection rawXAConnection = createPhysicalXAConnection(physicalConn); return new DruidPooledXAConnection(conn, rawXAConnection); } protected void initCheck() throws SQLException { super.initCheck(); DbType dbType = DbType.of(this.dbTypeName); if (JdbcUtils.H2.equals(dbType)) { h2Factory = H2Utils.createJdbcDataSourceFactory(); } } private XAConnection createPhysicalXAConnection(Connection physicalConn) throws SQLException { DbType dbType = DbType.of(this.dbTypeName); if (dbType == null) { throw new SQLException("xa not support dbType : " + this.dbTypeName); } switch (dbType) { case oracle: try { return OracleUtils.OracleXAConnection(physicalConn); } catch (XAException xae) { LOG.error("create xaConnection error", xae); return null; } case mysql: case mariadb: return MySqlUtils.createXAConnection(driver, physicalConn); case postgresql: return PGUtils.createXAConnection(physicalConn); case h2: return H2Utils.createXAConnection(h2Factory, physicalConn); case jtds: return new JtdsXAConnection(physicalConn); default: throw new SQLException("xa not support dbType : " + this.dbTypeName); } } @Override public XAConnection getXAConnection(String user, String password) throws SQLException { throw new UnsupportedOperationException("Not supported by DruidDataSource"); } }
3.XA事务的提交,是两阶段,XA的事务和本地的事务是互相独立的,XA结束前,本地事务不能提交;反之则一样
像mysql,步骤如下,引用官方,https://dev.mysql.com/doc/refman/5.7/en/xa-states.html
mysql> XA START 'xatest'; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO mytable (i) VALUES(10); Query OK, 1 row affected (0.04 sec) mysql> XA END 'xatest'; Query OK, 0 rows affected (0.00 sec) mysql> XA PREPARE 'xatest'; Query OK, 0 rows affected (0.00 sec) mysql> XA COMMIT 'xatest'; Query OK, 0 rows affected (0.00 sec)
3.PooledConnection是一个接口,java注释是提供连接器管理的钩子;DruidPooledConnection实现了这个接口,这个接口也有关闭方法,Druid也实现了这个方法,但是DruidDataSource回收连接,实际上不会调用这个方法的,目前也搜不到这个close方法最终哪里会调用
public void close() throws SQLException { if (this.disable) { return; } DruidConnectionHolder holder = this.holder; if (holder == null) { if (dupCloseLogEnable) { LOG.error("dup close"); } return; } DruidAbstractDataSource dataSource = holder.getDataSource(); boolean isSameThread = this.getOwnerThread() == Thread.currentThread(); if (!isSameThread) { dataSource.setAsyncCloseConnectionEnable(true); } if (dataSource.isAsyncCloseConnectionEnable()) { syncClose(); return; } if (!CLOSING_UPDATER.compareAndSet(this, 0, 1)) { return; } try { for (ConnectionEventListener listener : holder.getConnectionEventListeners()) { listener.connectionClosed(new ConnectionEvent(this)); } List<Filter> filters = dataSource.getProxyFilters(); if (filters.size() > 0) { FilterChainImpl filterChain = new FilterChainImpl(dataSource); filterChain.dataSource_recycle(this); } else { recycle(); } } finally { CLOSING_UPDATER.set(this, 0); } this.disable = true; }