最近一个小项目DAO层涉及对Connection的获取,优化的时候顺便了解了一下JDBC中Connection使用的几个细节问题,跟大家分享一下:
1. 通常情况下,应在每个方法调用时新建Connection。
一般要在每个方法调用时新建connection,因为connection实例并不是线程安全的,下面给出Mysql中调用Connection实现类的方法(com.mysql.jdbc.PreparedStatement中executeUpdate())实例:
/**
* com.mysql.jdbc.PreparedStatement中的executeUpdete()
* @throws SQLException
*/
protected synchronized int executeUpdate(byte[][] batchedParameterStrings,
InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
int[] batchedStreamLengths, boolean[] batchedIsNull, boolean isReallyBatch)
throws SQLException {
checkClosed();
//声明MySQLConnection
MySQLConnection locallyScopedConn = this.connection;
if (locallyScopedConn.isReadOnly()) {
throw SQLError.createSQLException(Messages.getString("PreparedStatement.34") //$NON-NLS-1$
+ Messages.getString("PreparedStatement.35"), //$NON-NLS-1$
SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
if ((this.firstCharOfStmt == 'S')
&& isSelectQuery()) { //$NON-NLS-1$
throw SQLError.createSQLException(Messages.getString("PreparedStatement.37"), //$NON-NLS-1$
"01S03", getExceptionInterceptor()); //$NON-NLS-1$
}
if (this.results != null) {
if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
this.results.realClose(false);
}
}
ResultSetInternalMethods rs = null;
// The checking and changing of catalogs
// must happen in sequence, so synchronize
// on the same mutex that _conn is using
/*注意此处,sychronized(connection实例)
catalogs的检查和更改必须按顺序进行,因此在_conn正在使用的synchronized 进行同步*/
synchronized (locallyScopedConn) {
Buffer sendPacket = fillSendPacket(batchedParameterStrings,
batchedParameterStreams, batchedIsStream,
batchedStreamLengths);
String oldCatalog = null;
if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
oldCatalog = locallyScopedConn.getCatalog();
locallyScopedConn.setCatalog(this.currentCatalog);
}
//
// Only apply max_rows to selects
//
if (locallyScopedConn.useMaxRows()) {
executeSimpleNonQuery(locallyScopedConn,
"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
}
boolean oldInfoMsgState = false;
if (this.retrieveGeneratedKeys) {
oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();
locallyScopedConn.setReadInfoMsgEnabled(true);
}
rs = executeInternal(-1, sendPacket, false, false, null,
isReallyBatch);
if (this.retrieveGeneratedKeys) {
locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);
rs.setFirstCharOfQuery(this.firstCharOfStmt);
}
if (oldCatalog != null) {
locallyScopedConn.setCatalog(oldCatalog);
}
}
一个connection对象只能被一个线程所使用,如果涉及到多个线程同时调用同一个connection将造成数据的错乱。因此,一般应在一个方法被调用时新建connection。因此多线程情况下,可以使用数据库连接池来提升性能,数据库连接池可以保证connection实例交替被多个线程使用。
2. 使用数据库连接池时,如何保证一个线程中的所有数据库操作使用的是同一个connection。
若想要同一个线程获取到的connection为同一个有两种方法:
其一,可以使用ThreadLocal保存connection。ThreadLocal内部是用Map来保存数据。其中Map键值对的减是当前线程而值即为当前线程对于的connection。这种方法适用于要求每次请求得到的connection都是唯一的,当线程消亡时才会将connection交给数据库连接池。
在网上看到有些文章交代要看数据库连接池本身是否使用了ThreadLocal然后看了下java.sql包和javax.sql下的Connection和数据库连接池都是interface,每个DatasourcePool具体可以自己更改实现类,DatasourcePool的具体实现需要的话可以采取ThreadLocal来存放connection。
其二,可以直接交给spring去管理,在srping中每个事务和一个connection绑定,在事务回滚时该connection才会被返回数据库连接池。
自己的浅薄理解,若有异议欢迎提出~