SpringJDBC解析2-execute方法

简介: 大家都使用过JDBCTEMPLATE的execute方法,execute作为数据库操作的核心入口,将大多数数据库操作相同的步骤统一封装,而将个性化的操作使用参数PreparedStatementCallback回调。

大家都使用过JDBCTEMPLATE的execute方法,execute作为数据库操作的核心入口,将大多数数据库操作相同的步骤统一封装,而将个性化的操作使用参数PreparedStatementCallback回调。

public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)  
        throws DataAccessException {  
    Assert.notNull(psc, "PreparedStatementCreator must not be null");  
    Assert.notNull(action, "Callback object must not be null");  
    if (logger.isDebugEnabled()) {  
        String sql = getSql(psc);  
        logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));  
    }  
    //获取数据库连接  
    Connection con = DataSourceUtils.getConnection(getDataSource());  
    PreparedStatement ps = null;  
    try {  
        Connection conToUse = con;  
        if (this.nativeJdbcExtractor != null &&  
                this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {  
            conToUse = this.nativeJdbcExtractor.getNativeConnection(con);  
        }  
        ps = psc.createPreparedStatement(conToUse);  
        //应用用户设定的输入参数  
        applyStatementSettings(ps);  
        PreparedStatement psToUse = ps;  
        if (this.nativeJdbcExtractor != null) {  
            psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);  
        }  
        //调用回掉函数  
        T result = action.doInPreparedStatement(psToUse);  
        handleWarnings(ps);  
        return result;  
    }  
    catch (SQLException ex) {  //释放数据库连接避免当异常转换器没有被初始化的时候出现潜在的连接池死锁  
        if (psc instanceof ParameterDisposer) {  
            ((ParameterDisposer) psc).cleanupParameters();  
        }  
        String sql = getSql(psc);  
        psc = null;  
        JdbcUtils.closeStatement(ps);  
        ps = null;  
        DataSourceUtils.releaseConnection(con, getDataSource());  
        con = null;  
        throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);  
    }  
    finally {  
        if (psc instanceof ParameterDisposer) {  
            ((ParameterDisposer) psc).cleanupParameters();  
        }  
        JdbcUtils.closeStatement(ps);  
        DataSourceUtils.releaseConnection(con, getDataSource());  
    }  
} 

以上方法对常用操作进行了封装,包括如下几项内容:

  1. 获取数据库连接
  2. 应用用户设定的输入参数
  3. 调用回调函数
  4. 警告处理
  5. 资源释放

获取数据库连接

public static Connection doGetConnection(DataSource dataSource) throws SQLException {  
    Assert.notNull(dataSource, "No DataSource specified");  
    ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);  
    if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {  
        conHolder.requested();  
        if (!conHolder.hasConnection()) {  
            logger.debug("Fetching resumed JDBC Connection from DataSource");  
            conHolder.setConnection(dataSource.getConnection());  
        }  
        return conHolder.getConnection();  
    }  
    logger.debug("Fetching JDBC Connection from DataSource");  
    Connection con = dataSource.getConnection();  
    //当前线程支持同步  
    if (TransactionSynchronizationManager.isSynchronizationActive()) {  
        logger.debug("Registering transaction synchronization for JDBC Connection");  //在事务中使用同一数据库连接  
        ConnectionHolder holderToUse = conHolder;  
        if (holderToUse == null) {  
            holderToUse = new ConnectionHolder(con);  
        }  
        else {  
            holderToUse.setConnection(con);  
        }  
        //记录数据库连接  
        holderToUse.requested();  
        TransactionSynchronizationManager.registerSynchronization(  
                new ConnectionSynchronization(holderToUse, dataSource));  
        holderToUse.setSynchronizedWithTransaction(true);  
        if (holderToUse != conHolder) {  
            TransactionSynchronizationManager.bindResource(dataSource, holderToUse);  
        }  
    }  
  
    return con;  
}  

在数据库连接方面,Spring主要考虑的是关于事务方面的处理。基于事务处理的特殊性,Spring需要保证线程中的数据库操作都是使用同一个事务连接

应用用户设定的输入参数

protected void applyStatementSettings(Statement stmt) throws SQLException {  
    int fetchSize = getFetchSize();  
    if (fetchSize > 0) {  
        stmt.setFetchSize(fetchSize);  
    }  
    int maxRows = getMaxRows();  
    if (maxRows > 0) {  
        stmt.setMaxRows(maxRows);  
    }  
    DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());  
}  

setFetchSize最主要是为了减少网络交互次数设计的。访问ResultSet时,如果它每次只从服务器读取一行数据,则会产生大量的开销。setFetchSize的意思是ResultSet会一次性从服务器上取得多少行数据回来,这样在下次rs.next时,它可以直接从内存中获取数据而不需要网络交互,提高了效率。这个设置可能会被某些JDBC驱动忽略,而且设置过大也会造成内存的上升。setMaxRows将此Statement对象生成的所有ResultSet对象可以包含的最大行数设置为给定数。

调用回调函数

处理一些通用方法外的个性化处理,也就是PreparedStatementCallback类型的参数的doInPreparedStatement方法的回调。

T result = action.doInPreparedStatement(psToUse);

警告处理

protected void handleWarnings(Statement stmt) throws SQLException {  
    //当设置为忽略警告时只尝试打印日志  
    if (isIgnoreWarnings()) {  
        if (logger.isDebugEnabled()) {  
            //如果日志开启的情况下下打印日志  
            SQLWarning warningToLog = stmt.getWarnings();  
            while (warningToLog != null) {  
                logger.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + "', error code '" +  
                        warningToLog.getErrorCode() + "', message [" + warningToLog.getMessage() + "]");  
                warningToLog = warningToLog.getNextWarning();  
            }  
        }  
    }  
    else {  
        handleWarnings(stmt.getWarnings());  
    }  
}  

