ConnectionManager 管理JDBC连接


log:打印日志(废话)
callback: 回调类:在 打开连接,关闭连接,事务状态时机定义动作
factory:供应ConnectionProvider以提供连接,Batch以管理Statement
releaseMode:释放模式
connection:连接缓存
borrowedConnection:连用连接缓存
wasConnectionSupplied:提供的连接是被客户端提供的而非ConnectionProvider
batcher:管理Statement
interceptor:拦截器,给batcher用
isClosed:该类实例是否关闭
isFlushing:改状态值是为了解决一次flush中发多条Statement时,重复释放连接问题


可以把session与connection的使用关系分为三种

session使用的连接是被提供的,常常SessionFactory.openSession(connection,Interceptor?)
session使用的连接被借给客户端,常常session.connection
session通过connectionProvider.getConnection得到的连接
如果session使用的连接是被提供的,那么当之后调用session.connection向session借取的连接就是之前被提供的连接,这类型的连接释放模式必须是ON_CLOSE,但即使这样,连接也不能由session关闭之后而关闭,既然是被提供的,当然生命周期不必与Session同步
如果session使用的连接是通过connectionprovider得到,并被提供给客户端,那么此时的客户端得到的是一个代理连接(被代理的对象正是connectionprovider获取的),当调用代理连接的close方法,并不能关闭被代理的连接,只是标识缓存中的borrowedConnection为空并设置代理连接不可用,因为既然是借用session的,当然不能够擅自了结连接。
如果session使用的连接是通过connectionprovider得到,那么连接的释放遵守ConnectionReleaseMode定义

事务&非事务操作
在hibernate中事务和非事务操作划分原则体现在TransactionFactory.isTransactionInProgress方法上
对JDBC事务来说,由jdbcTransaction.begin标志事务为激活状态,并且没有提交或回滚,则之后的操作认为是事务性的
对JTA事务来说,只要检测到事务javax.transaction.Status
=STATUS_ACTIVE或STATUS_MARKED_ROLLBACK(至于由javax.transaction.UserTransaction还是javax.transaction.TransactionManager.getTransaction确定事务状态都无所谓)则之后的操作认为是事务性的
对CMT事务来说,只能由javax.transaction.TransactionManager.getTransaction确定javax.transaction.Status
=STATUS_ACTIVE或STATUS_MARKED_ROLLBACK,则之后的操作认为是事务性的

当确定其中一种事务管理后,如果isTransactionInProgress返回false,则之后的操作被认为是事务性操作


释放操作执行

afterStatement由以下调用操作调用
flush完成之后
关闭每条Statement之后
创建或删除临时表之后

调用afterStatement并不能保证连接立即关闭,下列条件都必须满足
releaseMode等于AFTER_STATEMENT或者非事务环境下的自动提交模式
flush完成之后或没有flush
由batch管理的Statement和ResultSet全被关闭掉
连接没被借出去或被借出去之后关闭掉的

afterTransaction由以下情况调用
session.close或disconnect(先关闭所有Statement)
事务提交或回滚之后的同步处理(本地同步或JTA同步)
非事务环境查询
afterTransaction中下列条件之一满足即可关闭连接
releaseMode等于AFTER_TRANSACTION
releaseMode等于AFTER_STATEMENT并且batch还有未关闭的Statement和ResultSet:同时关闭尚未处理的Statement和ResultSet

// $Id: ConnectionManager.java 11304 2007-03-19 22:06:45Z steve.ebersole@jboss.com $
package org.hibernate.jdbc;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.util.JDBCExceptionReporter;

