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月前
|
机器学习/深度学习 存储 PyTorch
Pytorch中in-place操作相关错误解析及detach()方法说明
Pytorch中in-place操作相关错误解析及detach()方法说明
41 0
|
1月前
|
存储 开发框架 开发者
QT C++焦点事件:多角度解析实用技巧与方法
QT C++焦点事件:多角度解析实用技巧与方法
155 0
|
1月前
|
敏捷开发 开发框架 数据可视化
|
1月前
|
C语言
【C语言】大小写字母的相互转化:多种方法解析及原理说明
【C语言】大小写字母的相互转化:多种方法解析及原理说明
109 0
|
1月前
|
机器学习/深度学习 数据采集 自然语言处理
岭回归与LASSO回归:解析两大经典线性回归方法
岭回归与LASSO回归:解析两大经典线性回归方法
岭回归与LASSO回归:解析两大经典线性回归方法
|
4天前
|
SQL 分布式计算 资源调度
一文解析 ODPS SQL 任务优化方法原理
本文重点尝试从ODPS SQL的逻辑执行计划和Logview中的执行计划出发,分析日常数据研发过程中各种优化方法背后的原理,覆盖了部分调优方法的分析,从知道怎么优化,到为什么这样优化,以及还能怎样优化。
|
4天前
并发编程之Callable方法的详细解析(带小案例)
并发编程之Callable方法的详细解析(带小案例)
11 0
|
23天前
|
存储 缓存 监控
深入解析linux内存指标:快速定位系统内存问题的有效技巧与实用方法(free、top、ps、vmstat、cachestat、cachetop、sar、swap、动态内存、cgroops、oom)
深入解析linux内存指标:快速定位系统内存问题的有效技巧与实用方法(free、top、ps、vmstat、cachestat、cachetop、sar、swap、动态内存、cgroops、oom)
|
23天前
|
存储 算法
从动态规划到贪心算法:最长递增子序列问题的方法全解析
从动态规划到贪心算法:最长递增子序列问题的方法全解析
21 2
|
30天前
|
算法 项目管理 开发者
【Conan 入门教程 】深入解析Conan中的依赖关系的定义方法(In-depth Analysis of Dependency Definition Methods in Conan)
【Conan 入门教程 】深入解析Conan中的依赖关系的定义方法(In-depth Analysis of Dependency Definition Methods in Conan)
38 0

推荐镜像

更多