提要:
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
相关推荐
以上内容涵盖了JDBC的基本操作和高级特性,是学习和理解JDBC的基础。在实际开发中,结合具体数据库和应用场景,灵活运用这些知识能有效提升数据库操作的效率和安全性。在尚硅谷的2022版学习笔记中,你将找到更多实用...
System.setProperty("jdbc.drivers", "oracle.jdbc.driver.OracleDriver"); ``` 2. **建立连接** 使用`DriverManager.getConnection()`方法建立与数据库的连接。 ```java Connection conn = DriverManager.get...
### 我的JDBC课堂笔记 #### 第一章:JDBC 概述 - **ODBC**:全称为 Open Database Connectivity(开放数据库互连),是由微软提出的用于 C 程序与不同数据库进行通信的标准接口。它使得 C 程序能够通过标准的方式...
C3P0 是一个开源的 JDBC 数据源实现项目,它与 Hibernate 一起发布,实现了 JDBC3 和 JDBC2 扩展规范说明的 Connection 和 Statement 池。 ```xml <bean id="dataSource" class=...
**JDBC学习笔记第十一篇** 在Java开发中,JDBC(Java Database Connectivity)是一个重要的接口,它允许Java程序与各种数据库进行交互。本篇学习笔记将深入探讨JDBC的使用,特别是基于JDBC v2.0版本的工具类`...
总的来说,Java JDBC提供了标准且灵活的数据库访问方式,JDBC2.0引入的JNDI和DataSource接口让连接池管理更加便捷,提升了应用的性能和可维护性。了解并熟练掌握JDBC的各种概念和使用方法,对于Java开发者来说是至关...
**JDBC(Java Database Connectivity)**是Java编程...通过学习和理解JDBC,开发者可以更好地掌握数据库操作,为后续使用高级框架打下坚实基础。同时,了解JDBC的工作原理也有助于在出现问题时进行有效的调试和优化。
### J2EE-JDBC学习笔记知识点详述 #### JDBC驱动注册的三种方式 在Java开发中,使用JDBC(Java Database Connectivity)与数据库进行交互前,必须先注册对应的数据库驱动。J2EE环境下,通常有以下三种注册方式: ...
总结:理解并正确配置和使用数据源在Java Web开发中至关重要,它能提高应用的性能,简化管理和维护,同时提供分布式事务的支持。通过JNDI,我们可以将数据源的配置与应用程序解耦,使得系统的扩展性和可移植性得到...
JavaEE是企业级应用开发的重要框架,而MySQL则是一款广泛使用的开源关系型数据库...从数据库设计、JDBC编程到Servlet和JSP的使用,都需要深入理解并熟练掌握。通过持续实践和优化,可以构建出稳定、高效的企业级应用。
<property name="JDBC.ConnectionURL" value="${url}"/> <property name="JDBC.Username" value="${username}"/> <property name="JDBC.Password" value="${userpass}"/> </dataSource> ``` 4. 实体类映射...
<property name="driver" value="${driver}"/> ${url}"/> ${username}"/> ${password}"/> </dataSource> ``` 2. **映射文件**:映射文件是MyBatis的核心组件之一,其中定义了SQL语句及其映射规则...
从给定的内容来看,这里介绍了两种数据源配置方式:DBCP(Database Connection Pool)和C3P0。 ##### DBCP数据源配置 DBCP是Apache Commons Pool项目的一个子项目,主要功能是对数据库连接进行池化。下面是关于...
<property name="JDBC.ConnectionURL" value="${url}"/> <property name="JDBC.Username" value="${username}"/> <property name="JDBC.Password" value="${password}"/> </dataSource> ``` 这里定义了...
### ibatis教程学习笔记 #### 一、ibatis简介与特点 ...这些知识点对于初学者来说至关重要,它们不仅有助于快速掌握 ibatis 的使用方法,还能够帮助开发者更好地理解和应用这一框架,从而提高开发效率和代码质量。
### Java学习笔记知识点详解 #### 1. 连接数据库步骤 连接数据库是任何应用程序的基础之一,特别是对于那些需要与数据库交互的应用来说更是如此。在Java中,连接数据库的步骤通常包括以下几步: 1. **加载数据库...
Tomcat内建了两种连接池实现:Apache Commons DBCP(Database Connection Pool)和Apache Tomcat JDBC Pool。Apache Tomcat JDBC Pool自Tomcat 7版本开始成为默认的连接池实现,因为它提供了更好的性能和功能。 二...
在Java Web开发中,Tomcat是一个广泛使用的开源应用服务器,特别是对于Servlet和JSP的应用。在高并发场景下,为了优化...同时,案例笔记将帮助理解每个步骤背后的原理和最佳实践,从而更好地应对实际开发中的挑战。
- `<property name="JDBC.Driver" value="${jdbcDriver}"/>` - `<property name="JDBC.ConnectionURL" value="${jdbcUrl}"/>` - `<property name="JDBC.Username" value="${jdbcUser}"/>` - `<property name="...