MyBatis--连接池模块
生活随笔
收集整理的這篇文章主要介紹了
MyBatis--连接池模块
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
常見的數據源組件都實現了java.sql.DataSource接口。MyBatis也是這個套路。
從上圖可以看出:MyBatis使用不同的DataSourceFactory接口實現創建不同類型的DataSource。工廠模式
DataSourceFactory
public interface DataSourceFactory { //設置DataSoutce相關屬性,一般跟在初始化完成之后void setProperties(Properties props); //獲取DataSoutce對象DataSource getDataSource();} public class UnpooledDataSourceFactory implements DataSourceFactory {private static final String DRIVER_PROPERTY_PREFIX = "driver.";private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();protected DataSource dataSource; //構函初始化dataSourcepublic UnpooledDataSourceFactory() {this.dataSource = new UnpooledDataSource();}@Overridepublic void setProperties(Properties properties) {Properties driverProperties = new Properties();//創建DataSource相應的MetaObjectMetaObject metaDataSource = SystemMetaObject.forObject(dataSource);//遍歷for (Object key : properties.keySet()) {String propertyName = (String) key;if (propertyName.startsWith("driver.")) {//以"driver."開始的配置是對DataSource的配置保存到driverPropertiesString value = properties.getProperty(propertyName);driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);} else if (metaDataSource.hasSetter(propertyName)) {//是否有該屬性的setter方法String value = (String) properties.get(propertyName);Object convertedValue = convertValue(metaDataSource, propertyName, value);metaDataSource.setValue(propertyName, convertedValue);} else {throw new DataSourceException("Unknown DataSource property: " + propertyName);}}if (driverProperties.size() > 0) {//設置datasource.driverProperties相關屬性值metaDataSource.setValue("driverProperties", driverProperties);}}@Overridepublic DataSource getDataSource() {return dataSource;}private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {Object convertedValue = value;Class<?> targetType = metaDataSource.getSetterType(propertyName);if (targetType == Integer.class || targetType == int.class) {convertedValue = Integer.valueOf(value);} else if (targetType == Long.class || targetType == long.class) {convertedValue = Long.valueOf(value);} else if (targetType == Boolean.class || targetType == boolean.class) {convertedValue = Boolean.valueOf(value);}return convertedValue;}} 復制代碼UnpooledDataSource
public class UnpooledDataSource implements DataSource {private ClassLoader driverClassLoader;//加載Driver類的加載器private Properties driverProperties;//數據庫連接驅動的相關配置//緩存所有已注冊的數據庫連接驅動private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();private String driver;//驅動名稱private String url;private String username;private String password;private Boolean autoCommit;private Integer defaultTransactionIsolationLevel;//事務隔離級別static {Enumeration<Driver> drivers = DriverManager.getDrivers();while (drivers.hasMoreElements()) {Driver driver = drivers.nextElement();registeredDrivers.put(driver.getClass().getName(), driver);}}public UnpooledDataSource() {}public UnpooledDataSource(String driver, String url, String username, String password) {this.driver = driver;this.url = url;this.username = username;this.password = password;}public UnpooledDataSource(String driver, String url, Properties driverProperties) {this.driver = driver;this.url = url;this.driverProperties = driverProperties;}public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {this.driverClassLoader = driverClassLoader;this.driver = driver;this.url = url;this.username = username;this.password = password;}public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {this.driverClassLoader = driverClassLoader;this.driver = driver;this.url = url;this.driverProperties = driverProperties;}@Overridepublic Connection getConnection() throws SQLException {return doGetConnection(username, password);}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return doGetConnection(username, password);}@Overridepublic void setLoginTimeout(int loginTimeout) throws SQLException {DriverManager.setLoginTimeout(loginTimeout);}@Overridepublic int getLoginTimeout() throws SQLException {return DriverManager.getLoginTimeout();}@Overridepublic void setLogWriter(PrintWriter logWriter) throws SQLException {DriverManager.setLogWriter(logWriter);}@Overridepublic PrintWriter getLogWriter() throws SQLException {return DriverManager.getLogWriter();}public ClassLoader getDriverClassLoader() {return driverClassLoader;}public void setDriverClassLoader(ClassLoader driverClassLoader) {this.driverClassLoader = driverClassLoader;}public Properties getDriverProperties() {return driverProperties;}public void setDriverProperties(Properties driverProperties) {this.driverProperties = driverProperties;}private Connection doGetConnection(String username, String password) throws SQLException {Properties props = new Properties();if (driverProperties != null) {props.putAll(driverProperties);}if (username != null) {props.setProperty("user", username);}if (password != null) {props.setProperty("password", password);}return doGetConnection(props);}private Connection doGetConnection(Properties properties) throws SQLException {initializeDriver(); //1.初始化驅動//2.從DriverManager中獲取連接,獲取新的Connection對象Connection connection = DriverManager.getConnection(url, properties);configureConnection(connection);//3.配置autoCommit和隔離級別return connection;}private synchronized void initializeDriver() throws SQLException {if (!registeredDrivers.containsKey(driver)) {//檢測驅動是否注冊Class<?> driverType;try {if (driverClassLoader != null) {//注冊驅動driverType = Class.forName(driver, true, driverClassLoader);} else {driverType = Resources.classForName(driver);}//創建Driver對象Driver driverInstance = (Driver)driverType.newInstance();//注冊驅動DriverManager.registerDriver(new DriverProxy(driverInstance));registeredDrivers.put(driver, driverInstance);} catch (Exception e) {throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);}}}private void configureConnection(Connection conn) throws SQLException {if (autoCommit != null && autoCommit != conn.getAutoCommit()) {conn.setAutoCommit(autoCommit);//設置事務}if (defaultTransactionIsolationLevel != null) {//設置隔離級別conn.setTransactionIsolation(defaultTransactionIsolationLevel);}}private static class DriverProxy implements Driver {private Driver driver;DriverProxy(Driver d) {this.driver = d;}@Overridepublic boolean acceptsURL(String u) throws SQLException {return this.driver.acceptsURL(u);}@Overridepublic Connection connect(String u, Properties p) throws SQLException {return this.driver.connect(u, p);}@Overridepublic int getMajorVersion() {return this.driver.getMajorVersion();}@Overridepublic int getMinorVersion() {return this.driver.getMinorVersion();}@Overridepublic DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {return this.driver.getPropertyInfo(u, p);}@Overridepublic boolean jdbcCompliant() {return this.driver.jdbcCompliant();}// @Override only valid jdk7+public Logger getParentLogger() {return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);}}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {throw new SQLException(getClass().getName() + " is not a wrapper.");}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}// @Override only valid jdk7+public Logger getParentLogger() {// requires JDK version 1.6return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);}} 復制代碼PooledDataSource
依賴的組件
PooledDataSource并不會直接管理connection對象,而是管理PooledConnection對象。PooledConnection中封裝真正的連接對象以及其代理對象(通過jdk代理)
# PooledDataSource //通過PoolState管理池狀態并記錄統計信息private final PoolState state = new PoolState(this); //生成真正的連接對象,構造函數中初始化該字段private final UnpooledDataSource dataSource;protected int poolMaximumActiveConnections = 10;//最大活躍連接protected int poolMaximumIdleConnections = 5;//最大空閑連接protected int poolMaximumCheckoutTime = 20000;//最大checkout時長protected int poolTimeToWait = 20000;//無法獲取鏈接時,線程要等待的時間protected int poolMaximumLocalBadConnectionTolerance = 3; //檢測連接是否可用,會發送一個測試SQL語句protected String poolPingQuery = "NO PING QUERY SET";protected boolean poolPingEnabled;//是否允許發送測試SQL語句 //當連接超過poolPingConnectionsNotUsedFor毫秒未用時,發送一次測試SQL,檢測連接是否正常protected int poolPingConnectionsNotUsedFor;private int expectedConnectionTypeCode; //獲取鏈接過程public Connection getConnection() throws SQLException {return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();//得到連接后,返回代理。(這個是在初始化PooledConnection時,生成的代理)}// 返回可用的PooledConnectionprivate PooledConnection popConnection(String username, String password) throws SQLException {boolean countedWait = false;PooledConnection conn = null;long t = System.currentTimeMillis();int localBadConnectionCount = 0;while (conn == null) {synchronized (state) {if (!state.idleConnections.isEmpty()) {// 連接池中有空閑連接,取出第一個conn = state.idleConnections.remove(0);} else {// // 連接池中沒有空閑連接,則取當前正在使用的連接數小于最大限定值if (state.activeConnections.size() < poolMaximumActiveConnections) {// // 創建一個新的connection對象conn = new PooledConnection(dataSource.getConnection(), this);if (log.isDebugEnabled()) {log.debug("Created connection " + conn.getRealHashCode() + ".");}} else {// 當活動連接池已滿,不能創建時,取出活動連接池的第一個,即最先進入連接池的PooledConnection對象// 計算它的校驗時間,如果校驗時間大于連接池規定的最大校驗時間,則認為它已經過期了,// 利用這個PoolConnection內部的realConnection重新生成一個PooledConnectionPooledConnection oldestActiveConnection = state.activeConnections.get(0);long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();if (longestCheckoutTime > poolMaximumCheckoutTime) {// Can claim overdue connectionstate.claimedOverdueConnectionCount++;state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;state.accumulatedCheckoutTime += longestCheckoutTime;state.activeConnections.remove(oldestActiveConnection);if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {try {oldestActiveConnection.getRealConnection().rollback();} catch (SQLException e) {log.debug("Bad connection. Could not roll back");} }//如果過期,創建新的連接放入活躍池。//這里創建代理conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());oldestActiveConnection.invalidate();if (log.isDebugEnabled()) {log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");}} else {//如果不能釋放,則必須等待有try {if (!countedWait) {state.hadToWaitCount++;countedWait = true;}long wt = System.currentTimeMillis();state.wait(poolTimeToWait);state.accumulatedWaitTime += System.currentTimeMillis() - wt;} catch (InterruptedException e) {break;}}}} //如果獲取PooledConnection成功,則更新其信息if (conn != null) {// ping to server and check the connection is valid or notif (conn.isValid()) {if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));conn.setCheckoutTimestamp(System.currentTimeMillis());conn.setLastUsedTimestamp(System.currentTimeMillis());state.activeConnections.add(conn);state.requestCount++;state.accumulatedRequestTime += System.currentTimeMillis() - t;} else {state.badConnectionCount++;localBadConnectionCount++;conn = null;if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {throw new SQLException("PooledDataSource: Could not get a good connection to the database.");}}}}} ####返回連接//通過PooledConnection.invoke()方法我們知道,調用連接的代理的close方法時,調用的是//PooledDataSource.pushConnectioin方法,將PooledConnction歸還給連接池,供之后重用。protected void pushConnection(PooledConnection conn) throws SQLException {synchronized (state) {//同步state.activeConnections.remove(conn);//從活躍集合中移除該PooledConnection對象if (conn.isValid()) {//檢測PooledConnection是否有效if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {//空閑連接是否達到上限,以及連接是否為該連接池連接state.accumulatedCheckoutTime += conn.getCheckoutTime();//累積checkout時長if (!conn.getRealConnection().getAutoCommit()) {//回滾未提交的事務conn.getRealConnection().rollback();}//為返還的連接創建新的PooledConnection對象PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);state.idleConnections.add(newConn);//添加到idle集合newConn.setCreatedTimestamp(conn.getCreatedTimestamp());newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());conn.invalidate();//將原pooledConnecion設為無效state.notifyAll();//喚醒阻塞等待的線程} else {//空閑連接到達上限或pooledConnection對象不屬于該池state.accumulatedCheckoutTime += conn.getCheckoutTime();//累積checkout時長if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}conn.getRealConnection().close();//關閉真正的連接conn.invalidate();//將PooledConncetion設為無效}} else {state.badConnectionCount++;//統計無效PooledConnection對象個數}}}//檢測有效性的方法public boolean isValid() {return valid && realConnection != null&& dataSource.pingConnection(this);//檢測真正連接是否有用}//執行測試SQLprotected boolean pingConnection(PooledConnection conn) {boolean result = true;//記錄ping操作是否成功try {result = !conn.getRealConnection().isClosed();//檢測真正的連接是否關閉} catch (SQLException e) {result = false;}if (result) {if (poolPingEnabled) {//是否執行SQL語句if (poolPingConnectionsNotUsedFor >= 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {//長時間未使用的連接,才需要ping來檢測是否正常try {//執行測試語句Connection realConn = conn.getRealConnection();try (Statement statement = realConn.createStatement()) {statement.executeQuery(poolPingQuery).close();}if (!realConn.getAutoCommit()) {realConn.rollback();}result = true;} catch (Exception e) {try {conn.getRealConnection().close();} catch (Exception e2) {//ignore}result = false;}}}}return result;} 復制代碼PooledConnection核心字段:
//當前PooledConnection所屬的PooledDataSource。private final PooledDataSource dataSource;private final Connection realConnection;//真正的數據連接private final Connection proxyConnection;//連接的代理private long checkoutTimestamp;//從池中取出該連接的時間private long createdTimestamp;//該連接的創建時間private long lastUsedTimestamp;//最后一次使用時間private int connectionTypeCode;//url 用戶名密碼得到的hash,標示連接所在的連接池private boolean valid;//檢測當前連接是否有效。主要是防止程序通過close()方法將連接歸還池后,依然通過該連接操作數據庫 //當調用關閉的時候,回收此Connection到PooledDataSource中public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();//若調用close(),則將其重新放入池中,而不是真正關閉連接if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {dataSource.pushConnection(this);return null;} else {try {if (!Object.class.equals(method.getDeclaringClass())) {//通過valid字段檢測連接是否有效checkConnection();}//調用真正數據庫對象的對應方法return method.invoke(realConnection, args);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}}//池和連接的理解https://www.cnblogs.com/panxuejun/p/6403760.html //http://shift-alt-ctrl.iteye.com/blog/1967020 //數據庫連接池在初始化時,會創建一定數量的連接放在池中備用。 //當池中連接全部用完,請求就會進入阻塞隊列等待。 復制代碼PoolState是用于管理PooledConnection對象狀態的組件,通過兩個ArrayList<>管理空閑和活躍兩種狀態。
protected final List<PooledConnection> idleConnections = new ArrayList<>();//空閑protected final List<PooledConnection> activeConnections = new ArrayList<>();//活躍//統計的字段protected long requestCount = 0;//請求連接次數protected long accumulatedRequestTime = 0;//獲取連接的累積時間protected long accumulatedCheckoutTime = 0;//所有連接累積CheckoutTime時長protected long claimedOverdueConnectionCount = 0;//超時連接個數protected long accumulatedCheckoutTimeOfOverdueConnections = 0;//累積超時時間protected long accumulatedWaitTime = 0;//累積等待時間protected long hadToWaitCount = 0;//等待次數protected long badConnectionCount = 0;//無效的連接數 復制代碼轉載于:https://juejin.im/post/5bdbf5c56fb9a049bc4c1626
總結
以上是生活随笔為你收集整理的MyBatis--连接池模块的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ARTS打卡计划第四周-TIPS-自定义
- 下一篇: git可视化工具