从源代码解读hibernate之数据库连接

转载 http://cuishen.iteye.com/blog/427921 
大家都知道hibernate是在JDBC基础上的封装,那么它的数据库连接是怎样实现的呢?带着这个疑问最近研究了下hibernate的源代码,代码还是比较简单的,但是做的很通用,好现在一起来看下源代码 


hibernate的数据库连接类都放在org.hibernate.connection包内,对于数据库连接类hibernate称其为ConnectionProvider,对!就是连接提供者,org.hibernate.connection.ConnectionProvider只是个供hibernate使用的接口,通过该接口的getConnection()方法获得数据库连接,但是这个接口到底是怎么样实现的,或者是由谁提供的,hibernate并不关心,用户在使用hibernate的时候可以在其配置文件中指定具体的实现类(hoho,这就是面向接口编程的好处),现在看看这个接口的规范: 

Java代码  收藏代码
  1. package org.hibernate.connection;  
  2.   
  3. public interface ConnectionProvider {  
  4.     /** 初始化建立数据库连接所需要的配置 */  
  5.     public void configure(Properties props) throws HibernateException;  
  6.     /** 获得数据库连接 */  
  7.     public Connection getConnection() throws SQLException;  
  8.     /** 关闭数据库连接 */  
  9.     public void closeConnection(Connection conn) throws SQLException;  
  10.     /** 释放连接提供者占用的所有资源 */  
  11.     public void close() throws HibernateException;  
  12. }  


接下来要说的是org.hibernate.connection.ConnectionProviderFactory,望名会意,就是制造连接提供者的工厂,这个工厂类里面通过hibernate的配置反射获得具体的ConnectionProvider实现类的实例 

Java代码  收藏代码
  1. ConnectionProvider connections;  
  2. String providerClass = properties.getProperty(Environment.CONNECTION_PROVIDER);  
  3. if ( providerClass!=null ) {  
  4.     try {  
  5.         log.info("Initializing connection provider: " + providerClass);  
  6.         //反射获得具体的ConnectionProvider实现类的实例  
  7.         connections = (ConnectionProvider) ReflectHelper.classForName(providerClass).newInstance();  
  8.     }  
  9.     catch ( Exception e ) {  
  10.         log.error( "Could not instantiate connection provider", e );  
  11.         throw new HibernateException("Could not instantiate connection provider: " + providerClass);  
  12.     }  
  13. }  
  14. else if ( properties.getProperty(Environment.DATASOURCE)!=null ) {  
  15.     connections = new DatasourceConnectionProvider();  
  16. }  
  17. else if ( properties.getProperty(Environment.URL)!=null ) {  
  18.     connections = new DriverManagerConnectionProvider();  
  19. }  
  20. else {  
  21.     connections = new UserSuppliedConnectionProvider();  
  22. }  
  23. ...  
  24. return connections;  


对于ConnectionProvider接口,hibernate自己提供了一套丰富的实现 

1. DatasourceConnectionProvider,这是基于WEB容器提供的JNDI数据库连接池的连接实现 

Java代码  收藏代码
  1. package org.hibernate.connection;  
  2. public class DatasourceConnectionProvider implements ConnectionProvider {  
  3.     private DataSource ds;  
  4.   
  5.     public void configure(Properties props) throws HibernateException {  
  6.   
  7.         String jndiName = props.getProperty( Environment.DATASOURCE );  
  8.         if ( jndiName == null ) {  
  9.             String msg = "datasource JNDI name was not specified by property " + Environment.DATASOURCE;  
  10.             log.error( msg );  
  11.             throw new HibernateException( msg );  
  12.         }  
  13.   
  14.         user = props.getProperty( Environment.USER );  
  15.         pass = props.getProperty( Environment.PASS );  
  16.   
  17.         try {  
  18.             //通过JNDI方式获得DataSource  
  19.             ds = ( DataSource ) NamingHelper.getInitialContext( props ).lookup( jndiName );  
  20.         }  
  21.         catch ( Exception e ) {  
  22.             log.error( "Could not find datasource: " + jndiName, e );  
  23.             throw new HibernateException( "Could not find datasource", e );  
  24.         }  
  25.         if ( ds == null ) {  
  26.             throw new HibernateException( "Could not find datasource: " + jndiName );  
  27.         }  
  28.         log.info( "Using datasource: " + jndiName );  
  29.     }  
  30.   
  31.     public Connection getConnection() throws SQLException {  
  32.         if (user != null || pass != null) {  
  33.             //获得连接  
  34.             return ds.getConnection(user, pass);  
  35.         }  
  36.         else {  
  37.             //获得连接  
  38.             return ds.getConnection();  
  39.         }  
  40.     }  
  41. }  


2. DriverManagerConnectionProvider,这是基于JDBC的数据库连接,当然同时也实现了自己的数据库连接缓存池 

Java代码  收藏代码
  1. package org.hibernate.connection;  
  2. public class DriverManagerConnectionProvider implements ConnectionProvider {  
  3.     private String url;  
  4.     private Properties connectionProps;  
  5.     private Integer isolation;  
  6.     private final ArrayList pool = new ArrayList();  
  7.     private int poolSize;  
  8.     private int checkedOut = 0;  
  9.     private boolean autocommit;  
  10.   
  11.     private static final Logger log = LoggerFactory.getLogger(DriverManagerConnectionProvider.class);  
  12.   
  13.     public void configure(Properties props) throws HibernateException {  
  14.   
  15.         String driverClass = props.getProperty(Environment.DRIVER);  
  16.         //数据库连接池的大小,默认是20个  
  17.         poolSize = PropertiesHelper.getInt(Environment.POOL_SIZE, props, 20); //default pool size 20  
  18.         log.info("Using Hibernate built-in connection pool (not for production use!)");  
  19.         log.info("Hibernate connection pool size: " + poolSize);  
  20.           
  21.         autocommit = PropertiesHelper.getBoolean(Environment.AUTOCOMMIT, props);  
  22.         log.info("autocommit mode: " + autocommit);  
  23.   
  24.         isolation = PropertiesHelper.getInteger(Environment.ISOLATION, props);  
  25.         if (isolation!=null)  
  26.         log.info( "JDBC isolation level: " + Environment.isolationLevelToString( isolation.intValue() ) );  
  27.   
  28.         if (driverClass==null) {  
  29.             log.warn("no JDBC Driver class was specified by property " + Environment.DRIVER);  
  30.         }  
  31.         else {  
  32.             try {  
  33.                 // trying via forName() first to be as close to DriverManager's semantics  
  34.                 Class.forName(driverClass);  
  35.             }  
  36.             catch (ClassNotFoundException cnfe) {  
  37.                 try {  
  38.                     ReflectHelper.classForName(driverClass);  
  39.                 }  
  40.                 catch (ClassNotFoundException e) {  
  41.                     String msg = "JDBC Driver class not found: " + driverClass;  
  42.                     log.error( msg, e );  
  43.                     throw new HibernateException(msg, e);  
  44.                 }  
  45.             }  
  46.         }  
  47.   
  48.         url = props.getProperty( Environment.URL );  
  49.         if ( url == null ) {  
  50.             String msg = "JDBC URL was not specified by property " + Environment.URL;  
  51.             log.error( msg );  
  52.             throw new HibernateException( msg );  
  53.         }  
  54.   
  55.         connectionProps = ConnectionProviderFactory.getConnectionProperties( props );  
  56.   
  57.         log.info( "using driver: " + driverClass + " at URL: " + url );  
  58.         // if debug level is enabled, then log the password, otherwise mask it  
  59.         if ( log.isDebugEnabled() ) {  
  60.             log.info( "connection properties: " + connectionProps );  
  61.         }   
  62.         else if ( log.isInfoEnabled() ) {  
  63.             log.info( "connection properties: " + PropertiesHelper.maskOut(connectionProps, "password") );  
  64.         }  
  65.   
  66.     }  
  67.   
  68.     public Connection getConnection() throws SQLException {  
  69.   
  70.         if ( log.isTraceEnabled() ) log.trace( "total checked-out connections: " + checkedOut );  
  71.   
  72.         synchronized (pool) {  
  73.             if ( !pool.isEmpty() ) {  
  74.                 int last = pool.size() - 1;  
  75.                 if ( log.isTraceEnabled() ) {  
  76.                     log.trace("using pooled JDBC connection, pool size: " + last);  
  77.                     checkedOut++;  
  78.                 }  
  79.                 //如果连接池里有空闲的连接,则返回一个连接,并将该连接从连接池里移除  
  80.                 Connection pooled = (Connection) pool.remove(last);  
  81.                 if (isolation!=null) pooled.setTransactionIsolation( isolation.intValue() );  
  82.                 if ( pooled.getAutoCommit()!=autocommit ) pooled.setAutoCommit(autocommit);  
  83.                 return pooled;  
  84.             }  
  85.         }  
  86.   
  87.         log.debug("opening new JDBC connection");  
  88.         //如果连接池里没有空闲的连接,则新建一个JDBC连接并返回  
  89.         Connection conn = DriverManager.getConnection(url, connectionProps);  
  90.         if (isolation!=null) conn.setTransactionIsolation( isolation.intValue() );  
  91.         if ( conn.getAutoCommit()!=autocommit ) conn.setAutoCommit(autocommit);  
  92.   
  93.         if ( log.isDebugEnabled() ) {  
  94.             log.debug( "created connection to: " + url + ", Isolation Level: " + conn.getTransactionIsolation() );  
  95.         }  
  96.         if ( log.isTraceEnabled() ) checkedOut++;  
  97.   
  98.         return conn;  
  99.     }  
  100.   
  101.     public void closeConnection(Connection conn) throws SQLException {  
  102.   
  103.         if ( log.isDebugEnabled() ) checkedOut--;  
  104.   
  105.         synchronized (pool) {  
  106.             int currentSize = pool.size();  
  107.             if ( currentSize < poolSize ) {  
  108.                 if ( log.isTraceEnabled() ) log.trace("returning connection to pool, pool size: " + (currentSize + 1) );  
  109.                 //如果连接池没有满,则将该连接放进连接池  
  110.                 pool.add(conn);  
  111.                 return;  
  112.             }  
  113.         }  
  114.   
  115.         log.debug("closing JDBC connection");  
  116.         //如果连接池已满,则关闭该连接  
  117.         conn.close();  
  118.   
  119.     }  
  120.   
  121.     /** 释放连接池 */  
  122.     public void close() {  
  123.   
  124.         log.info("cleaning up connection pool: " + url);  
  125.   
  126.         Iterator iter = pool.iterator();  
  127.         while ( iter.hasNext() ) {  
  128.             try {  
  129.                 ( (Connection) iter.next() ).close();  
  130.             }  
  131.             catch (SQLException sqle) {  
  132.                 log.warn("problem closing pooled connection", sqle);  
  133.             }  
  134.         }  
  135.         pool.clear();  
  136.   
  137.     }  
  138. }  


3. 基于第三方项目的连接池实现,大家可以自己去看hibernate源代码:org.hibernate.connection.C3P0ConnectionProvider,以及org.hibernate.connection.ProxoolConnectionProvider。 

对于hibernate的普通使用,如下代码: 

Java代码  收藏代码
  1. SessionFactory sf = new Configuration().configure().buildSessionFactory();  
  2. Session session = sf.openSession();  


每次openSession()获得一个session就建立了一条数据库连接,一个session其实就对应着一条连接 

如果是使用spring和hibernate进行web开发,可能你会用到下面的代码 

Java代码  收藏代码
  1. Session session = org.springframework.orm.hibernate3.SessionFactoryUtils.getSession(sessionFactory, true);  


可以自己去看spring的源代码,这个返回的Session对象其实已经被包装后缓存到了ThreadLocal对象里 
原文地址:https://www.cnblogs.com/chenying99/p/2709030.html