/**
* Encapsulates JDBC Connection management logic needed by Hibernate.
* <p/>
* The lifecycle is intended to span a logical series of interactions with the
* database. Internally, this means the the lifecycle of the Session.
*
* @author Steve Ebersole
*/
public class ConnectionManager implements Serializable {

private static final Log log = LogFactory.getLog( ConnectionManager.class );

public static interface Callback {
public void connectionOpened();
public void connectionCleanedUp();
public boolean isTransactionInProgress();
}

private transient SessionFactoryImplementor factory;
private final Callback callback;

private final ConnectionReleaseMode releaseMode;
private transient Connection connection;
private transient Connection borrowedConnection;

private final boolean wasConnectionSupplied;
private transient Batcher batcher;
private transient Interceptor interceptor;
private boolean isClosed;
private transient boolean isFlushing;

/**
* Constructs a ConnectionManager.
* <p/>
* This is the form used internally.
*
* @param factory The SessionFactory.
* @param callback An observer for internal state change.
* @param releaseMode The mode by which to release JDBC connections.
* @param connection An externally supplied connection.
*/
public ConnectionManager(
SessionFactoryImplementor factory,
Callback callback,
ConnectionReleaseMode releaseMode,
Connection connection,
Interceptor interceptor) {
this.factory = factory;
this.callback = callback;

this.interceptor = interceptor;
this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this, interceptor );

this.connection = connection;
wasConnectionSupplied = ( connection != null );

this.releaseMode = wasConnectionSupplied ? ConnectionReleaseMode.ON_CLOSE : releaseMode;
}

/**
* Private constructor used exclusively from custom serialization
*/
private ConnectionManager(
SessionFactoryImplementor factory,
Callback callback,
ConnectionReleaseMode releaseMode,
Interceptor interceptor,
boolean wasConnectionSupplied,
boolean isClosed) {
this.factory = factory;
this.callback = callback;

this.interceptor = interceptor;
this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this, interceptor );

this.wasConnectionSupplied = wasConnectionSupplied;
this.isClosed = isClosed;
this.releaseMode = wasConnectionSupplied ? ConnectionReleaseMode.ON_CLOSE : releaseMode;
}

/**
* The session factory.
*
* @return the session factory.
*/
public SessionFactoryImplementor getFactory() {
return factory;
}

/**
* The batcher managed by this ConnectionManager.
*
* @return The batcher.
*/
public Batcher getBatcher() {
return batcher;
}

/**
* Was the connection being used here supplied by the user?
*
* @return True if the user supplied the JDBC connection; false otherwise
*/
public boolean isSuppliedConnection() {
return wasConnectionSupplied;
}

/**
* Retrieves the connection currently managed by this ConnectionManager.
* <p/>
* Note, that we may need to obtain a connection to return here if a
* connection has either not yet been obtained (non-UserSuppliedConnectionProvider)
* or has previously been aggressively released (if supported in this environment).
*
* @return The current Connection.
*
* @throws HibernateException Indicates a connection is currently not
* available (we are currently manually disconnected).
*/
public Connection getConnection() throws HibernateException {
if ( isClosed ) {
throw new HibernateException( "connection manager has been closed" );
}
if ( connection == null ) {
openConnection();
}
return connection;
}

public boolean hasBorrowedConnection() {
// used from testsuite
return borrowedConnection != null;
}

public Connection borrowConnection() {
if ( isClosed ) {
throw new HibernateException( "connection manager has been closed" );
}
if ( isSuppliedConnection() ) {
return connection;
}
else {
if ( borrowedConnection == null ) {
borrowedConnection = BorrowedConnectionProxy.generateProxy( this );
}
return borrowedConnection;
}
}

public void releaseBorrowedConnection() {
if ( borrowedConnection != null ) {
try {
BorrowedConnectionProxy.renderUnuseable( borrowedConnection );
}
finally {
borrowedConnection = null;
}
}
}

/**
* Is the connection considered "auto-commit"?
*
* @return True if we either do not have a connection, or the connection
* really is in auto-commit mode.
*
* @throws SQLException Can be thrown by the Connection.isAutoCommit() check.
*/
public boolean isAutoCommit() throws SQLException {
return connection == null
|| connection.isClosed()
|| connection.getAutoCommit();
}

/**
* Will connections be released after each statement execution?
* <p/>
* Connections will be released after each statement if either:<ul>
* <li>the defined release-mode is {@link ConnectionReleaseMode#AFTER_STATEMENT}; or
* <li>the defined release-mode is {@link ConnectionReleaseMode#AFTER_TRANSACTION} but
* we are in auto-commit mode.
* <p/>
* release-mode = {@link ConnectionReleaseMode#ON_CLOSE} should [b]never[/b] release
* a connection.
*
* @return True if the connections will be released after each statement; false otherwise.
*/
public boolean isAggressiveRelease() {
if ( releaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) {
return true;
}
else if ( releaseMode == ConnectionReleaseMode.AFTER_TRANSACTION ) {
boolean inAutoCommitState;
try {
inAutoCommitState = isAutoCommit()&& !callback.isTransactionInProgress();
}
catch( SQLException e ) {
// assume we are in an auto-commit state
inAutoCommitState = true;
}
return inAutoCommitState;
}
return false;
}

