| @@ -104,22 +104,22 @@ | |||
| ```xml | |||
| <!-- junit --> | |||
| <dependency> | |||
| <groupId>junit</groupId> | |||
| <artifactId>junit</artifactId> | |||
| <version>4.12</version> | |||
| <scope>test</scope> | |||
| <groupId>junit</groupId> | |||
| <artifactId>junit</artifactId> | |||
| <version>4.12</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <!-- dbcp --> | |||
| <dependency> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-dbcp2</artifactId> | |||
| <version>2.6.0</version> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-dbcp2</artifactId> | |||
| <version>2.6.0</version> | |||
| </dependency> | |||
| <!-- log4j --> | |||
| <dependency> | |||
| <groupId>log4j</groupId> | |||
| <artifactId>log4j</artifactId> | |||
| <version>1.2.17</version> | |||
| <groupId>log4j</groupId> | |||
| <artifactId>log4j</artifactId> | |||
| <version>1.2.17</version> | |||
| </dependency> | |||
| <!-- mysql驱动的jar包 --> | |||
| <dependency> | |||
| @@ -170,14 +170,14 @@ maxWaitMillis=-1 | |||
| 路径:`cn.zzs.dbcp` | |||
| ```java | |||
| // 导入配置文件 | |||
| Properties properties = new Properties(); | |||
| InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("dbcp.properties"); | |||
| properties.load(in); | |||
| // 根据配置文件内容获得数据源对象 | |||
| DataSource dataSource = BasicDataSourceFactory.createDataSource(properties); | |||
| // 获得连接 | |||
| Connection conn = dataSource.getConnection(); | |||
| // 导入配置文件 | |||
| Properties properties = new Properties(); | |||
| InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("dbcp.properties"); | |||
| properties.load(in); | |||
| // 根据配置文件内容获得数据源对象 | |||
| DataSource dataSource = BasicDataSourceFactory.createDataSource(properties); | |||
| // 获得连接 | |||
| Connection conn = dataSource.getConnection(); | |||
| ``` | |||
| ## 编写测试类 | |||
| @@ -185,34 +185,34 @@ maxWaitMillis=-1 | |||
| 这里以保存用户为例,路径test目录下的`cn.zzs.dbcp`。 | |||
| ```java | |||
| @Test | |||
| public void save() throws SQLException { | |||
| // 创建sql | |||
| String sql = "insert into demo_user values(null,?,?,?,?,?)"; | |||
| Connection connection = null; | |||
| PreparedStatement statement = null; | |||
| try { | |||
| // 获得连接 | |||
| connection = JDBCUtils.getConnection(); | |||
| // 开启事务设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| // 获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| // 设置参数 | |||
| statement.setString(1, "zzf003"); | |||
| statement.setInt(2, 18); | |||
| statement.setDate(3, new Date(System.currentTimeMillis())); | |||
| statement.setDate(4, new Date(System.currentTimeMillis())); | |||
| statement.setBoolean(5, false); | |||
| // 执行 | |||
| statement.executeUpdate(); | |||
| // 提交事务 | |||
| connection.commit(); | |||
| } finally { | |||
| // 释放资源 | |||
| JDBCUtils.release(connection, statement, null); | |||
| } | |||
| } | |||
| @Test | |||
| public void save() throws SQLException { | |||
| // 创建sql | |||
| String sql = "insert into demo_user values(null,?,?,?,?,?)"; | |||
| Connection connection = null; | |||
| PreparedStatement statement = null; | |||
| try { | |||
| // 获得连接 | |||
| connection = JDBCUtils.getConnection(); | |||
| // 开启事务设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| // 获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| // 设置参数 | |||
| statement.setString(1, "zzf003"); | |||
| statement.setInt(2, 18); | |||
| statement.setDate(3, new Date(System.currentTimeMillis())); | |||
| statement.setDate(4, new Date(System.currentTimeMillis())); | |||
| statement.setBoolean(5, false); | |||
| // 执行 | |||
| statement.executeUpdate(); | |||
| // 提交事务 | |||
| connection.commit(); | |||
| } finally { | |||
| // 释放资源 | |||
| JDBCUtils.release(connection, statement, null); | |||
| } | |||
| } | |||
| ``` | |||
| @@ -501,51 +501,51 @@ accessToUnderlyingConnectionAllowed=false | |||
| 5. 开启空闲资源回收线程(如果设置`timeBetweenEvictionRunsMillis`为正数)。 | |||
| ```java | |||
| protected DataSource createDataSource() throws SQLException { | |||
| if(closed) { | |||
| throw new SQLException("Data source is closed"); | |||
| } | |||
| if(dataSource != null) { | |||
| return dataSource; | |||
| } | |||
| synchronized(this) { | |||
| if(dataSource != null) { | |||
| return dataSource; | |||
| } | |||
| // 注册MBean,用于支持JMX,这方面的内容不在这里扩展 | |||
| jmxRegister(); | |||
| // 创建原生Connection工厂:本质就是持有数据库驱动对象和几个连接参数 | |||
| final ConnectionFactory driverConnectionFactory = createConnectionFactory(); | |||
| // 将driverConnectionFactory包装成池化Connection工厂 | |||
| PoolableConnectionFactory poolableConnectionFactory = createPoolableConnectionFactory(driverConnectionFactory); | |||
| // 设置PreparedStatements缓存(其实在这里可以发现,上面创建池化工厂时就设置了缓存,这里没必要再设置一遍) | |||
| poolableConnectionFactory.setPoolStatements(poolPreparedStatements); | |||
| poolableConnectionFactory.setMaxOpenPreparedStatements(maxOpenPreparedStatements); | |||
| // 创建数据库连接池对象GenericObjectPool,用于管理连接 | |||
| // BasicDataSource将持有GenericObjectPool对象 | |||
| createConnectionPool(poolableConnectionFactory); | |||
| // 创建PoolingDataSource对象 | |||
| // 该对象持有GenericObjectPool对象的引用 | |||
| DataSource newDataSource = createDataSourceInstance(); | |||
| newDataSource.setLogWriter(logWriter); | |||
| // 根据我们设置的initialSize创建初始连接 | |||
| for(int i = 0; i < initialSize; i++) { | |||
| connectionPool.addObject(); | |||
| } | |||
| // 开启连接池的evictor线程 | |||
| startPoolMaintenance(); | |||
| // 最后BasicDataSource将持有上面创建的PoolingDataSource对象 | |||
| dataSource = newDataSource; | |||
| return dataSource; | |||
| } | |||
| } | |||
| protected DataSource createDataSource() throws SQLException { | |||
| if(closed) { | |||
| throw new SQLException("Data source is closed"); | |||
| } | |||
| if(dataSource != null) { | |||
| return dataSource; | |||
| } | |||
| synchronized(this) { | |||
| if(dataSource != null) { | |||
| return dataSource; | |||
| } | |||
| // 注册MBean,用于支持JMX,这方面的内容不在这里扩展 | |||
| jmxRegister(); | |||
| // 创建原生Connection工厂:本质就是持有数据库驱动对象和几个连接参数 | |||
| final ConnectionFactory driverConnectionFactory = createConnectionFactory(); | |||
| // 将driverConnectionFactory包装成池化Connection工厂 | |||
| PoolableConnectionFactory poolableConnectionFactory = createPoolableConnectionFactory(driverConnectionFactory); | |||
| // 设置PreparedStatements缓存(其实在这里可以发现,上面创建池化工厂时就设置了缓存,这里没必要再设置一遍) | |||
| poolableConnectionFactory.setPoolStatements(poolPreparedStatements); | |||
| poolableConnectionFactory.setMaxOpenPreparedStatements(maxOpenPreparedStatements); | |||
| // 创建数据库连接池对象GenericObjectPool,用于管理连接 | |||
| // BasicDataSource将持有GenericObjectPool对象 | |||
| createConnectionPool(poolableConnectionFactory); | |||
| // 创建PoolingDataSource对象 | |||
| // 该对象持有GenericObjectPool对象的引用 | |||
| DataSource newDataSource = createDataSourceInstance(); | |||
| newDataSource.setLogWriter(logWriter); | |||
| // 根据我们设置的initialSize创建初始连接 | |||
| for(int i = 0; i < initialSize; i++) { | |||
| connectionPool.addObject(); | |||
| } | |||
| // 开启连接池的evictor线程 | |||
| startPoolMaintenance(); | |||
| // 最后BasicDataSource将持有上面创建的PoolingDataSource对象 | |||
| dataSource = newDataSource; | |||
| return dataSource; | |||
| } | |||
| } | |||
| ``` | |||
| 以上方法涉及到几个类,这里再补充下`UML`图。 | |||
| @@ -1154,24 +1154,24 @@ accessToUnderlyingConnectionAllowed=false | |||
| 本文在前面例子的基础上增加以下依赖,因为是web项目,所以打包方式为`war`: | |||
| ```xml | |||
| <dependency> | |||
| <groupId>javax.servlet</groupId> | |||
| <artifactId>jstl</artifactId> | |||
| <version>1.2</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>javax.servlet</groupId> | |||
| <artifactId>javax.servlet-api</artifactId> | |||
| <version>3.1.0</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>javax.servlet.jsp</groupId> | |||
| <artifactId>javax.servlet.jsp-api</artifactId> | |||
| <version>2.2.1</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>javax.servlet</groupId> | |||
| <artifactId>jstl</artifactId> | |||
| <version>1.2</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>javax.servlet</groupId> | |||
| <artifactId>javax.servlet-api</artifactId> | |||
| <version>3.1.0</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>javax.servlet.jsp</groupId> | |||
| <artifactId>javax.servlet.jsp-api</artifactId> | |||
| <version>2.2.1</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| ``` | |||
| ## 编写context.xml | |||
| @@ -1181,20 +1181,20 @@ accessToUnderlyingConnectionAllowed=false | |||
| ```xml | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <Context> | |||
| <Resource | |||
| name="bean/SharedPoolDataSourceFactory" | |||
| auth="Container" | |||
| type="org.apache.commons.dbcp2.datasources.SharedPoolDataSource" | |||
| factory="org.apache.commons.dbcp2.datasources.SharedPoolDataSourceFactory" | |||
| singleton="false" | |||
| driverClassName="com.mysql.cj.jdbc.Driver" | |||
| url="jdbc:mysql://localhost:3306/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true" | |||
| username="root" | |||
| password="root" | |||
| maxTotal="8" | |||
| maxIdle="10" | |||
| dataSourceName="java:comp/env/bean/DriverAdapterCPDS" | |||
| /> | |||
| <Resource | |||
| name="bean/SharedPoolDataSourceFactory" | |||
| auth="Container" | |||
| type="org.apache.commons.dbcp2.datasources.SharedPoolDataSource" | |||
| factory="org.apache.commons.dbcp2.datasources.SharedPoolDataSourceFactory" | |||
| singleton="false" | |||
| driverClassName="com.mysql.cj.jdbc.Driver" | |||
| url="jdbc:mysql://localhost:3306/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true" | |||
| username="root" | |||
| password="root" | |||
| maxTotal="8" | |||
| maxIdle="10" | |||
| dataSourceName="java:comp/env/bean/DriverAdapterCPDS" | |||
| /> | |||
| <Resource | |||
| name="bean/PerUserPoolDataSourceFactory" | |||
| auth="Container" | |||
| @@ -1208,7 +1208,7 @@ accessToUnderlyingConnectionAllowed=false | |||
| maxTotal="8" | |||
| maxIdle="10" | |||
| dataSourceName="java:comp/env/bean/DriverAdapterCPDS" | |||
| /> | |||
| /> | |||
| <Resource | |||
| name="bean/DriverAdapterCPDS" | |||
| auth="Container" | |||
| @@ -1220,7 +1220,7 @@ accessToUnderlyingConnectionAllowed=false | |||
| userName="root" | |||
| userPassword="root" | |||
| maxIdle="10" | |||
| /> | |||
| /> | |||
| </Context> | |||
| ``` | |||
| @@ -1233,17 +1233,17 @@ accessToUnderlyingConnectionAllowed=false | |||
| <description>Test DriverAdapterCPDS</description> | |||
| <resource-env-ref-name>bean/DriverAdapterCPDS</resource-env-ref-name> | |||
| <resource-env-ref-type>org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS</resource-env-ref-type> | |||
| </resource-env-ref> | |||
| <resource-env-ref> | |||
| <description>Test SharedPoolDataSource</description> | |||
| <resource-env-ref-name>bean/SharedPoolDataSourceFactory</resource-env-ref-name> | |||
| <resource-env-ref-type>org.apache.commons.dbcp2.datasources.SharedPoolDataSource</resource-env-ref-type> | |||
| </resource-env-ref> | |||
| </resource-env-ref> | |||
| <resource-env-ref> | |||
| <description>Test SharedPoolDataSource</description> | |||
| <resource-env-ref-name>bean/SharedPoolDataSourceFactory</resource-env-ref-name> | |||
| <resource-env-ref-type>org.apache.commons.dbcp2.datasources.SharedPoolDataSource</resource-env-ref-type> | |||
| </resource-env-ref> | |||
| <resource-env-ref> | |||
| <description>Test erUserPoolDataSource</description> | |||
| <resource-env-ref-name>bean/erUserPoolDataSourceFactory</resource-env-ref-name> | |||
| <resource-env-ref-type>org.apache.commons.dbcp2.datasources.erUserPoolDataSource</resource-env-ref-type> | |||
| </resource-env-ref> | |||
| </resource-env-ref> | |||
| ``` | |||
| ## 编写jsp | |||
| @@ -1254,22 +1254,22 @@ accessToUnderlyingConnectionAllowed=false | |||
| <body> | |||
| <% | |||
| // 获得名称服务的上下文对象 | |||
| Context initCtx = new InitialContext(); | |||
| Context envCtx = (Context)initCtx.lookup("java:comp/env/"); | |||
| // 查找指定名字的对象 | |||
| DataSource ds = (DataSource)envCtx.lookup("bean/SharedPoolDataSourceFactory"); | |||
| DataSource ds2 = (DataSource)envCtx.lookup("bean/PerUserPoolDataSourceFactory"); | |||
| // 获取连接 | |||
| Connection conn = ds.getConnection("root","root"); | |||
| System.out.println("conn" + conn); | |||
| Context initCtx = new InitialContext(); | |||
| Context envCtx = (Context)initCtx.lookup("java:comp/env/"); | |||
| // 查找指定名字的对象 | |||
| DataSource ds = (DataSource)envCtx.lookup("bean/SharedPoolDataSourceFactory"); | |||
| DataSource ds2 = (DataSource)envCtx.lookup("bean/PerUserPoolDataSourceFactory"); | |||
| // 获取连接 | |||
| Connection conn = ds.getConnection("root","root"); | |||
| System.out.println("conn" + conn); | |||
| Connection conn2 = ds2.getConnection("zzf","zzf"); | |||
| System.out.println("conn2" + conn2); | |||
| // ... 使用连接操作数据库,以及释放资源 ... | |||
| conn.close(); | |||
| conn2.close(); | |||
| conn.close(); | |||
| conn2.close(); | |||
| %> | |||
| </body> | |||
| ``` | |||
| @@ -1325,12 +1325,12 @@ XA RECOVER; -- 查看处于prepare状态的事务列表 | |||
| ```xml | |||
| <!-- jta:用于测试DBCP对JTA事务的支持 --> | |||
| <dependency> | |||
| <groupId>javax.transaction</groupId> | |||
| <artifactId>jta</artifactId> | |||
| <version>1.1</version> | |||
| </dependency> | |||
| <dependency> | |||
| <dependency> | |||
| <groupId>javax.transaction</groupId> | |||
| <artifactId>jta</artifactId> | |||
| <version>1.1</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.atomikos</groupId> | |||
| <artifactId>transactions-jdbc</artifactId> | |||
| <version>3.9.3</version> | |||
| @@ -1342,42 +1342,42 @@ XA RECOVER; -- 查看处于prepare状态的事务列表 | |||
| 这里千万记得要设置`DefaultCatalog`,否则当前事务中注册不同资源管理器时,可能都会被当成同一个资源管理器而拒绝注册并报错,因为这个问题,花了我好长时间才解决。 | |||
| ```java | |||
| public BasicManagedDataSource getBasicManagedDataSource( | |||
| TransactionManager transactionManager, | |||
| String url, | |||
| String username, | |||
| String password) { | |||
| BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource(); | |||
| basicManagedDataSource.setTransactionManager(transactionManager); | |||
| basicManagedDataSource.setUrl(url); | |||
| basicManagedDataSource.setUsername(username); | |||
| basicManagedDataSource.setPassword(password); | |||
| basicManagedDataSource.setDefaultAutoCommit(false); | |||
| basicManagedDataSource.setXADataSource("com.mysql.cj.jdbc.MysqlXADataSource"); | |||
| return basicManagedDataSource; | |||
| } | |||
| @Test | |||
| public void test01() throws Exception { | |||
| // 获得事务管理器 | |||
| TransactionManager transactionManager = new UserTransactionManager(); | |||
| // 获取第一个数据库的数据源 | |||
| BasicManagedDataSource basicManagedDataSource1 = getBasicManagedDataSource( | |||
| transactionManager, | |||
| "jdbc:mysql://localhost:3306/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true", | |||
| "root", | |||
| "root"); | |||
| // 注意,这一步非常重要 | |||
| basicManagedDataSource1.setDefaultCatalog("github_demo"); | |||
| // 获取第二个数据库的数据源 | |||
| BasicManagedDataSource basicManagedDataSource2 = getBasicManagedDataSource( | |||
| transactionManager, | |||
| "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true", | |||
| "zzf", | |||
| "zzf"); | |||
| // 注意,这一步非常重要 | |||
| basicManagedDataSource1.setDefaultCatalog("test"); | |||
| public BasicManagedDataSource getBasicManagedDataSource( | |||
| TransactionManager transactionManager, | |||
| String url, | |||
| String username, | |||
| String password) { | |||
| BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource(); | |||
| basicManagedDataSource.setTransactionManager(transactionManager); | |||
| basicManagedDataSource.setUrl(url); | |||
| basicManagedDataSource.setUsername(username); | |||
| basicManagedDataSource.setPassword(password); | |||
| basicManagedDataSource.setDefaultAutoCommit(false); | |||
| basicManagedDataSource.setXADataSource("com.mysql.cj.jdbc.MysqlXADataSource"); | |||
| return basicManagedDataSource; | |||
| } | |||
| @Test | |||
| public void test01() throws Exception { | |||
| // 获得事务管理器 | |||
| TransactionManager transactionManager = new UserTransactionManager(); | |||
| // 获取第一个数据库的数据源 | |||
| BasicManagedDataSource basicManagedDataSource1 = getBasicManagedDataSource( | |||
| transactionManager, | |||
| "jdbc:mysql://localhost:3306/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true", | |||
| "root", | |||
| "root"); | |||
| // 注意,这一步非常重要 | |||
| basicManagedDataSource1.setDefaultCatalog("github_demo"); | |||
| // 获取第二个数据库的数据源 | |||
| BasicManagedDataSource basicManagedDataSource2 = getBasicManagedDataSource( | |||
| transactionManager, | |||
| "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true", | |||
| "zzf", | |||
| "zzf"); | |||
| // 注意,这一步非常重要 | |||
| basicManagedDataSource1.setDefaultCatalog("test"); | |||
| } | |||
| ``` | |||
| @@ -1386,56 +1386,56 @@ XA RECOVER; -- 查看处于prepare状态的事务列表 | |||
| 通过运行代码可以发现,当数据库1和2的操作都成功,才会提交,只要其中一个数据库执行失败,两个操作都会回滚。 | |||
| ```java | |||
| @Test | |||
| public void test01() throws Exception { | |||
| Connection connection1 = null; | |||
| Statement statement1 = null; | |||
| Connection connection2 = null; | |||
| Statement statement2 = null; | |||
| transactionManager.begin(); | |||
| try { | |||
| // 获取连接并进行数据库操作,这里会将会将XAResource注册到当前线程的XA事务对象 | |||
| /** | |||
| * XA START xid1;-- 启动一个事务,并使之为active状态 | |||
| */ | |||
| connection1 = basicManagedDataSource1.getConnection(); | |||
| statement1 = connection1.createStatement(); | |||
| /** | |||
| * update github_demo.demo_user set deleted = 1 where id = '1'; -- 事务中的语句 | |||
| */ | |||
| boolean result1 = statement1.execute("update github_demo.demo_user set deleted = 1 where id = '1'"); | |||
| System.out.println(result1); | |||
| /** | |||
| * XA START xid2;-- 启动一个事务,并使之为active状态 | |||
| */ | |||
| connection2 = basicManagedDataSource2.getConnection(); | |||
| statement2 = connection2.createStatement(); | |||
| /** | |||
| * update test.demo_user set deleted = 1 where id = '1'; -- 事务中的语句 | |||
| */ | |||
| boolean result2 = statement2.execute("update test.demo_user set deleted = 1 where id = '1'"); | |||
| System.out.println(result2); | |||
| /** | |||
| * 当这执行以下语句: | |||
| * XA END xid1; -- 把事务置为idle状态 | |||
| * XA PREPARE xid1; -- 把事务置为prepare状态 | |||
| * XA END xid2; -- 把事务置为idle状态 | |||
| * XA PREPARE xid2; -- 把事务置为prepare状态 | |||
| * XA COMMIT xid1; -- 提交事务 | |||
| * XA COMMIT xid2; -- 提交事务 | |||
| */ | |||
| transactionManager.commit(); | |||
| } catch(Exception e) { | |||
| e.printStackTrace(); | |||
| } finally { | |||
| statement1.close(); | |||
| statement2.close(); | |||
| connection1.close(); | |||
| connection2.close(); | |||
| } | |||
| } | |||
| @Test | |||
| public void test01() throws Exception { | |||
| Connection connection1 = null; | |||
| Statement statement1 = null; | |||
| Connection connection2 = null; | |||
| Statement statement2 = null; | |||
| transactionManager.begin(); | |||
| try { | |||
| // 获取连接并进行数据库操作,这里会将会将XAResource注册到当前线程的XA事务对象 | |||
| /** | |||
| * XA START xid1;-- 启动一个事务,并使之为active状态 | |||
| */ | |||
| connection1 = basicManagedDataSource1.getConnection(); | |||
| statement1 = connection1.createStatement(); | |||
| /** | |||
| * update github_demo.demo_user set deleted = 1 where id = '1'; -- 事务中的语句 | |||
| */ | |||
| boolean result1 = statement1.execute("update github_demo.demo_user set deleted = 1 where id = '1'"); | |||
| System.out.println(result1); | |||
| /** | |||
| * XA START xid2;-- 启动一个事务,并使之为active状态 | |||
| */ | |||
| connection2 = basicManagedDataSource2.getConnection(); | |||
| statement2 = connection2.createStatement(); | |||
| /** | |||
| * update test.demo_user set deleted = 1 where id = '1'; -- 事务中的语句 | |||
| */ | |||
| boolean result2 = statement2.execute("update test.demo_user set deleted = 1 where id = '1'"); | |||
| System.out.println(result2); | |||
| /** | |||
| * 当这执行以下语句: | |||
| * XA END xid1; -- 把事务置为idle状态 | |||
| * XA PREPARE xid1; -- 把事务置为prepare状态 | |||
| * XA END xid2; -- 把事务置为idle状态 | |||
| * XA PREPARE xid2; -- 把事务置为prepare状态 | |||
| * XA COMMIT xid1; -- 提交事务 | |||
| * XA COMMIT xid2; -- 提交事务 | |||
| */ | |||
| transactionManager.commit(); | |||
| } catch(Exception e) { | |||
| e.printStackTrace(); | |||
| } finally { | |||
| statement1.close(); | |||
| statement2.close(); | |||
| connection1.close(); | |||
| connection2.close(); | |||
| } | |||
| } | |||
| ``` | |||
| > 相关源码请移步:https://github.com/ZhangZiSheng001/dbcp-demo | |||
| @@ -1,68 +1,67 @@ | |||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <groupId>cn.zzs</groupId> | |||
| <artifactId>DBCP-demo</artifactId> | |||
| <packaging>war</packaging> | |||
| <version>1.0.0</version> | |||
| <name>DBCP-demo</name> | |||
| <url>http://maven.apache.org</url> | |||
| <dependencies> | |||
| <!-- junit --> | |||
| <dependency> | |||
| <groupId>junit</groupId> | |||
| <artifactId>junit</artifactId> | |||
| <version>4.12</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <!-- dbcp --> | |||
| <dependency> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-dbcp2</artifactId> | |||
| <version>2.6.0</version> | |||
| </dependency> | |||
| <!-- log4j --> | |||
| <dependency> | |||
| <groupId>log4j</groupId> | |||
| <artifactId>log4j</artifactId> | |||
| <version>1.2.17</version> | |||
| </dependency> | |||
| <!-- mysql驱动的jar包 --> | |||
| <dependency> | |||
| <groupId>mysql</groupId> | |||
| <artifactId>mysql-connector-java</artifactId> | |||
| <version>8.0.15</version> | |||
| </dependency> | |||
| <!-- JSP相关:用于测试DBCP对jndi的支持 --> | |||
| <dependency> | |||
| <groupId>javax.servlet</groupId> | |||
| <artifactId>jstl</artifactId> | |||
| <version>1.2</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>javax.servlet</groupId> | |||
| <artifactId>javax.servlet-api</artifactId> | |||
| <version>3.1.0</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>javax.servlet.jsp</groupId> | |||
| <artifactId>javax.servlet.jsp-api</artifactId> | |||
| <version>2.2.1</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <groupId>cn.zzs</groupId> | |||
| <artifactId>DBCP-demo</artifactId> | |||
| <packaging>war</packaging> | |||
| <version>1.0.0</version> | |||
| <name>DBCP-demo</name> | |||
| <url>http://maven.apache.org</url> | |||
| <dependencies> | |||
| <!-- junit --> | |||
| <dependency> | |||
| <groupId>junit</groupId> | |||
| <artifactId>junit</artifactId> | |||
| <version>4.12</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <!-- dbcp --> | |||
| <dependency> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-dbcp2</artifactId> | |||
| <version>2.6.0</version> | |||
| </dependency> | |||
| <!-- log4j --> | |||
| <dependency> | |||
| <groupId>log4j</groupId> | |||
| <artifactId>log4j</artifactId> | |||
| <version>1.2.17</version> | |||
| </dependency> | |||
| <!-- mysql驱动的jar包 --> | |||
| <dependency> | |||
| <groupId>mysql</groupId> | |||
| <artifactId>mysql-connector-java</artifactId> | |||
| <version>8.0.15</version> | |||
| </dependency> | |||
| <!-- JSP相关:用于测试DBCP对jndi的支持 --> | |||
| <dependency> | |||
| <groupId>javax.servlet</groupId> | |||
| <artifactId>jstl</artifactId> | |||
| <version>1.2</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>javax.servlet</groupId> | |||
| <artifactId>javax.servlet-api</artifactId> | |||
| <version>3.1.0</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>javax.servlet.jsp</groupId> | |||
| <artifactId>javax.servlet.jsp-api</artifactId> | |||
| <version>2.2.1</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <!-- jta:用于测试DBCP对JTA事务的支持 --> | |||
| <dependency> | |||
| <groupId>javax.transaction</groupId> | |||
| <artifactId>jta</artifactId> | |||
| <version>1.1</version> | |||
| </dependency> | |||
| <dependency> | |||
| <dependency> | |||
| <groupId>javax.transaction</groupId> | |||
| <artifactId>jta</artifactId> | |||
| <version>1.1</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.atomikos</groupId> | |||
| <artifactId>transactions-jdbc</artifactId> | |||
| <version>3.9.3</version> | |||
| </dependency> | |||
| </dependencies> | |||
| </dependencies> | |||
| </project> | |||
| @@ -20,107 +20,107 @@ import org.apache.commons.logging.LogFactory; | |||
| */ | |||
| public class JDBCUtils { | |||
| private static DataSource dataSource; | |||
| private static DataSource dataSource; | |||
| private static ThreadLocal<Connection> tl = new ThreadLocal<>(); | |||
| private static ThreadLocal<Connection> tl = new ThreadLocal<>(); | |||
| private static final Object obj = new Object(); | |||
| private static final Object obj = new Object(); | |||
| private static final Log log = LogFactory.getLog(JDBCUtils.class); | |||
| private static final Log log = LogFactory.getLog(JDBCUtils.class); | |||
| static { | |||
| init(); | |||
| } | |||
| static { | |||
| init(); | |||
| } | |||
| /** | |||
| * | |||
| * <p>获取数据库连接对象的方法,线程安全</p> | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:22:29 | |||
| * @return: Connection | |||
| */ | |||
| public static Connection getConnection() throws SQLException { | |||
| // 从当前线程中获取连接对象 | |||
| Connection connection = tl.get(); | |||
| // 判断为空的话,创建连接并绑定到当前线程 | |||
| if(connection == null) { | |||
| synchronized(obj) { | |||
| if((connection = tl.get()) == null) { | |||
| connection = createConnection(); | |||
| tl.set(connection); | |||
| } | |||
| } | |||
| } | |||
| return connection; | |||
| } | |||
| /** | |||
| * | |||
| * <p>获取数据库连接对象的方法,线程安全</p> | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:22:29 | |||
| * @return: Connection | |||
| */ | |||
| public static Connection getConnection() throws SQLException { | |||
| // 从当前线程中获取连接对象 | |||
| Connection connection = tl.get(); | |||
| // 判断为空的话,创建连接并绑定到当前线程 | |||
| if(connection == null) { | |||
| synchronized(obj) { | |||
| if((connection = tl.get()) == null) { | |||
| connection = createConnection(); | |||
| tl.set(connection); | |||
| } | |||
| } | |||
| } | |||
| return connection; | |||
| } | |||
| /** | |||
| * | |||
| * <p>释放资源</p> | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:39:24 | |||
| * @param conn | |||
| * @param statement | |||
| * @return: void | |||
| */ | |||
| public static void release(Connection conn, Statement statement, ResultSet resultSet) { | |||
| if(resultSet != null) { | |||
| try { | |||
| resultSet.close(); | |||
| } catch(SQLException e) { | |||
| log.error("关闭ResultSet对象异常", e); | |||
| } | |||
| } | |||
| if(statement != null) { | |||
| try { | |||
| statement.close(); | |||
| } catch(SQLException e) { | |||
| log.error("关闭Statement对象异常", e); | |||
| } | |||
| } | |||
| // 注意:这里不关闭连接 | |||
| if(conn != null) { | |||
| try { | |||
| conn.close(); | |||
| tl.remove(); | |||
| } catch(SQLException e) { | |||
| log.error("关闭Connection对象异常", e); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * | |||
| * <p>释放资源</p> | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:39:24 | |||
| * @param conn | |||
| * @param statement | |||
| * @return: void | |||
| */ | |||
| public static void release(Connection conn, Statement statement, ResultSet resultSet) { | |||
| if(resultSet != null) { | |||
| try { | |||
| resultSet.close(); | |||
| } catch(SQLException e) { | |||
| log.error("关闭ResultSet对象异常", e); | |||
| } | |||
| } | |||
| if(statement != null) { | |||
| try { | |||
| statement.close(); | |||
| } catch(SQLException e) { | |||
| log.error("关闭Statement对象异常", e); | |||
| } | |||
| } | |||
| // 注意:这里不关闭连接 | |||
| if(conn != null) { | |||
| try { | |||
| conn.close(); | |||
| tl.remove(); | |||
| } catch(SQLException e) { | |||
| log.error("关闭Connection对象异常", e); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * | |||
| * <p>创建数据库连接</p> | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:27:03 | |||
| * @return: Connection | |||
| * @throws SQLException | |||
| */ | |||
| private static Connection createConnection() throws SQLException { | |||
| Connection conn = null; | |||
| // 获得连接 | |||
| conn = dataSource.getConnection(); | |||
| return conn; | |||
| } | |||
| /** | |||
| * | |||
| * <p>创建数据库连接</p> | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:27:03 | |||
| * @return: Connection | |||
| * @throws SQLException | |||
| */ | |||
| private static Connection createConnection() throws SQLException { | |||
| Connection conn = null; | |||
| // 获得连接 | |||
| conn = dataSource.getConnection(); | |||
| return conn; | |||
| } | |||
| /** | |||
| * <p>根据指定配置文件创建数据源对象</p> | |||
| * @author: zzs | |||
| * @date: 2019年9月1日 上午10:53:05 | |||
| * @return: void | |||
| * @throws Exception | |||
| */ | |||
| private static void init() { | |||
| // 导入配置文件 | |||
| Properties properties = new Properties(); | |||
| InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("dbcp.properties"); | |||
| try { | |||
| properties.load(in); | |||
| // 根据配置文件内容获得数据源对象 | |||
| dataSource = BasicDataSourceFactory.createDataSource(properties); | |||
| } catch(Exception ex) { | |||
| throw new RuntimeException("根据指定配置文件创建数据源出错", ex); | |||
| } | |||
| } | |||
| /** | |||
| * <p>根据指定配置文件创建数据源对象</p> | |||
| * @author: zzs | |||
| * @date: 2019年9月1日 上午10:53:05 | |||
| * @return: void | |||
| * @throws Exception | |||
| */ | |||
| private static void init() { | |||
| // 导入配置文件 | |||
| Properties properties = new Properties(); | |||
| InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("dbcp.properties"); | |||
| try { | |||
| properties.load(in); | |||
| // 根据配置文件内容获得数据源对象 | |||
| dataSource = BasicDataSourceFactory.createDataSource(properties); | |||
| } catch(Exception ex) { | |||
| throw new RuntimeException("根据指定配置文件创建数据源出错", ex); | |||
| } | |||
| } | |||
| } | |||
| @@ -15,126 +15,126 @@ import org.junit.Test; | |||
| */ | |||
| public class BasicDataSourceTest { | |||
| /** | |||
| * <p>测试添加用户</p> | |||
| * @throws SQLException | |||
| */ | |||
| @Test | |||
| public void save() throws SQLException { | |||
| // 创建sql | |||
| String sql = "insert into demo_user values(null,?,?,?,?,?)"; | |||
| Connection connection = null; | |||
| PreparedStatement statement = null; | |||
| try { | |||
| // 获得连接 | |||
| connection = JDBCUtils.getConnection(); | |||
| // 开启事务设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| // 获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| // 设置参数 | |||
| statement.setString(1, "zzf003"); | |||
| statement.setInt(2, 18); | |||
| statement.setDate(3, new Date(System.currentTimeMillis())); | |||
| statement.setDate(4, new Date(System.currentTimeMillis())); | |||
| statement.setBoolean(5, false); | |||
| // 执行 | |||
| statement.executeUpdate(); | |||
| // 提交事务 | |||
| connection.commit(); | |||
| } finally { | |||
| // 释放资源 | |||
| JDBCUtils.release(connection, statement, null); | |||
| } | |||
| } | |||
| /** | |||
| * <p>测试添加用户</p> | |||
| * @throws SQLException | |||
| */ | |||
| @Test | |||
| public void save() throws SQLException { | |||
| // 创建sql | |||
| String sql = "insert into demo_user values(null,?,?,?,?,?)"; | |||
| Connection connection = null; | |||
| PreparedStatement statement = null; | |||
| try { | |||
| // 获得连接 | |||
| connection = JDBCUtils.getConnection(); | |||
| // 开启事务设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| // 获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| // 设置参数 | |||
| statement.setString(1, "zzf003"); | |||
| statement.setInt(2, 18); | |||
| statement.setDate(3, new Date(System.currentTimeMillis())); | |||
| statement.setDate(4, new Date(System.currentTimeMillis())); | |||
| statement.setBoolean(5, false); | |||
| // 执行 | |||
| statement.executeUpdate(); | |||
| // 提交事务 | |||
| connection.commit(); | |||
| } finally { | |||
| // 释放资源 | |||
| JDBCUtils.release(connection, statement, null); | |||
| } | |||
| } | |||
| /** | |||
| * <p>测试更新用户</p> | |||
| * @throws SQLException | |||
| */ | |||
| @Test | |||
| public void update() throws SQLException { | |||
| // 创建sql | |||
| String sql = "update demo_user set age = ?,gmt_modified = ? where name = ?"; | |||
| Connection connection = null; | |||
| PreparedStatement statement = null; | |||
| try { | |||
| // 获得连接 | |||
| connection = JDBCUtils.getConnection(); | |||
| // 开启事务 | |||
| connection.setAutoCommit(false); | |||
| // 获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| // 设置参数 | |||
| statement.setInt(1, 19); | |||
| statement.setDate(2, new Date(System.currentTimeMillis())); | |||
| statement.setString(3, "zzf003"); | |||
| // 执行 | |||
| statement.executeUpdate(); | |||
| // 提交事务 | |||
| connection.commit(); | |||
| } finally { | |||
| // 释放资源 | |||
| JDBCUtils.release(connection, statement, null); | |||
| } | |||
| } | |||
| /** | |||
| * <p>测试更新用户</p> | |||
| * @throws SQLException | |||
| */ | |||
| @Test | |||
| public void update() throws SQLException { | |||
| // 创建sql | |||
| String sql = "update demo_user set age = ?,gmt_modified = ? where name = ?"; | |||
| Connection connection = null; | |||
| PreparedStatement statement = null; | |||
| try { | |||
| // 获得连接 | |||
| connection = JDBCUtils.getConnection(); | |||
| // 开启事务 | |||
| connection.setAutoCommit(false); | |||
| // 获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| // 设置参数 | |||
| statement.setInt(1, 19); | |||
| statement.setDate(2, new Date(System.currentTimeMillis())); | |||
| statement.setString(3, "zzf003"); | |||
| // 执行 | |||
| statement.executeUpdate(); | |||
| // 提交事务 | |||
| connection.commit(); | |||
| } finally { | |||
| // 释放资源 | |||
| JDBCUtils.release(connection, statement, null); | |||
| } | |||
| } | |||
| /** | |||
| * <p>测试查找用户</p> | |||
| * @throws SQLException | |||
| */ | |||
| @Test | |||
| public void findAll() throws SQLException { | |||
| // 创建sql | |||
| String sql = "select * from demo_user where deleted = false"; | |||
| Connection connection = null; | |||
| PreparedStatement statement = null; | |||
| ResultSet resultSet = null; | |||
| try { | |||
| // 获得连接 | |||
| connection = JDBCUtils.getConnection(); | |||
| // 获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| // 执行 | |||
| resultSet = statement.executeQuery(); | |||
| // 遍历结果集 | |||
| while(resultSet.next()) { | |||
| String name = resultSet.getString(2); | |||
| int age = resultSet.getInt(3); | |||
| System.out.println("用户名:" + name + ",年龄:" + age); | |||
| } | |||
| } finally { | |||
| // 释放资源 | |||
| JDBCUtils.release(connection, statement, resultSet); | |||
| } | |||
| } | |||
| /** | |||
| * <p>测试查找用户</p> | |||
| * @throws SQLException | |||
| */ | |||
| @Test | |||
| public void findAll() throws SQLException { | |||
| // 创建sql | |||
| String sql = "select * from demo_user where deleted = false"; | |||
| Connection connection = null; | |||
| PreparedStatement statement = null; | |||
| ResultSet resultSet = null; | |||
| try { | |||
| // 获得连接 | |||
| connection = JDBCUtils.getConnection(); | |||
| // 获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| // 执行 | |||
| resultSet = statement.executeQuery(); | |||
| // 遍历结果集 | |||
| while(resultSet.next()) { | |||
| String name = resultSet.getString(2); | |||
| int age = resultSet.getInt(3); | |||
| System.out.println("用户名:" + name + ",年龄:" + age); | |||
| } | |||
| } finally { | |||
| // 释放资源 | |||
| JDBCUtils.release(connection, statement, resultSet); | |||
| } | |||
| } | |||
| /** | |||
| * <p>测试删除用户</p> | |||
| */ | |||
| @Test | |||
| public void delete() throws Exception { | |||
| // 创建sql | |||
| String sql = "delete from demo_user where name = ?"; | |||
| Connection connection = null; | |||
| PreparedStatement statement = null; | |||
| try { | |||
| // 获得连接 | |||
| connection = JDBCUtils.getConnection(); | |||
| // 设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| // 获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| // 设置参数 | |||
| statement.setString(1, "zzf003"); | |||
| // 执行 | |||
| statement.executeUpdate(); | |||
| // 提交事务 | |||
| connection.commit(); | |||
| } finally { | |||
| // 释放资源 | |||
| JDBCUtils.release(connection, statement, null); | |||
| } | |||
| } | |||
| /** | |||
| * <p>测试删除用户</p> | |||
| */ | |||
| @Test | |||
| public void delete() throws Exception { | |||
| // 创建sql | |||
| String sql = "delete from demo_user where name = ?"; | |||
| Connection connection = null; | |||
| PreparedStatement statement = null; | |||
| try { | |||
| // 获得连接 | |||
| connection = JDBCUtils.getConnection(); | |||
| // 设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| // 获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| // 设置参数 | |||
| statement.setString(1, "zzf003"); | |||
| // 执行 | |||
| statement.executeUpdate(); | |||
| // 提交事务 | |||
| connection.commit(); | |||
| } finally { | |||
| // 释放资源 | |||
| JDBCUtils.release(connection, statement, null); | |||
| } | |||
| } | |||
| } | |||
| @@ -17,91 +17,78 @@ import com.atomikos.icatch.jta.UserTransactionManager; | |||
| */ | |||
| public class BasicManagedDataSourceTest { | |||
| public BasicManagedDataSource getBasicManagedDataSource( | |||
| TransactionManager transactionManager, | |||
| String url, | |||
| String username, | |||
| String password) { | |||
| BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource(); | |||
| basicManagedDataSource.setTransactionManager(transactionManager); | |||
| basicManagedDataSource.setUrl(url); | |||
| basicManagedDataSource.setUsername(username); | |||
| basicManagedDataSource.setPassword(password); | |||
| basicManagedDataSource.setDefaultAutoCommit(false); | |||
| basicManagedDataSource.setXADataSource("com.mysql.cj.jdbc.MysqlXADataSource"); | |||
| return basicManagedDataSource; | |||
| } | |||
| public BasicManagedDataSource getBasicManagedDataSource(TransactionManager transactionManager, String url, String username, String password) { | |||
| BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource(); | |||
| basicManagedDataSource.setTransactionManager(transactionManager); | |||
| basicManagedDataSource.setUrl(url); | |||
| basicManagedDataSource.setUsername(username); | |||
| basicManagedDataSource.setPassword(password); | |||
| basicManagedDataSource.setDefaultAutoCommit(false); | |||
| basicManagedDataSource.setXADataSource("com.mysql.cj.jdbc.MysqlXADataSource"); | |||
| return basicManagedDataSource; | |||
| } | |||
| @Test | |||
| public void test01() throws Exception { | |||
| // 获得事务管理器 | |||
| TransactionManager transactionManager = new UserTransactionManager(); | |||
| // 获取第一个数据库的数据源 | |||
| BasicManagedDataSource basicManagedDataSource1 = getBasicManagedDataSource( | |||
| transactionManager, | |||
| "jdbc:mysql://localhost:3306/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true", | |||
| "root", | |||
| "root"); | |||
| // 注意,这一步非常重要 | |||
| basicManagedDataSource1.setDefaultCatalog("github_demo"); | |||
| // 获取第二个数据库的数据源 | |||
| BasicManagedDataSource basicManagedDataSource2 = getBasicManagedDataSource( | |||
| transactionManager, | |||
| "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true", | |||
| "zzf", | |||
| "zzf"); | |||
| // 注意,这一步非常重要 | |||
| basicManagedDataSource1.setDefaultCatalog("test"); | |||
| Connection connection1 = null; | |||
| Statement statement1 = null; | |||
| Connection connection2 = null; | |||
| Statement statement2 = null; | |||
| transactionManager.begin(); | |||
| try { | |||
| // 获取连接并进行数据库操作,这里会将会将XAResource注册到当前线程的XA事务对象 | |||
| /** | |||
| * XA START xid1;-- 启动一个事务,并使之为active状态 | |||
| */ | |||
| connection1 = basicManagedDataSource1.getConnection(); | |||
| statement1 = connection1.createStatement(); | |||
| /** | |||
| * update github_demo.demo_user set deleted = 1 where id = '1'; -- 事务中的语句 | |||
| */ | |||
| boolean result1 = statement1.execute("update github_demo.demo_user set deleted = 1 where id = '1'"); | |||
| System.out.println(result1); | |||
| /** | |||
| * XA START xid2;-- 启动一个事务,并使之为active状态 | |||
| */ | |||
| connection2 = basicManagedDataSource2.getConnection(); | |||
| statement2 = connection2.createStatement(); | |||
| /** | |||
| * update test.demo_user set deleted = 1 where id = '1'; -- 事务中的语句 | |||
| */ | |||
| boolean result2 = statement2.execute("update test.demo_user set deleted = 1 where id = '1'"); | |||
| System.out.println(result2); | |||
| /** | |||
| * 当这执行以下语句: | |||
| * XA END xid1; -- 把事务置为idle状态 | |||
| * XA PREPARE xid1; -- 把事务置为prepare状态 | |||
| * XA END xid2; -- 把事务置为idle状态 | |||
| * XA PREPARE xid2; -- 把事务置为prepare状态 | |||
| * XA COMMIT xid1; -- 提交事务 | |||
| * XA COMMIT xid2; -- 提交事务 | |||
| */ | |||
| transactionManager.commit(); | |||
| } catch(Exception e) { | |||
| e.printStackTrace(); | |||
| } finally { | |||
| statement1.close(); | |||
| statement2.close(); | |||
| connection1.close(); | |||
| connection2.close(); | |||
| } | |||
| } | |||
| @Test | |||
| public void test01() throws Exception { | |||
| // 获得事务管理器 | |||
| TransactionManager transactionManager = new UserTransactionManager(); | |||
| // 获取第一个数据库的数据源 | |||
| BasicManagedDataSource basicManagedDataSource1 = getBasicManagedDataSource(transactionManager, "jdbc:mysql://localhost:3306/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true", "root", "root"); | |||
| // 注意,这一步非常重要 | |||
| basicManagedDataSource1.setDefaultCatalog("github_demo"); | |||
| // 获取第二个数据库的数据源 | |||
| BasicManagedDataSource basicManagedDataSource2 = getBasicManagedDataSource(transactionManager, "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true", "zzf", "zzf"); | |||
| // 注意,这一步非常重要 | |||
| basicManagedDataSource1.setDefaultCatalog("test"); | |||
| Connection connection1 = null; | |||
| Statement statement1 = null; | |||
| Connection connection2 = null; | |||
| Statement statement2 = null; | |||
| transactionManager.begin(); | |||
| try { | |||
| // 获取连接并进行数据库操作,这里会将会将XAResource注册到当前线程的XA事务对象 | |||
| /** | |||
| * XA START xid1;-- 启动一个事务,并使之为active状态 | |||
| */ | |||
| connection1 = basicManagedDataSource1.getConnection(); | |||
| statement1 = connection1.createStatement(); | |||
| /** | |||
| * update github_demo.demo_user set deleted = 1 where id = '1'; -- 事务中的语句 | |||
| */ | |||
| boolean result1 = statement1.execute("update github_demo.demo_user set deleted = 1 where id = '1'"); | |||
| System.out.println(result1); | |||
| /** | |||
| * XA START xid2;-- 启动一个事务,并使之为active状态 | |||
| */ | |||
| connection2 = basicManagedDataSource2.getConnection(); | |||
| statement2 = connection2.createStatement(); | |||
| /** | |||
| * update test.demo_user set deleted = 1 where id = '1'; -- 事务中的语句 | |||
| */ | |||
| boolean result2 = statement2.execute("update test.demo_user set deleted = 1 where id = '1'"); | |||
| System.out.println(result2); | |||
| /** | |||
| * 当这执行以下语句: | |||
| * XA END xid1; -- 把事务置为idle状态 | |||
| * XA PREPARE xid1; -- 把事务置为prepare状态 | |||
| * XA END xid2; -- 把事务置为idle状态 | |||
| * XA PREPARE xid2; -- 把事务置为prepare状态 | |||
| * XA COMMIT xid1; -- 提交事务 | |||
| * XA COMMIT xid2; -- 提交事务 | |||
| */ | |||
| transactionManager.commit(); | |||
| } catch(Exception e) { | |||
| e.printStackTrace(); | |||
| } finally { | |||
| statement1.close(); | |||
| statement2.close(); | |||
| connection1.close(); | |||
| connection2.close(); | |||
| } | |||
| } | |||
| } | |||