diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 0c48c4eeb8..22bdb1a420 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -494,7 +494,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements public String getDbType() { return dbType; } - + public void setDbType(String dbType) { this.dbType = dbType; } @@ -852,7 +852,6 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements this.username = username; } - public String getPassword() { return password; @@ -922,7 +921,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements this.jdbcUrl = jdbcUrl; } - + public String getDriverClassName() { return driverClass; } @@ -1546,7 +1545,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements JdbcUtils.close(stmtHolder.getStatement()); } - + public static boolean equals(Object object1, Object object2) { if (object1 == object2) { return true; @@ -1556,4 +1555,62 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements } return object1.equals(object2); } + + protected void cloneTo(DruidAbstractDataSource to) { + to.defaultAutoCommit = this.defaultAutoCommit; + to.defaultReadOnly = this.defaultReadOnly; + to.defaultTransactionIsolation = this.defaultTransactionIsolation; + to.defaultCatalog = this.defaultCatalog; + to.name = this.name; + to.username = this.username; + to.password = this.password; + to.jdbcUrl = this.jdbcUrl; + to.driverClass = this.driverClass; + to.connectionProperties = this.connectionProperties; + to.passwordCallback = this.passwordCallback; + to.userCallback = this.userCallback; + to.connectionFactory = this.connectionFactory; + to.initialSize = this.initialSize; + to.maxActive = this.maxActive; + to.minIdle = this.minIdle; + to.maxIdle = this.maxIdle; + to.maxWait = this.maxWait; + to.validationQuery = this.validationQuery; + to.validationQueryTimeout = this.validationQueryTimeout; + to.testOnBorrow = this.testOnBorrow; + to.testOnReturn = this.testOnReturn; + to.testWhileIdle = this.testWhileIdle; + to.poolPreparedStatements = this.poolPreparedStatements; + to.sharePreparedStatements = this.sharePreparedStatements; + to.maxPoolPreparedStatementPerConnectionSize = this.maxPoolPreparedStatementPerConnectionSize; + to.logWriter = this.logWriter; + if (this.filters != null) { + to.filters = new ArrayList(this.filters); + } + to.exceptionSorter = this.exceptionSorter; + to.driver = this.driver; + to.queryTimeout = this.queryTimeout; + to.transactionQueryTimeout = this.transactionQueryTimeout; + to.accessToUnderlyingConnectionAllowed = this.accessToUnderlyingConnectionAllowed; + to.timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis; + to.numTestsPerEvictionRun = this.numTestsPerEvictionRun; + to.minEvictableIdleTimeMillis = this.minEvictableIdleTimeMillis; + to.removeAbandoned = this.removeAbandoned; + to.removeAbandonedTimeoutMillis = this.removeAbandonedTimeoutMillis; + to.logAbandoned = this.logAbandoned; + to.maxOpenPreparedStatements = this.maxOpenPreparedStatements; + if (connectionInitSqls != null) { + to.connectionInitSqls = new ArrayList(this.connectionInitSqls); + } + to.dbType = this.dbType; + to.timeBetweenConnectErrorMillis = this.timeBetweenConnectErrorMillis; + to.validConnectionChecker = this.validConnectionChecker; + to.connectionErrorRetryAttempts = this.connectionErrorRetryAttempts; + to.breakAfterAcquireFailure = this.breakAfterAcquireFailure; + to.transactionThresholdMillis = this.transactionThresholdMillis; + to.dupCloseLogEnable = this.dupCloseLogEnable; + to.isOracle = this.isOracle; + to.useOracleImplicitCache = this.useOracleImplicitCache; + } + } diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index 60fb81364a..9eb65780e5 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -64,7 +64,7 @@ import com.alibaba.druid.util.JdbcUtils; * @author ljw * @author wenshao */ -public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable { +public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable { private final static Log LOG = LogFactory.getLog(DruidDataSource.class); @@ -1384,4 +1384,11 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat return dataSourceStat; } + public DruidDataSource clone() { + DruidDataSource x = new DruidDataSource(); + + cloneTo(x); + + return x; + } } diff --git a/src/test/java/com/alibaba/druid/bvt/pool/TestClone.java b/src/test/java/com/alibaba/druid/bvt/pool/TestClone.java new file mode 100644 index 0000000000..3b70e6e1d5 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/TestClone.java @@ -0,0 +1,76 @@ +package com.alibaba.druid.bvt.pool; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.mock.MockConnection; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.util.JdbcUtils; + + +public class TestClone extends TestCase { + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + dataSource = new DruidDataSource(); + + dataSource.setUsername("xxx1"); + dataSource.setPassword("ppp"); + dataSource.setUrl("jdbc:mock:xx"); + dataSource.setFilters("stat"); + dataSource.setMaxOpenPreparedStatements(30); + } + + public void test_clone() throws Exception { + Connection conn_0 = dataSource.getConnection(); + Connection conn_1 = dataSource.getConnection(); + Connection conn_2 = dataSource.getConnection(); + + DruidDataSource clone = dataSource.clone(); + clone.init(); + + dataSource.close(); + + Assert.assertEquals(dataSource.getUrl(), clone.getUrl()); + Assert.assertEquals(dataSource.getUsername(), clone.getUsername()); + Assert.assertEquals(dataSource.getPassword(), clone.getPassword()); + Assert.assertEquals(dataSource.getFilterClassNames(), clone.getFilterClassNames()); + Assert.assertEquals(dataSource.getMaxOpenPreparedStatements(), clone.getMaxOpenPreparedStatements()); + + PreparedStatement ps_0 = conn_0.prepareStatement("select 1"); + ResultSet rs = ps_0.executeQuery(); + rs.next(); + rs.close(); + ps_0.close(); + + // dataSource is closed, but connections is not closed + Assert.assertFalse(conn_0.isClosed()); + Assert.assertFalse(conn_1.isClosed()); + Assert.assertFalse(conn_2.isClosed()); + + MockConnection mockConn_0 = conn_0.unwrap(MockConnection.class); + MockConnection mockConn_1 = conn_1.unwrap(MockConnection.class); + MockConnection mockConn_2 = conn_2.unwrap(MockConnection.class); + + Assert.assertFalse(mockConn_0.isClosed()); + Assert.assertFalse(mockConn_1.isClosed()); + Assert.assertFalse(mockConn_2.isClosed()); + + conn_0.close(); // no error + conn_1.close(); // no error + conn_2.close(); // no error + + // real connection already closed + Assert.assertTrue(mockConn_0.isClosed()); + Assert.assertTrue(mockConn_1.isClosed()); + Assert.assertTrue(mockConn_2.isClosed()); + } + + protected void tearDown() throws Exception { + JdbcUtils.close(dataSource); + } +}