//$Id: DriverManagerConnectionProvider.java 7888 2005-08-12 21:22:38Z oneovthafew $
package org.hibernate.connection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.cfg.Environment;
import org.hibernate.util.PropertiesHelper;
import org.hibernate.util.ReflectHelper;
/**
* A connection provider that uses <tt>java.sql.DriverManager</tt>. This provider
* also implements a very rudimentary connection pool.
* @see ConnectionProvider
* @author Gavin King
*/
public class DriverManagerConnectionProvider implements ConnectionProvider {
private String url;
private Properties connectionProps;
private Integer isolation;
private final ArrayList pool = new ArrayList();
private int poolSize;
private int checkedOut = 0;
private boolean autocommit;
private static final Logger log = LoggerFactory.getLogger(DriverManagerConnectionProvider.class);
public void configure(Properties props) throws HibernateException {
String driverClass = props.getProperty(Environment.DRIVER);
poolSize = PropertiesHelper.getInt(Environment.POOL_SIZE, props, 20); //default pool size 20
log.info("Using Hibernate built-in connection pool (not for production use!)");
log.info("Hibernate connection pool size: " + poolSize);
autocommit = PropertiesHelper.getBoolean(Environment.AUTOCOMMIT, props);
log.info("autocommit mode: " + autocommit);
isolation = PropertiesHelper.getInteger(Environment.ISOLATION, props);
if (isolation!=null)
log.info( "JDBC isolation level: " + Environment.isolationLevelToString( isolation.intValue() ) );
if (driverClass==null) {
log.warn("no JDBC Driver class was specified by property " + Environment.DRIVER);
}
else {
try {
// trying via forName() first to be as close to DriverManager's semantics
Class.forName(driverClass);
}
catch (ClassNotFoundException cnfe) {
try {
ReflectHelper.classForName(driverClass);
}
catch (ClassNotFoundException e) {
String msg = "JDBC Driver class not found: " + driverClass;
log.error( msg, e );
throw new HibernateException(msg, e);
}
}
}
url = props.getProperty( Environment.URL );
if ( url == null ) {
String msg = "JDBC URL was not specified by property " + Environment.URL;
log.error( msg );
throw new HibernateException( msg );
}
connectionProps = ConnectionProviderFactory.getConnectionProperties( props );
log.info( "using driver: " + driverClass + " at URL: " + url );
// if debug level is enabled, then log the password, otherwise mask it
if ( log.isDebugEnabled() ) {
log.info( "connection properties: " + connectionProps );
}
else if ( log.isInfoEnabled() ) {
log.info( "connection properties: " + PropertiesHelper.maskOut(connectionProps, "password") );
}
}
public Connection getConnection() throws SQLException {
if ( log.isTraceEnabled() ) log.trace( "total checked-out connections: " + checkedOut );
synchronized (pool) {
if ( !pool.isEmpty() ) {
int last = pool.size() - 1;
if ( log.isTraceEnabled() ) {
log.trace("using pooled JDBC connection, pool size: " + last);
checkedOut++;
}
Connection pooled = (Connection) pool.remove(last);
if (isolation!=null) pooled.setTransactionIsolation( isolation.intValue() );
if ( pooled.getAutoCommit()!=autocommit ) pooled.setAutoCommit(autocommit);
return pooled;
}
}
log.debug("opening new JDBC connection");
Connection conn = DriverManager.getConnection(url, connectionProps);
if (isolation!=null) conn.setTransactionIsolation( isolation.intValue() );
if ( conn.getAutoCommit()!=autocommit ) conn.setAutoCommit(autocommit);
if ( log.isDebugEnabled() ) {
log.debug( "created connection to: " + url + ", Isolation Level: " + conn.getTransactionIsolation() );
}
if ( log.isTraceEnabled() ) checkedOut++;
return conn;
}
public void closeConnection(Connection conn) throws SQLException {
if ( log.isDebugEnabled() ) checkedOut--;
synchronized (pool) {
int currentSize = pool.size();
if ( currentSize < poolSize ) {
if ( log.isTraceEnabled() ) log.trace("returning connection to pool, pool size: " + (currentSize + 1) );
pool.add(conn);
return;
}
}
log.debug("closing JDBC connection");
conn.close();
}
protected void finalize() {
close();
}
public void close() {
log.info("cleaning up connection pool: " + url);
Iterator iter = pool.iterator();
while ( iter.hasNext() ) {
try {
( (Connection) iter.next() ).close();
}
catch (SQLException sqle) {
log.warn("problem closing pooled connection", sqle);
}
}
pool.clear();
}
/**
* @see ConnectionProvider#supportsAggressiveRelease()
*/
public boolean supportsAggressiveRelease() {
return false;
}
}
DriverManagerConnectionProvider类继承了ConnectionProvider接口,主要是使用用户提供的JDBC驱动程序来连接数据库,或者使用连接池的方式来连接数据库。 一个典型的JDBC驱动连接数据库的方式:
<property name="connection.driver_class">com.mysql.jdbc.Driver</property> <!-- 连接数据库的URL--> <property name="connection.url"> jdbc:mysql://localhost:3306/schoolproject </property> <property name="connection.useUnicode">true</property> <property name="connection.characterEncoding">UTF-8</property> <!--连接的登录名--> <property name="connection.username">root</property> <!—登录密码--> <property name="connection.password"></property> <!--是否将运行期生成的SQL输出到日志以供调试--> <property name="show_sql">true</property> <!--指定连接的语言--> <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
至于连接池,由于在hibernate3.0中,已经不再支持dbcp了,hibernate的作者在hibernate.org中,明确指出在实践中发现dbcp有BUG,在某些种情会产生很多空连接不能释放,所以抛弃了对dbcp的支持。因此官方推荐使用c3p0或Proxool连接池。
C3P0连接配置
<!—JDBC驱动程序--> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <!-- 连接数据库的URL--> <property name="connection.url"> jdbc:mysql://localhost:3306/schoolproject </property> <property name="connection.useUnicode">true</property> <property name="connection.characterEncoding">UTF-8</property> <!--连接的登录名--> <property name="connection.username">root</property> <!--登录密码--> <property name="connection.password"></property> <!-- C3P0连接池设定--> <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider </property> <property name="hibernate.c3p0.max_size">20</property> <property name="hibernate.c3p0.min_size">5</property> <property name="hibernate.c3p0.timeout">120</property> <property name="hibernate.c3p0.max_statements">100</property> <property name="hibernate.c3p0.idle_test_period">120</property> <property name="hibernate.c3p0.acquire_increment">2</property> <!--是否将运行期生成的SQL输出到日志以供调试--> <property name="show_sql">true</property> <!--指定连接的语言--> <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
首先看DriverManagerConnectionProvider的configure(Properties props)方法,在这个方法中并没有去判断是否使用了连接池,真正判断使用连接池是在ConnectionProviderFactory这个类中,我们以后会分析。
DriverManagerConnectionProvider这个类只是初始化了最基本的连接配置,有如下几个:hibernate.connection.driver_class(JDBC驱动类)、hibernate.connection.pool_size(连接池容量的上限数目)、hibernate.connection.autocommit(允许被缓存的JDBC连接开启自动自动提交)、hibernate.connection.isolation(设置JDBC事务隔离级别,可查看java.sql.Connection来了解各个值的具体意义,但请注意多数数据库都不支持所有的隔离级别,取值1,2,4,8)。无论你是使用JDBC还是使用连接池方式,这几个最基本的设置都是要先初始化的。其中pool_size的默认值为20。
configure(Properties props)方法中的这个语句connectionProps = ConnectionProviderFactory.getConnectionProperties( props );值得注意, ConnectionProviderFactory.getConnectionProperties( props )主要是把HIBERNATE.CFG或者HIBERNATE.PROPERTIES的配置文件中的属性重新封装到另一个Properties 类——connectionProps中,且connectionProps的键值与 HIBERNATE.CFG或者HIBERNATE.PROPERTIES中的键值相比是没有“hibernate.connection”这样的字样的,比如,在HIBERNATE.PROPERTIES中定义的“hibernate.connection.pool_size”属性键,到该Properties 类 中的键就成了“pool_size ”。connectionProps主要是为后面的getConnection()方法而提供的。
接下来是getConnection()方法,在这里我们发现了synchronized关键字,这个关键字是加在pool这个对象上的,也就是说某个时刻只有其中的一个线程可以访问这个对象的synchronized数据。每个对象都有一个"锁标志",当这个对象的一个线程访问这个对象的某个synchronized数据时,这个对象的所有被synchronized修饰的数据将被上锁(因为"锁标志"被当前线程拿走了),只有当前线程访问完它要访问的synchronized数据时,当前线程才会释放"锁标志",这样同一个对象的其它线程才有机会访问synchronized数据。pool是什么?它是一个ArrayList,也就是连接池,里面都是一个个的Connection对象,这个可以在后面的closeConnection(Connection conn)体现出来,正在pool上加synchronized修饰符就保证了多个线程不能并发访问此ArrayList而生成不正确的结果。当调用getConnection()方法时,我们可以看到连接池就移除了一个连接。
Connection conn = DriverManager.getConnection(url, connectionProps)语句中我们可以看到全局变量connectionProps是传入到了这个方法中的,返回了一个Connection 对象。
closeConnection(Connection conn)方法是将连接重新放回到连接池。
close()方法清空了连接池,关闭所有连接。
supportsAggressiveRelease()方法返回的是false,表示连接释放模式会被设置为hibernate.connection.release_mode.AFTER_TRANSACTION也就是说在org.hibernate.Transaction结束后释放连接。
DatasourceConnectionProvider类分析 DatasourceConnectionProvider类比较简单,不同的是,它通过JNDI的方式来查找数据源来获得连接,这个类中要注意的方法是supportsAggressiveRelease()它返回的是true,表示在使用数据源连接的情况下,支持使用hibernate.connection.release_mode.AFTER_STATEMENT这一个设置。
UserSuppliedConnectionProvider类分析 UserSuppliedConnectionProvider 这个类是留给用户自己扩展的,基本上什么都不干。