從源代碼解讀hibernate之數據庫連接
大家都知道hibernate是在JDBC基礎上的封裝,那么它的數據庫連接是怎樣實現的呢?帶著這個疑問最近研究了下hibernate的源代碼,代碼還是比較簡單的,但是做的很通用,好現在一起來看下源代碼
hibernate的數據庫連接類都放在org.hibernate.connection包內,對于數據庫連接類hibernate稱其為ConnectionProvider,對!就是連接提供者,org.hibernate.connection.ConnectionProvider只是個供hibernate使用的接口,通過該接口的getConnection()方法獲得數據庫連接,但是這個接口到底是怎么樣實現的,或者是由誰提供的,hibernate并不關心,用戶在使用hibernate的時候可以在其配置文件中指定具體的實現類(hoho,這就是面向接口編程的好處),現在看看這個接口的規范:
package org.hibernate.connection; public interface ConnectionProvider { /** 初始化建立數據庫連接所需要的配置 */ public void configure(Properties props) throws HibernateException; /** 獲得數據庫連接 */ public Connection getConnection() throws SQLException; /** 關閉數據庫連接 */ public void closeConnection(Connection conn) throws SQLException; /** 釋放連接提供者占用的所有資源 */ public void close() throws HibernateException; } |
接下來要說的是org.hibernate.connection.ConnectionProviderFactory,望名會意,就是制造連接提供者的工廠,這個工廠類里面通過hibernate的配置反射獲得具體的ConnectionProvider實現類的實例
ConnectionProvider connections; String providerClass = properties.getProperty(Environment.CONNECTION_PROVIDER); if ( providerClass!=null ) { try { log.info("Initializing connection provider: " + providerClass); //反射獲得具體的ConnectionProvider實現類的實例 connections = (ConnectionProvider) ReflectHelper.classForName(providerClass).newInstance(); } catch ( Exception e ) { log.error( "Could not instantiate connection provider", e ); throw new HibernateException("Could not instantiate connection provider: " + providerClass); } } else if ( properties.getProperty(Environment.DATASOURCE)!=null ) { connections = new DatasourceConnectionProvider(); } else if ( properties.getProperty(Environment.URL)!=null ) { connections = new DriverManagerConnectionProvider(); } else { connections = new UserSuppliedConnectionProvider(); } ... return connections; |
對于ConnectionProvider接口,hibernate自己提供了一套豐富的實現
1. DatasourceConnectionProvider,這是基于WEB容器提供的JNDI數據庫連接池的連接實現
package org.hibernate.connection; public class DatasourceConnectionProvider implements ConnectionProvider { private DataSource ds; public void configure(Properties props) throws HibernateException { String jndiName = props.getProperty( Environment.DATASOURCE ); if ( jndiName == null ) { String msg = "datasource JNDI name was not specified by property " + Environment.DATASOURCE; log.error( msg ); throw new HibernateException( msg ); } user = props.getProperty( Environment.USER ); pass = props.getProperty( Environment.PASS ); try { //通過JNDI方式獲得DataSource ds = ( DataSource ) NamingHelper.getInitialContext( props ).lookup( jndiName ); } catch ( Exception e ) { log.error( "Could not find datasource: " + jndiName, e ); throw new HibernateException( "Could not find datasource", e ); } if ( ds == null ) { throw new HibernateException( "Could not find datasource: " + jndiName ); } log.info( "Using datasource: " + jndiName ); } public Connection getConnection() throws SQLException { if (user != null || pass != null) { //獲得連接 return ds.getConnection(user, pass); } else { //獲得連接 return ds.getConnection(); } } } |
2. DriverManagerConnectionProvider,這是基于JDBC的數據庫連接,當然同時也實現了自己的數據庫連接緩存池
package org.hibernate.connection; 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); //數據庫連接池的大小,默認是20個 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"); //如果連接池里沒有空閑的連接,則新建一個JDBC連接并返回 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(); } /** 釋放連接池 */ 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(); } } |
3. 基于第三方項目的連接池實現,大家可以自己去看hibernate源代碼:org.hibernate.connection.C3P0ConnectionProvider,以及org.hibernate.connection.ProxoolConnectionProvider。
對于hibernate的普通使用,如下代碼:
SessionFactory sf = new Configuration().configure().buildSessionFactory(); Session session = sf.openSession(); |
每次openSession()獲得一個session就建立了一條數據庫連接,一個session其實就對應著一條連接
如果是使用spring和hibernate進行web開發,可能你會用到下面的代碼
Session session = org.springframework.orm.hibernate3.SessionFactoryUtils.getSession(sessionFactory, true); |
可以自己去看spring的源代碼,這個返回的Session對象其實已經被包裝后緩存到了ThreadLocal對象里