- //$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 这个类是留给用户自己扩展的,基本上什么都不干。