这里用到了一个类SQLWarning,SQLWarning提供关于数据库访问警告信息的异常。这些警告直接链接到导致报告警告的方法所在的对象。警告可以从Connection、Statement和ResultSet对象中获得。试图在已经关闭的连接上获取警告将导致抛出异常。类似地,试图在已经关闭的语句上或已经关闭的结果集上获取警告也将导致抛出异常。注意,关闭语句时还会关闭它可能生成的结果集。

最常见的警告DataTruncation:DataTruncation直接继承SQLWarning,由于某种原因意外地截断数据值时会以DataTruncation 警告形式报告异常。对于警告的处理方式并不是直接抛出异常,出现警告很可能会出现数据错误,但是,并不一定会影响程序执行,所以用户可以自己设置处理警告的方式,如默认的是忽略警告,当出现警告时只打印警告日志,而另一种方式只直接抛出异常。

资源释放

数据库的连接并不是直接调用了Connection的API中的close()方法。考虑到存在事务的情况,如果当前线程存在事务,那么说明在当前线程中存在共用数据库的连接,这种情况下直接使用ConnectionHolder中的released方法进行连接数减一,而不是真正的释放连接。

public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException {  
    if (con == null) {  
        return;  
    }  
    if (dataSource != null) {  
        //当前线程存在事务的情况下说明存在共用数据库连接  
        //直接使用ConnectionHolder中的released方法进行连接数减一而不是真正的释放链接  
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);  
        if (conHolder != null && connectionEquals(conHolder, con)) {  
            // It's the transactional Connection: Don't close it.  
            conHolder.released();  
            return;  
        }  
    }  
    logger.debug("Returning JDBC Connection to DataSource");  
    //在此方法中判断是否需要关闭连接,然后再进行关闭  
    doCloseConnection(con, dataSource);  
}  

 

目录
相关文章
|
1月前
|
存储 缓存 安全
第二章 HTTP请求方法、状态码详解与缓存机制解析
第二章 HTTP请求方法、状态码详解与缓存机制解析
|
1天前
|
机器学习/深度学习 算法 数据挖掘
算法金 | K-均值、层次、DBSCAN聚类方法解析
**摘要:** 这篇文章介绍了聚类分析的基本概念和几种主要的聚类算法。聚类是无监督学习中用于发现数据内在结构的技术,常用于市场分析、图像分割等场景。K-均值是一种基于划分的算法,简单高效但易受初始值影响;层次聚类包括凝聚和分裂方式,形成层次结构但计算复杂;DBSCAN基于密度,能处理任意形状的簇,但参数选择敏感。文章还讨论了这些算法的优缺点和适用场景,并提供了相关资源链接和Python实现。
21 9
算法金 | K-均值、层次、DBSCAN聚类方法解析
|
2天前
|
域名解析 存储 缓存
HTTP请求流程概览:浏览器构建请求行含方法、URL和版本;检查缓存;解析IP与端口
【6月更文挑战第23天】 HTTP请求流程概览:浏览器构建请求行含方法、URL和版本;检查缓存;解析IP与端口;TCP连接(HTTP/1.1可能需排队);三次握手;发送请求头与体;服务器处理并返回响应;TCP连接可能关闭或保持;浏览器接收并显示响应,更新缓存。HTTP版本间有差异。
12 5
|
10天前
|
存储 安全 Java
深入理解Java中的ThreadLocal机制:原理、方法与使用场景解析
深入理解Java中的ThreadLocal机制:原理、方法与使用场景解析
20 2
|
15天前
|
存储 算法 数据可视化
【模拟面试问答】深入解析力扣164题:最大间距(桶排序与排序方法详解)
【模拟面试问答】深入解析力扣164题:最大间距(桶排序与排序方法详解)
|
14天前
|
SQL 算法 数据挖掘
深入解析力扣183题:从不订购的客户(LEFT JOIN与子查询方法详解)
深入解析力扣183题:从不订购的客户(LEFT JOIN与子查询方法详解)
|
14天前
|
算法 数据挖掘 大数据
深入解析力扣172题:阶乘后的零(计算因子5的方法详解及模拟面试问答)
深入解析力扣172题:阶乘后的零(计算因子5的方法详解及模拟面试问答)
|
15天前
|
SQL 算法 数据可视化
LeetCode题目92:反转链表ll 【python 递归与迭代方法全解析】
LeetCode题目92:反转链表ll 【python 递归与迭代方法全解析】
|
18天前
|
测试技术 C语言
数据结构学习记录——树习题—Tree Traversals Again(题目描述、输入输出示例、解题思路、解题方法C语言、解析)
数据结构学习记录——树习题—Tree Traversals Again(题目描述、输入输出示例、解题思路、解题方法C语言、解析)
14 1
|
23天前
|
C++ 存储 Java
C++ 引用和指针:内存地址、创建方法及应用解析
'markdown'C++ 中的引用是现有变量的别名,用 `&` 创建。例如:`string &meal = food;`。指针通过 `&` 获取变量内存地址,用 `*` 创建。指针变量存储地址,如 `string *ptr = &food;`。引用不可为空且不可变,指针可为空且可变,适用于动态内存和复杂数据结构。两者在函数参数传递和效率提升方面各有优势。 ```

推荐镜像

更多