/**
* Modified version of {@link #isAggressiveRelease} which does not force a
* transaction check. This is solely used from our {@link #afterTransaction}
* callback, so no need to do the check; plus it seems to cause problems on
* websphere (god i love websphere ;)
* </p>
* It uses this information to decide if an aggressive release was skipped
* do to open resources, and if so forces a release.
*
* @return True if the connections will be released after each statement; false otherwise.
*/
private boolean isAggressiveReleaseNoTransactionCheck() {
if ( releaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) {
return true;
}
else {
boolean inAutoCommitState;
try {
inAutoCommitState = isAutoCommit();
}
catch( SQLException e ) {
// assume we are in an auto-commit state
inAutoCommitState = true;
}
return releaseMode == ConnectionReleaseMode.AFTER_TRANSACTION && inAutoCommitState;
}
}

/**
* Is this ConnectionManager instance "logically" connected. Meaning
* do we either have a cached connection available or do we have the
* ability to obtain a connection on demand.
*
* @return True if logically connected; false otherwise.
*/
public boolean isCurrentlyConnected() {
return wasConnectionSupplied ? connection != null : !isClosed;
}

/**
* To be called after execution of each JDBC statement. Used to
* conditionally release the JDBC connection aggressively if
* the configured release mode indicates.
*/
public void afterStatement() {
if ( isAggressiveRelease() ) {
if ( isFlushing ) {
log.debug( "skipping aggressive-release due to flush cycle" );
}
else if ( batcher.hasOpenResources() ) {
log.debug( "skipping aggresive-release due to open resources on batcher" );
}
else if ( borrowedConnection != null ) {
log.debug( "skipping aggresive-release due to borrowed connection" );
}
else {
aggressiveRelease();
}
}
}

/**
* To be called after local transaction completion. Used to conditionally
* release the JDBC connection aggressively if the configured release mode
* indicates.
*/
public void afterTransaction() {
if ( isAfterTransactionRelease() ) {
aggressiveRelease();
}
else if ( isAggressiveReleaseNoTransactionCheck() && batcher.hasOpenResources() ) {
log.info( "forcing batcher resource cleanup on transaction completion; forgot to close ScrollableResults/Iterator?" );
batcher.closeStatements();
aggressiveRelease();
}
else if ( isOnCloseRelease() ) {
// log a message about potential connection leaks
log.debug( "transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!" );
}
batcher.unsetTransactionTimeout();
}

private boolean isAfterTransactionRelease() {
return releaseMode == ConnectionReleaseMode.AFTER_TRANSACTION;
}

private boolean isOnCloseRelease() {
return releaseMode == ConnectionReleaseMode.ON_CLOSE;
}

/**
* To be called after Session completion. Used to release the JDBC
* connection.
*
* @return The connection mantained here at time of close. Null if
* there was no connection cached internally.
*/
public Connection close() {
try {
return cleanup();
}
finally {
isClosed = true;
}
}

/**
* Manually disconnect the underlying JDBC Connection. The assumption here
* is that the manager will be reconnected at a later point in time.
*
* @return The connection mantained here at time of disconnect. Null if
* there was no connection cached internally.
*/
public Connection manualDisconnect() {
return cleanup();
}

/**
* Manually reconnect the underlying JDBC Connection. Should be called at
* some point after manualDisconnect().
* <p/>
* This form is used for ConnectionProvider-supplied connections.
*/
public void manualReconnect() {
}

/**
* Manually reconnect the underlying JDBC Connection. Should be called at
* some point after manualDisconnect().
* <p/>
* This form is used for user-supplied connections.
*/
public void manualReconnect(Connection suppliedConnection) {
this.connection = suppliedConnection;
}

