📄 simpledatasource.java
字号:
/*
* Copyright 2004 Clinton Begin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ibatis.common.jdbc;
import com.ibatis.common.beans.ClassInfo;
import com.ibatis.common.resources.Resources;
import com.ibatis.common.logging.LogFactory;
import com.ibatis.common.logging.Log;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.*;
/**
* This is a simple, synchronous, thread-safe database connection pool.
* <p/>
* REQUIRED PROPERTIES
* -------------------
* JDBC.Driver
* JDBC.ConnectionURL
* JDBC.Username
* JDBC.Password
* <p/>
* Pool.MaximumActiveConnections
* Pool.MaximumIdleConnections
* Pool.MaximumCheckoutTime
* Pool.TimeToWait
* Pool.PingQuery
* Pool.PingEnabled
* Pool.PingConnectionsOlderThan
* Pool.PingConnectionsNotUsedFor
* Pool.QuietMode
*/
public class SimpleDataSource implements DataSource {
private static final Log log = LogFactory.getLog(SimpleDataSource.class);
// Required Properties
private static final String PROP_JDBC_DRIVER = "JDBC.Driver";
private static final String PROP_JDBC_URL = "JDBC.ConnectionURL";
private static final String PROP_JDBC_USERNAME = "JDBC.Username";
private static final String PROP_JDBC_PASSWORD = "JDBC.Password";
private static final String PROP_JDBC_DEFAULT_AUTOCOMMIT = "JDBC.DefaultAutoCommit";
// Optional Properties
private static final String PROP_POOL_MAX_ACTIVE_CONN = "Pool.MaximumActiveConnections";
private static final String PROP_POOL_MAX_IDLE_CONN = "Pool.MaximumIdleConnections";
private static final String PROP_POOL_MAX_CHECKOUT_TIME = "Pool.MaximumCheckoutTime";
private static final String PROP_POOL_TIME_TO_WAIT = "Pool.TimeToWait";
private static final String PROP_POOL_PING_QUERY = "Pool.PingQuery";
private static final String PROP_POOL_PING_CONN_OLDER_THAN = "Pool.PingConnectionsOlderThan";
private static final String PROP_POOL_PING_ENABLED = "Pool.PingEnabled";
private static final String PROP_POOL_PING_CONN_NOT_USED_FOR = "Pool.PingConnectionsNotUsedFor";
private int expectedConnectionTypeCode;
// Additional Driver Properties prefix
private static final String ADD_DRIVER_PROPS_PREFIX = "Driver.";
private static final int ADD_DRIVER_PROPS_PREFIX_LENGTH = ADD_DRIVER_PROPS_PREFIX.length();
// ----- BEGIN: FIELDS LOCKED BY POOL_LOCK -----
private final Object POOL_LOCK = new Object();
private List idleConnections = new ArrayList();
private List activeConnections = new ArrayList();
private long requestCount = 0;
private long accumulatedRequestTime = 0;
private long accumulatedCheckoutTime = 0;
private long claimedOverdueConnectionCount = 0;
private long accumulatedCheckoutTimeOfOverdueConnections = 0;
private long accumulatedWaitTime = 0;
private long hadToWaitCount = 0;
private long badConnectionCount = 0;
// ----- END: FIELDS LOCKED BY POOL_LOCK -----
// ----- BEGIN: PROPERTY FIELDS FOR CONFIGURATION -----
private String jdbcDriver;
private String jdbcUrl;
private String jdbcUsername;
private String jdbcPassword;
private boolean jdbcDefaultAutoCommit;
private Properties driverProps;
private boolean useDriverProps;
private int poolMaximumActiveConnections;
private int poolMaximumIdleConnections;
private int poolMaximumCheckoutTime;
private int poolTimeToWait;
private String poolPingQuery;
private boolean poolPingEnabled;
private int poolPingConnectionsOlderThan;
private int poolPingConnectionsNotUsedFor;
//----- END: PROPERTY FIELDS FOR CONFIGURATION -----
/**
* Constructor to allow passing in a map of properties for configuration
*
* @param props - the configuration parameters
*/
public SimpleDataSource(Map props) {
initialize(props);
}
private void initialize(Map props) {
try {
String prop_pool_ping_query = null;
if (props == null) {
throw new RuntimeException("SimpleDataSource: The properties map passed to the initializer was null.");
}
if (!(props.containsKey(PROP_JDBC_DRIVER)
&& props.containsKey(PROP_JDBC_URL)
&& props.containsKey(PROP_JDBC_USERNAME)
&& props.containsKey(PROP_JDBC_PASSWORD))) {
throw new RuntimeException("SimpleDataSource: Some properties were not set.");
} else {
jdbcDriver = (String) props.get(PROP_JDBC_DRIVER);
jdbcUrl = (String) props.get(PROP_JDBC_URL);
jdbcUsername = (String) props.get(PROP_JDBC_USERNAME);
jdbcPassword = (String) props.get(PROP_JDBC_PASSWORD);
poolMaximumActiveConnections =
props.containsKey(PROP_POOL_MAX_ACTIVE_CONN)
? Integer.parseInt((String) props.get(PROP_POOL_MAX_ACTIVE_CONN))
: 10;
poolMaximumIdleConnections =
props.containsKey(PROP_POOL_MAX_IDLE_CONN)
? Integer.parseInt((String) props.get(PROP_POOL_MAX_IDLE_CONN))
: 5;
poolMaximumCheckoutTime =
props.containsKey(PROP_POOL_MAX_CHECKOUT_TIME)
? Integer.parseInt((String) props.get(PROP_POOL_MAX_CHECKOUT_TIME))
: 20000;
poolTimeToWait =
props.containsKey(PROP_POOL_TIME_TO_WAIT)
? Integer.parseInt((String) props.get(PROP_POOL_TIME_TO_WAIT))
: 20000;
poolPingEnabled =
props.containsKey(PROP_POOL_PING_ENABLED)
&& Boolean.valueOf((String) props.get(PROP_POOL_PING_ENABLED)).booleanValue();
prop_pool_ping_query = (String) props.get(PROP_POOL_PING_QUERY);
poolPingQuery =
props.containsKey(PROP_POOL_PING_QUERY)
? prop_pool_ping_query
: "NO PING QUERY SET";
poolPingConnectionsOlderThan =
props.containsKey(PROP_POOL_PING_CONN_OLDER_THAN)
? Integer.parseInt((String) props.get(PROP_POOL_PING_CONN_OLDER_THAN))
: 0;
poolPingConnectionsNotUsedFor =
props.containsKey(PROP_POOL_PING_CONN_NOT_USED_FOR)
? Integer.parseInt((String) props.get(PROP_POOL_PING_CONN_NOT_USED_FOR))
: 0;
jdbcDefaultAutoCommit =
props.containsKey(PROP_JDBC_DEFAULT_AUTOCOMMIT)
&& Boolean.valueOf((String) props.get(PROP_JDBC_DEFAULT_AUTOCOMMIT)).booleanValue();
useDriverProps = false;
Iterator propIter = props.keySet().iterator();
driverProps = new Properties();
driverProps.put("user", jdbcUsername);
driverProps.put("password", jdbcPassword);
while (propIter.hasNext()) {
String name = (String) propIter.next();
String value = (String) props.get(name);
if (name.startsWith(ADD_DRIVER_PROPS_PREFIX)) {
driverProps.put(name.substring(ADD_DRIVER_PROPS_PREFIX_LENGTH), value);
useDriverProps = true;
}
}
expectedConnectionTypeCode = assembleConnectionTypeCode(jdbcUrl, jdbcUsername, jdbcPassword);
Resources.instantiate(jdbcDriver);
if ( poolPingEnabled && (!props.containsKey(PROP_POOL_PING_QUERY) ||
prop_pool_ping_query.trim().length() == 0) ) {
throw new RuntimeException("SimpleDataSource: property '" + PROP_POOL_PING_ENABLED + "' is true, but property '" +
PROP_POOL_PING_QUERY + "' is not set correctly.");
}
}
} catch (Exception e) {
log.error("SimpleDataSource: Error while loading properties. Cause: " + e.toString(), e);
throw new RuntimeException("SimpleDataSource: Error while loading properties. Cause: " + e, e);
}
}
private int assembleConnectionTypeCode(String url, String username, String password) {
return ("" + url + username + password).hashCode();
}
/**
* @see javax.sql.DataSource#getConnection()
*/
public Connection getConnection() throws SQLException {
return popConnection(jdbcUsername, jdbcPassword).getProxyConnection();
}
/**
* @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
*/
public Connection getConnection(String username, String password) throws SQLException {
return popConnection(username, password).getProxyConnection();
}
/**
* @see javax.sql.DataSource#setLoginTimeout(int)
*/
public void setLoginTimeout(int loginTimeout) throws SQLException {
DriverManager.setLoginTimeout(loginTimeout);
}
/**
* @see javax.sql.DataSource#getLoginTimeout()
*/
public int getLoginTimeout() throws SQLException {
return DriverManager.getLoginTimeout();
}
/**
* @see javax.sql.DataSource#setLogWriter(java.io.PrintWriter)
*/
public void setLogWriter(PrintWriter logWriter) throws SQLException {
DriverManager.setLogWriter(logWriter);
}
/**
* @see javax.sql.DataSource#getLogWriter()
*/
public PrintWriter getLogWriter() throws SQLException {
return DriverManager.getLogWriter();
}
/**
* If a connection has not been used in this many milliseconds, ping the
* database to make sure the connection is still good.
*
* @return the number of milliseconds of inactivity that will trigger a ping
*/
public int getPoolPingConnectionsNotUsedFor() {
return poolPingConnectionsNotUsedFor;
}
/**
* Getter for the name of the JDBC driver class used
* @return The name of the class
*/
public String getJdbcDriver() {
return jdbcDriver;
}
/**
* Getter of the JDBC URL used
* @return The JDBC URL
*/
public String getJdbcUrl() {
return jdbcUrl;
}
/**
* Getter for the JDBC user name used
* @return The user name
*/
public String getJdbcUsername() {
return jdbcUsername;
}
/**
* Getter for the JDBC password used
* @return The password
*/
public String getJdbcPassword() {
return jdbcPassword;
}
/**
* Getter for the maximum number of active connections
* @return The maximum number of active connections
*/
public int getPoolMaximumActiveConnections() {
return poolMaximumActiveConnections;
}
/**
* Getter for the maximum number of idle connections
* @return The maximum number of idle connections
*/
public int getPoolMaximumIdleConnections() {
return poolMaximumIdleConnections;
}
/**
* Getter for the maximum time a connection can be used before it *may* be
* given away again.
* @return The maximum time
*/
public int getPoolMaximumCheckoutTime() {
return poolMaximumCheckoutTime;
}
/**
* Getter for the time to wait before retrying to get a connection
* @return The time to wait
*/
public int getPoolTimeToWait() {
return poolTimeToWait;
}
/**
* Getter for the query to be used to check a connection
* @return The query
*/
public String getPoolPingQuery() {
return poolPingQuery;
}
/**
* Getter to tell if we should use the ping query
* @return True if we need to check a connection before using it
*/
public boolean isPoolPingEnabled() {
return poolPingEnabled;
}
/**
* Getter for the age of connections that should be pinged before using
* @return The age
*/
public int getPoolPingConnectionsOlderThan() {
return poolPingConnectionsOlderThan;
}
private int getExpectedConnectionTypeCode() {
return expectedConnectionTypeCode;
}
/**
* Getter for the number of connection requests made
* @return The number of connection requests made
*/
public long getRequestCount() {
synchronized (POOL_LOCK) {
return requestCount;
}
}
/**
* Getter for the average time required to get a connection to the database
* @return The average time
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -