从源代码解读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对象里
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值