mybatis连接池
连接池
在 Mybatis 中,数据源 dataSource 共有三类,分别是:
UNPOOLED : 不使用连接池的数据源。采用传统的 javax.sql.DataSource 规范中的连接池,Mybatis 中有针对规范的实现
POOLED : 使用连接池的数据源。采用池的思想
JNDI : 使用 JNDI 实现的数据源,采用服务器提供的 JNDI 技术实现,来获取 DataSource 对象,不同的服务器所能拿到的 DataSource 是不一样的。(如果不是web或者maven的war工程,是不能使用的,tomcat-dbcp连接池:因为需要相应的代码需要在服务器端才能启动,连接池连接的数据源是部署在服务器上的,如tomcat你可以在servlet或者jsp(最终还是会转换成servlet)上进行sqlsession的创建才能在jndi上getConnection到相应的connection,通过浏览器或客户端对服务器的访问调用了服务器上部署的有相应需要连接数据库的方法,才能调用服务器连接的数据库源)
连接池和线程池:
连接池:(降低物理连接损耗)
1、连接池是面向数据库连接的
2、连接池是为了优化数据库连接资源
3、连接池有点类似在客户端做优化数据库连接是一项有限的昂贵资源,一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的性能低下。 数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。
线程池:(降低线程创建销毁损耗)
1.、线程池是面向后台程序的
2、线程池是是为了提高内存和CPU效率
3、线程池有点类似于在服务端做优化线程池是一次性创建一定数量的线程(应该可以配置初始线程数量的),当用请求过来不用去创建新的线程,直接使用已创建的线程,使用后又放回到线程池中。
避免了频繁创建线程,及销毁线程的系统开销,提高是内存和CPU效率。相同点:
都是事先准备好资源,避免频繁创建和销毁的代价。扩展:
对象池:
对象池技术基本原理的核心有两点:缓存和共享,即对于那些被频繁使用的对象,在使用完后,不立即将它们释放,而是将它们缓存起来,以供后续的应用程序重复使用,从而减少创建对象和释放对象的次数,进而改善应用程序的性能。事实上,由于对象池技术将对象限制在一定的数量,也有效地减少了应用程序内存上的开销。对于两者是否有联系:
- 首先,每个连接要启动,都需要依赖于一个线程的调用,否则,即使连接在连接池里,没有断开连接,也无法做出行为
- 当线程使用完时,会将连接与线程解绑交还于连接池
- 一般会是多个连接并发执行,即需要多个线程,因此前面也有线程池的管理
- 每个连接都是线程安全的,用synchronized锁住
while(conn == null) { synchronized(this.state) { PoolState var10000; if (!this.state.idleConnections.isEmpty()) { conn = (PooledConnection)this.state.idleConnections.remove(0); if (log.isDebugEnabled()) { log.debug("Checked out connection " + conn.getRealHashCode() + " from pool."); } } else if (this.state.activeConnections.size() < this.poolMaximumActiveConnections) { conn = new PooledConnection(this.dataSource.getConnection(), this); if (log.isDebugEnabled()) { log.debug("Created connection " + conn.getRealHashCode() + "."); } } else { PooledConnection oldestActiveConnection = (PooledConnection)this.state.activeConnections.get(0); long longestCheckoutTime = oldestActiveConnection.getCheckoutTime(); if (longestCheckoutTime > (long)this.poolMaximumCheckoutTime) { ++this.state.claimedOverdueConnectionCount; var10000 = this.state; var10000.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime; var10000 = this.state; var10000.accumulatedCheckoutTime += longestCheckoutTime; this.state.activeConnections.remove(oldestActiveConnection); if (!oldestActiveConnection.getRealConnection().getAutoCommit()) { try { oldestActiveConnection.getRealConnection().rollback(); } catch (SQLException var16) { 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) { ++this.state.hadToWaitCount; countedWait = true; } if (log.isDebugEnabled()) { log.debug("Waiting as long as " + this.poolTimeToWait + " milliseconds for connection."); } long wt = System.currentTimeMillis(); this.state.wait((long)this.poolTimeToWait); var10000 = this.state; var10000.accumulatedWaitTime += System.currentTimeMillis() - wt; } catch (InterruptedException var17) { break; } } }
-
如果想要修改 Mybatis 使用的数据源,那么就可以在 Mybatis 配置文件中修改:(这里
type
属性的取值就是为POOLED、UNPOOLED、JNDI
。)
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
查看 POOLED
的实现 PooledDataSource
,可以看出获取连接时采用了池的思想,大概流程如下图(只是一个简单介绍,不全面)
查看 UNPOOLED
的实现 UnpooledDataSource
,可以看出每次获取连接时都会注册驱动并创建新连接,大概流程如下图(只是一个简单介绍,不全面)
sqlsession和connection
- 一个sqlsession一般对应一个connection,并且mybatis默认每次获取session都会开启一个事务,且不自动提交事务。如果更新操作完成后不手动commit,则在连接断开时会将更新操作回滚,一个sqlSession(一个transaction)中可以多次commit,commit后cache和statement刷新(一般一个事务(transaction)只对应一个sqlseesion,也即一个connection,分布式一个事务可以对应多个connection),只有close后同时关闭sqlsession和transaction(transaction可以由factory关闭)并返回connection。mybatis的transaction基本没添加什么功能,大部分仅状态判断后交由connection(再由jdbc)执行。可能是为了给spring等框架容易组转自己的事务管理机制。
sqlsession与transaction
(第一个JdbcTransactionFactory、第二个defaultsqlsessonfactory):
用transactionFactory创建相应的transaction,用创建出的tx来创建executor,再用executor和配置信息来创建defaulSqlsession
public class JdbcTransactionFactory implements TransactionFactory {
public JdbcTransactionFactory() {
}
public void setProperties(Properties props) {
}
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
}
public class DefaultSqlSessionFactory implements SqlSessionFactory {
public SqlSession openSession(ExecutorType execType, Connection connection) {
return this.openSessionFromConnection(execType, connection);
}
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {//分为两种创建(dataSource、fromConnection)
DefaultSqlSession var8;
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException var13) {
autoCommit = true;
}
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
Transaction tx = transactionFactory.newTransaction(connection);
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var14, var14);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
DefaultSqlSession var8;
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException var13) {
autoCommit = true;
}
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
Transaction tx = transactionFactory.newTransaction(connection);
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var14, var14);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
sqlsession对executor的调用
(defaultSqlsession)
public class DefaultSqlSession implements SqlSession {
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
public int update(String statement, Object parameter) {
int var4;
try {
this.dirty = true;
MappedStatement ms = this.configuration.getMappedStatement(statement);
var4 = this.executor.update(ms, this.wrapCollection(parameter));
} catch (Exception var8) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + var8, var8);
} finally {
ErrorContext.instance().reset();
}
return var4;
}
public void commit(boolean force) {
try {
this.executor.commit(this.isCommitOrRollbackRequired(force));
this.dirty = false;
} catch (Exception var6) {
throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + var6, var6);
} finally {
ErrorContext.instance().reset();
}
}
public void rollback(boolean force) {
try {
this.executor.rollback(this.isCommitOrRollbackRequired(force));
this.dirty = false;
} catch (Exception var6) {
throw ExceptionFactory.wrapException("Error rolling back transaction. Cause: " + var6, var6);
} finally {
ErrorContext.instance().reset();
}
}
- sqlsession无论执行sql语句还是对事物的管理,都会转由executor执行
executor对sql的执行
- (simpleExecutor extends baseExecutor) sqlsession->executor->connection
- 由configuration以及参数生成语句处理对象 handler,再调用preaparement方法对handler进行connection的获取与连接,之后将操作都交给了handler,经过实现了statementhandler接口的RoutingStatementHandler->PreparedStatementHandler(extends BaseStatementHandler)之后的preparedstatement等statement后,便是jdbc的excute操作与result的封装
- 一级缓存的发生也在处理后发生
public class SimpleExecutor extends BaseExecutor {
public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
int var6;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var6 = handler.update(stmt);
} finally {
this.closeStatement(stmt);
}
return var6;
}
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Connection connection = this.getConnection(statementLog);
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
executor对事务的处理
- 由simpleExecutor对象处理,但用的方法都继承自baseExecutor
- sqlsession(带着在sqlsessionfactory创建的transaction)->executor(transaction)->transaction.commit/close·····
- executor 将事务管理交给了 transaction,commit/rollback等对cache和statement的清空(clearLocalCache,flushStatements)也在这里开始进行。
- 在sqlsession的close前,对sql语句的执行都会用getConnection创建的连接进行sql语句的执行,commit也并不会说新建一个事务(transaction),而是清空cache和statement并提交jdbc执行后的结果到mysql。此时仍可以用commit后的sqlsession和transaction进行getMapper代理并调用接口方法,只有close后两者才会消失
public class SimpleExecutor extends BaseExecutor {
public Transaction getTransaction() {
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
return this.transaction;
}
}
public void close(boolean forceRollback) {
try {
try {
this.rollback(forceRollback);
} finally {
if (this.transaction != null) {
this.transaction.close();
}
}
} catch (SQLException var11) {
log.warn("Unexpected exception on closing transaction. Cause: " + var11);
} finally {
this.transaction = null;
this.deferredLoads = null;
this.localCache = null;
this.localOutputParameterCache = null;
this.closed = true;
}
}
public void commit(boolean required) throws SQLException {
if (this.closed) {
throw new ExecutorException("Cannot commit, transaction is already closed");
} else {
this.clearLocalCache();
this.flushStatements();
if (required) {
this.transaction.commit();
}
}
}
public void rollback(boolean required) throws SQLException {
if (!this.closed) {
try {
this.clearLocalCache();
this.flushStatements(true);
} finally {
if (required) {
this.transaction.rollback();
}
}
}
}
public void clearLocalCache() {
if (!this.closed) {
this.localCache.clear();
this.localOutputParameterCache.clear();
}
}
transaction到connection
- JdbcTransaction
- 由transaction对与connection 的commit/colse进行管理。
public class JdbcTransaction implements Transaction {
public JdbcTransaction(Connection connection) {
this.connection = connection;
}
public Connection getConnection() throws SQLException {
if (this.connection == null) {
this.openConnection();
}
return this.connection;
}
public void commit() throws SQLException {
if (this.connection != null && !this.connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + this.connection + "]");
}
this.connection.commit();
}
}
public void rollback() throws SQLException {
if (this.connection != null && !this.connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + this.connection + "]");
}
this.connection.rollback();
}
}
public void close() throws SQLException {
if (this.connection != null) {
this.resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + this.connection + "]");
}
this.connection.close();
}
}
}
connection
- connectionImp类
- 从这开始便是com.mysql.jdbc包的东西了,就是与数据库直接进行交接的部分了
public class ConnectionImpl extends ConnectionPropertiesImpl implements Connection {
public void commit() throws SQLException {
synchronized(this.getMutex()) {
this.checkClosed();
try {
if (this.connectionLifecycleInterceptors != null) {
IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {
void forEach(Object each) throws SQLException {
if (!((ConnectionLifecycleInterceptor)each).commit()) {
this.stopIterating = true;
}
}
};
iter.doForAll();
if (!iter.fullIteration()) {
return;
}
}
if (this.autoCommit && !this.getRelaxAutoCommit()) {
throw SQLError.createSQLException("Can't call commit when autocommit=true");
}
if (this.transactionsSupported) {
if (this.getUseLocalSessionState() && this.versionMeetsMinimum(5, 0, 0) && !this.io.inTransactionOnServer()) {
return;
}
this.execSQL((StatementImpl)null, "commit", -1, (Buffer)null, 1003, 1007, false, this.database, (Field[])null, false);
}
} catch (SQLException var8) {
if ("08S01".equals(var8.getSQLState())) {
throw SQLError.createSQLException("Communications link failure during commit(). Transaction resolution unknown.", "08007");
}
throw var8;
} finally {
this.needsPing = this.getReconnectAtTxEnd();
}
}
}
}