/**
* Releases the Connection and cleans up any resources associated with
* that Connection. This is intended for use:
* 1) at the end of the session
* 2) on a manual disconnect of the session
* 3) from afterTransaction(), in the case of skipped aggressive releasing
*
* @return The released connection.
* @throws HibernateException
*/
private Connection cleanup() throws HibernateException {
releaseBorrowedConnection();

if ( connection == null ) {
log.trace( "connection already null in cleanup : no action");
return null;
}

try {
log.trace( "performing cleanup" );

batcher.closeStatements();
Connection c = null;
if ( !wasConnectionSupplied ) {
closeConnection();
}
else {
c = connection;
}
connection = null;
return c;
}
finally {
callback.connectionCleanedUp();
}
}

/**
* Performs actions required to perform an aggressive release of the
* JDBC Connection.
*/
private void aggressiveRelease() {
if ( !wasConnectionSupplied ) {
log.debug( "aggressively releasing JDBC connection" );
if ( connection != null ) {
closeConnection();
}
}
}

/**
* Pysically opens a JDBC Connection.
*
* @throws HibernateException
*/
private void openConnection() throws HibernateException {
if ( connection != null ) {
return;
}

log.debug("opening JDBC connection");
try {
connection = factory.getConnectionProvider().getConnection();
}
catch (SQLException sqle) {
throw JDBCExceptionHelper.convert(
factory.getSQLExceptionConverter(),
sqle,
"Cannot open connection"
);
}

callback.connectionOpened(); // register synch; stats.connect()
}

/**
* Physically closes the JDBC Connection.
*/
private void closeConnection() {
if ( log.isDebugEnabled() ) {
log.debug(
"releasing JDBC connection [" +
batcher.openResourceStatsAsString() + "]"
);
}

try {
if ( !connection.isClosed() ) {
JDBCExceptionReporter.logAndClearWarnings( connection );
}
factory.getConnectionProvider().closeConnection( connection );
connection = null;
}
catch (SQLException sqle) {
throw JDBCExceptionHelper.convert(
factory.getSQLExceptionConverter(),
sqle,
"Cannot release connection"
);
}
}

/**
* Callback to let us know that a flush is beginning. We use this fact
* to temporarily circumvent aggressive connection releasing until after
* the flush cycle is complete {@link #flushEnding()}
*/
public void flushBeginning() {
log.trace( "registering flush begin" );
isFlushing = true;
}

/**
* Callback to let us know that a flush is ending. We use this fact to
* stop circumventing aggressive releasing connections.
*/
public void flushEnding() {
log.trace( "registering flush end" );
isFlushing = false;
afterStatement();
}

public boolean isReadyForSerialization() {
return wasConnectionSupplied ? connection == null : !batcher.hasOpenResources();
}

/**
* Used during serialization.
*
* @param oos The stream to which we are being written.
* @throws IOException Indicates an I/O error writing to the stream
*/
private void writeObject(ObjectOutputStream oos) throws IOException {
if ( !isReadyForSerialization() ) {
throw new IllegalStateException( "Cannot serialize a ConnectionManager while connected" );
}

oos.writeObject( factory );
oos.writeObject( interceptor );
oos.defaultWriteObject();
}

/**
* Used during deserialization.
*
* @param ois The stream from which we are being read.
* @throws IOException Indicates an I/O error reading the stream
* @throws ClassNotFoundException Indicates resource class resolution.
*/
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
factory = (SessionFactoryImplementor) ois.readObject();
interceptor = (Interceptor) ois.readObject();
ois.defaultReadObject();

this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this, interceptor );
}

public void serialize(ObjectOutputStream oos) throws IOException {
oos.writeBoolean( wasConnectionSupplied );
oos.writeBoolean( isClosed );
}

public static ConnectionManager deserialize(
ObjectInputStream ois,
SessionFactoryImplementor factory,
Interceptor interceptor,
ConnectionReleaseMode connectionReleaseMode,
JDBCContext jdbcContext) throws IOException {
return new ConnectionManager(
factory,
jdbcContext,
connectionReleaseMode,
interceptor,
ois.readBoolean(),
ois.readBoolean()
);
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值