package com.easyooo.framework.sharding.transaction; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.transaction.CannotCreateTransactionException; import org.springframework.transaction.TransactionDefinition; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * * JDBC连接帮助类,包括事务管理器 * * @author Killer */ public abstract class DataSourceUtils { private static final Logger logger = LoggerFactory.getLogger(DataSourceUtils.class); /** * 从数据源获取一个连接,该连接是否新连接,取决于连接池厂商是否会缓存Connection的策略 * * @param dataSource * @return * @throws CannotGetJdbcConnectionException */ public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException { try { return doGetConnection(dataSource, null, null); } catch (SQLException ex) { throw new CannotGetJdbcConnectionException( "Could not get JDBC Connection", ex); } } public static Connection getConnection(DataSource dataSource, String user, String pwd) throws CannotGetJdbcConnectionException { try { return doGetConnection(dataSource, user, pwd); } catch (SQLException ex) { throw new CannotGetJdbcConnectionException( "Could not get JDBC Connection", ex); } } /** * 获取一个连接池,将连接池绑定到当前线程变量,在调用该方法的同时, * 需要显示调用 {@link #releaseConnection(Connection, DataSource)} * * @param dataSource * @return * @throws SQLException */ public static Connection doGetConnection(DataSource dataSource, String user, String password) throws SQLException { Assert.notNull(dataSource, "No DataSource specified"); ConnectionHolder holder = getCurrentConnectionHolder(dataSource, user, password); if(holder == null){ Connection conn = null; if(StringUtils.isEmpty(user)){ conn = dataSource.getConnection(); }else{ conn = dataSource.getConnection(user, password); } holder = new ConnectionHolder(new CloseDelegatingConnection(conn), user, password); if (RoutingSynchronizationManager.isSynchronizationActive()) { RoutingSynchronizationManager.registerSynchronization(dataSource, holder); doBegin(conn, dataSource); prepareConnectionForTransaction(conn); } } holder.requested(); if (logger.isDebugEnabled()) { logger.debug("Connection["+ holder.getConn() +"] is used " + holder.getReferenceCount() +" times."); } return holder.getConn(); } /** * Get opened connection in the given datasource * @param dataSource * @param user * @param password */ public static ConnectionHolder getCurrentConnectionHolder(DataSource dataSource, String user, String password){ if(!RoutingSynchronizationManager.isSynchronizationActive()){ return null; } ConnectionHolder holder = RoutingSynchronizationManager.getSynchronizations().get(dataSource); if(holder != null && holder.equalUserAndPwd(user, password)){ return holder; } return null; } public static void doBegin(Connection con, DataSource dataSource) { try { if (con.getAutoCommit()) { if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); } } catch (Throwable ex) { DataSourceUtils.releaseConnection(con); throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } } public static Integer prepareConnectionForTransaction(Connection con) throws SQLException { Assert.notNull(con, "No Connection specified"); TransactionDefinition definition = RoutingSynchronizationManager.getCurrentTransactionDefinition(); if (definition != null && definition.isReadOnly()) { try { if (logger.isDebugEnabled()) { logger.debug("Setting JDBC Connection [" + con + "] read-only"); } con.setReadOnly(true); } catch (SQLException ex) { Throwable exToCheck = ex; while (exToCheck != null) { if (exToCheck.getClass().getSimpleName().contains("Timeout")) { throw ex; } exToCheck = exToCheck.getCause(); } logger.debug("Could not set JDBC Connection read-only", ex); } catch (RuntimeException ex) { Throwable exToCheck = ex; while (exToCheck != null) { if (exToCheck.getClass().getSimpleName().contains("Timeout")) { throw ex; } exToCheck = exToCheck.getCause(); } logger.debug("Could not set JDBC Connection read-only", ex); } } // Apply specific isolation level, if any. Integer previousIsolationLevel = null; if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { if (logger.isDebugEnabled()) { logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " + definition.getIsolationLevel()); } int currentIsolation = con.getTransactionIsolation(); if (currentIsolation != definition.getIsolationLevel()) { previousIsolationLevel = currentIsolation; con.setTransactionIsolation(definition.getIsolationLevel()); } } return previousIsolationLevel; } public static void releaseConnection(Connection con) { try { doCloseConnection(con); } catch (SQLException ex) { logger.debug("Could not close JDBC Connection", ex); } catch (Throwable ex) { logger.debug("Unexpected exception on closing JDBC Connection", ex); } } public static void doCloseConnection(Connection con) throws SQLException { con.close(); } }