`

JDBC的Driver,Connection和Datasource的理解笔记

阅读更多

提要:

1.JDBC连接数据库前常见到Class.forName("com.mysql.jdbc.Driver"),为什么要这么一句话?可不可以不要。

2.ibatis使用SqlMapClient时如果要显示使用数据库连接,sqlMap.getCurrentConnection()和sqlMap.getDatasource().getConnection()的区别是什么?

3.通过sqlMap.getDatasource().getConnection()拿到的连接需要close()么,那么sqlMap.getCurrentConnection() 的需要自己close么。

================================================================================

理解:

1.之前学习JDBC时总会遇到在获取连接前使用Class.form(#Driver#)的语句,知道是将Driver注册到DriverManager里,但一直不明白其原理。因为Class.forName的返回值并没有被使用。后来看了Driver里的代码才明白原理。

   关键的一句是:

     static {
         try {
             java.sql.DriverManager.registerDriver(new Driver());
         } catch (SQLException E) {
             throw new RuntimeException("Can't register driver!");
         }
     }

   可以见到在static块中,Driver将自己注册到了DriverManager中,因此只要Driver被加载就能完成此操作。这就是为啥要用Class.formName来注册了。

   那么可以不可以不要这句话呢,我试了下,jdk1.7是可以的,这又是为什么呢?

   这个归功SPI,一种服务发现机制,这个代码在DriverManager的loadInitialDrivers方法中

 AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {

                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator driversIterator = loadedDrivers.iterator();

                try{
                    while(driversIterator.hasNext()) {
                        println(" Loading done by the java.util.ServiceLoader :  "+driversIterator.next());
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;
            }
        });

 

     ServiceLoader就是SPI的关键,他通过搜索classpath下的jar包里的META-INF/services/下面的文件文本文件,文件名是接口名,文件内容为实现类的className,然后通过文件内容找到对应接口的实现类进行加载,这样就完成了自动加载。这种服务发现的机制真的很精妙,各个厂商按一定标准完成自己的Driver,使用者只需要将jar包加到classpath下,DriverManager使用SPI加载驱动,没有一句硬编码。当然这个也得益于DriverManager对桥接模式的使用。也就是将行为进行抽象,面向接口编程。

   

 

2.之前在集成了ibatis的项目里需要拿到Connection执行单独的sql语句,然后发现sqlMapClient拿到连接有两种方式,一个是API sqlMapClient.getCurrentConnection(),一个是使用Datasource,sqlMapClient.getDatasource().getConnection(),我到底使用哪个呢?

    其实直接使用sqlMapClient.getCurrentConnection()返回的是NULL,那么这个Current是什么意思呢?

    实际上current是获取当前线程启动的事务里创建的连接,

    要先调用sqlMapClient.startTransaction(),那么getCurrentConnection()才会有值。

    SqlMapSessionImpl

  public Connection getCurrentConnection() throws SQLException {
    try {
      Connection conn = null;
      Transaction trans = delegate.getTransaction(sessionScope);
      if (trans != null) {
        conn = trans.getConnection();
      }
      return conn;
    } catch (TransactionException e) {
      throw new NestedSQLException("Error getting Connection from Transaction.  Cause: " + e, e);
    }
  }
     可以看到其实是获取到Transaction中的Connection,如果没有事务,那么con为NULL,因此getCurrentConnection是在事务期间获取当前事务连接的方法。

 

     ps,其实sqlMap会为每次查询创建事务,如果你没有显示启动事务,即使是select语句,

     如SqlMapExecutorDelegate

     

  public Object queryForObject(SessionScope sessionScope, String id, Object paramObject, Object resultObject) throws SQLException {
    Object object = null;

    MappedStatement ms = getMappedStatement(id);
    Transaction trans = getTransaction(sessionScope);
    boolean autoStart = trans == null;

    try {
      trans = autoStartTransaction(sessionScope, autoStart, trans);

      StatementScope statementScope = beginStatementScope(sessionScope, ms);
      try {
        object = ms.executeQueryForObject(statementScope, trans, paramObject, resultObject);
      } finally {
        endStatementScope(statementScope);
      }

      autoCommitTransaction(sessionScope, autoStart);
    } finally {
      autoEndTransaction(sessionScope, autoStart);
    }

    return object;
  }

      可以看到,如果transaction是空的,sqlMap会显示开启一个transaction。因此为了避免开启事务,我选择了sqlMapClient.getDatasource().getConnection().通过数据源BaiscDatasource显示拿到Connection,那又有一个问题了,拿到这个Connection我是否需要把他关闭掉呢?

 

3.这个自然就联想到了,ibatis,我们在使用sqlMap时并没有释放连接的动作。其实他是在模板模式里释放了,然后看了下Datasource的接口

     

public interface DataSource  extends CommonDataSource,Wrapper {

  Connection getConnection() throws SQLException;

  Connection getConnection(String username, String password)
    throws SQLException;

}

     也没有releaseConnection的操作,难道通过Datasource拿连接不需要release么。

     实际上不是的,我做了下试验,手动创建一个BasicDatasource,然后调用getConnection(),在第9次时发生阻塞了,说明已经没有可用的连接了,所以我们需要使用connection.close()方法来释放连接的。那么close()到底是关闭连接还是释放连接呢。实际上是释放连接,连接并不会关闭。

       其实BaiscDatasource里对Connection进行了包装,使用PoolableConnectionFactory来创建PoolableConnection:

      

    public Object makeObject() throws Exception {
        Connection conn = _connFactory.createConnection();
        if (conn == null) {
            throw new IllegalStateException("Connection factory returned null from createConnection");
        }
        initializeConnection(conn);
        if(null != _stmtPoolFactory) {
            KeyedObjectPool stmtpool = _stmtPoolFactory.createPool();
            conn = new PoolingConnection(conn,stmtpool);
            stmtpool.setFactory((PoolingConnection)conn);
        }
        return new PoolableConnection(conn,_pool,_config);
    }

            其中_connectionFactory是在前面创建的DriverConnectionFactory,实现直接调用Driver获取原始的Connection,然后在return时包装为PoolableConnection。

            所以让我们看看PoolableConnection的close方法:

            

        if (!isUnderlyingConectionClosed) {
            // Normal close: underlying connection is still open, so we
            // simply need to return this proxy to the pool
            try {
                _pool.returnObject(this); // XXX should be guarded to happen at most once
            } catch(IllegalStateException e) {
                // pool is closed, so close the connection
                passivate();
                getInnermostDelegate().close();
            } catch(SQLException e) {
                throw e;
            } catch(RuntimeException e) {
                throw e;
            } catch(Exception e) {
                throw (SQLException) new SQLException("Cannot close connection (return to pool failed)").initCause(e);
            }
        } else {
            // Abnormal close: underlying connection closed unexpectedly, so we
            // must destroy this proxy
            try {
                _pool.invalidateObject(this); // XXX should be guarded to happen at most once
            } catch(IllegalStateException e) {
                // pool is closed, so close the connection
                passivate();
                getInnermostDelegate().close();
            } catch (Exception ie) {
                // DO NOTHING, "Already closed" exception thrown below
            }
            throw new SQLException("Already closed.");
        }

       

      可以看到如果连接没有被强制关闭,那么PoolConnecion是将连接释放会connectionPool,如果连接已经是关闭的,则把连接从Pool里失效掉,在下次获取时,新的连接将会被创建。保证Pool里有足够多的ActiveConnection(默认是8个)。

         那么SqlMap是在哪里关闭连接的呢?实际上是在endTransaction()里,

 

   摘自ExternalTransaction

  public void close() throws SQLException, TransactionException {
    if (connection != null) {
      try {
        isolationLevel.restoreIsolationLevel(connection);
      } finally {
        connection.close();
        connection = null;
      }
    }
  }
 

     Finish

    

分享到:
评论

相关推荐

    JDBC数据源(DataSource)的简单实现

    NULL 博文链接:https://jlins.iteye.com/blog/695322

    JDBC DruidDataSource dataSource = new DruidDataSource();

    JDBC DruidDataSource dataSource = new DruidDataSource();

    JDBC Connection Pool org.apache.tomcat.jdbc.pool

    apache出品,用来取代老旧的dbcp

    Spring Data JDBC与JDBC的区别

     Driver:JDBC驱动  Connection:数据库连接  Statement:语句,执行SQL  PrepareStatement:预编译语句,性能更好  CallableStatement:调用存储过程  ResultSet:结果集,封装了多条记录  JDBC数据库连接池/...

    springboot多数据源快速启动器,基于 springBoot2.0

    一个简单能直接运行的项目基于 springBoot2.0. 它适用于读写分离,一主多从的环境。 主数据库使用 INSERT UPDATE DELETE 操作. 从数据库使用 SELECT 操作. 如果你的项目比较复杂,建议使用 sharding-jdbc .

    ibatis基础案例

    &lt;property name="JDBC.Driver" value="${driver}" /&gt; &lt;property name="JDBC.ConnectionURL" value="${url}" /&gt; &lt;property name="JDBC.Username" value="${username}" /&gt; &lt;property name="JDBC.Password...

    springboot双数据源

    spring.datasource.db2.driver-class-name=oracle.jdbc.driver.OracleDriver spring.datasource.db2.max-idle=10 spring.datasource.db2.max-wait=10000 spring.datasource.db2.min-idle=5 spring.datasource.db2....

    JDBC详解HTML-JDBC.pp

    1、JDBC(Java Database Connection):java连接数据库统一接口API,底层主要通过直接的JDBC驱动和 JDBC-ODBC桥驱动实现与数据库的连接。 1&gt;.JDBC驱动程序类型: &lt;1&gt;.JDBC-ODBC桥加ODBC驱动程序:需要ODBC驱动,适合...

    阿里巴巴的开源项目JDBC连接池、监控组件 Druid.zip

    Druid是一个JDBC组件,它包括三部分: DruidDriver 代理Driver,能够提供基于Filter-Chain模式的插件体系。 DruidDataSource 高效可管理的数据库连接池。 SQLParser Druid可以做什么? 1) 可以监控数据库访问...

    jdbc——内嵌事务

    &lt;property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"&gt; &lt;value&gt;false &lt;bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"&gt; &lt;property name="...

    ibatis应用

    &lt;property name="JDBC.Driver" value="${driver}" /&gt; &lt;property name="JDBC.ConnectionURL" value="${url}" /&gt; &lt;property name="JDBC.Username" value="${username}" /&gt; &lt;property name="JDBC.Password" value=...

    jdbc-ldap spring

    class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt; ${ldap.datasource.driverClassName}" /&gt; ${ldap.datasource.url}" /&gt; ${ldap.datasource.username}" /&gt; ${ldap.datasource....

    hibernate.properties

    #hibernate.connection.driver_class oracle.jdbc.driver.OracleDriver #hibernate.connection.username ora #hibernate.connection.password ora #hibernate.connection.url jdbc:oracle:thin:@localhost:1521:orcl...

    jdbc-drivers.zip

    idea离线驱动集合(pg,mysql,H2,mongodb)

    酒店住宿管理系统 用EXT+Spring+Hibernate(上)

    修改数据库密码 &lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt; &lt;property name="driverClassName" value="com.mysql.jdbc.Driver"/&gt; ...

    springboot2+两种方式:(读写分离、动态DataSource、事务懒处理)+sharding-jdbc.zip

    2、sharding-jdbc (1)、实现多种模式的读写分离 (2)、支持事务 (3)、配置中心化 (4)、存储过程不能正常解析 ———————————————— 版权声明:本文为CSDN博主「毛豆有毛没豆」的原创文章,...

    JDBC 3.0数据库开发与设计

    4.7.4 连接池和DataSource实现 4.7.5 包含连接池的数据源配置 4.7.6 池连接对象对于语句的重新利用 4.7.7 关闭池连接语句 4.7.8 连接池使用实例 4.8 分布式事务处理 4.8.1 XADataSource接口和XAConnection接口...

    connection_reset.rar

    解决oracle数据库发生'Connection reset by peer' or 'Connection reset'的错误 这边的数据库连接是kettle自带的h2数据库,只要有kettle就有这个数据库,目录位于data-integration\samples\db\,连接方式是 ...

    DataSource接口介绍与使用

    JDBC1.0是原来是用DriverManager类来产生一个对数据源的连接。JDBC2.0用一种替代的方法,使用DataSource的实现,代码变的更小巧精致,也更容易控制。

    SpringBoot框架Datasource注入

    该项目采用标签形式对Datasource进行注入将Datasource组件交给容器进行统一管理

Global site tag (gtag.js) - Google Analytics