Spring-JDBC 源码学习(1) —— JdbcTemplate

Spring-JDBC 源码学习(1)

JdbcTemplate

Spring-JDBC 源码学习(1) —— JdbcTemplate_第1张图片

JdbcTemplate 类作为 org.springframework.core 包下的核心类,对 Java 实现的 JDBC 进行了一定程度的封装,可以简化 JDBC 的使用,同时避免许多常见的错误。由该类来执行 JDBC 工作流,应用程序只需要提供 SQL 语句就可以获得 DB 执行结果(当然实际操作上没有描述的这么简单)。

使用 JdbcTemplate 类只需要实现两个回调接口 PreparedStatementCreator (创建一个 PreparedStatement,给定连接,提供 SQL 语句以及必要的参数), ResultSetExtractor (用于花式获取结果集)。当然,不实现上述两个接口也可以进行简单的数据库操作,比如只通过 JdbcTemplate 获取一个连接(Connection) 或者执行一个静态 SQL Update。

看不懂无所谓,先继续向下看,会做更详细的讲解。

JdbcAccessor

首先了解 JdbcTemplate 的实现,JdbcTemplate 继承了 JdbcAccessor 抽象类,该抽象类有两个主要属性—— DataSource dataSource & SQLExceptionTranslator exceptionTranslator ,以及一个懒加载标识符 lazyInit

其中,

  • DataSource 可以认为是一个存储有与数据库相关的属性实例,主要方法包括 Connection getConnection() & Connection getConnection(String username, String password)
  • SQLExceptionTranslator 利用策略模式将 Java 定义的 SQLException 转换成 Spring 声明的 DataAccessException。

JdbcAccessor 实现的 InitializingBean 接口,InitializingBean 接口为 Bean 提供了一个初始化实例的方法 —— afterPropertiesSet() 方法,凡是继承该接口的类,一般都在类构造方法中执行该方法。同样的,声明初始化实例调用的方法还可采用 在初始化 bean 时执行 myInitMethod() 方法。具体执行顺序为 :

  1. bean 的属性注入
  2. 调用 afterPropertiesSet() 方法
  3. 执行 myInitMethod() 方法

此处继承该接口是为了实现 DataSource 验证以及 SQLExceptionTranslator 的懒加载 OR NOT 的选择。

@Override
public void afterPropertiesSet() {
    if (getDataSource() == null) {  // 判断是否注入了 DataSource
        throw new IllegalArgumentException("Property 'dataSource' is required");
    }
    if (!isLazyInit()) {    // 根据懒加载标识符选择执行与否
        getExceptionTranslator();   // 获取一个 SQLExceptionTranslator 实例
    }
}

该方法在 BeanFactory 完成该 bean 的依赖注入后执行,将首先判断 DataSource 是否已经被注入,再根据懒加载标识符来决定是否实例化一个 SQLExceptionTranslator 。

JdbcOperations

同时,JdbcTemplate 类实现了 JdbcOperations 接口,该接口定义了基本的 JDBC 操作。基本方法如下:

  1. T execute(ConnectionCallback action);
  2. T execute(StatementCallback action);
  3. T execute(PreparedStatementCreator psc, PreparedStatementCallback action);
  4. T execute(CallableStatementCreator csc, CallableStatementCallback action);

Spring-JDBC 源码学习(1) —— JdbcTemplate_第2张图片

其它的 execute(…) , query(…), update(…) 等最终都将调用上述 4 种方法其一来完成目的。

详细观察上述方法的入参, ConnectionCallback, StatementCallback, PreparedStatementCallback 和 CallableStatementCallback 四个 Callback 接口,分别都是函数式接口,其中的唯一方法 doInXXX() 将在 execute() 中被调用,以此实现获得 ResultSet 并返回 Result 。execute 中调用 doInXXX() 的通用代码如下 (以 Statement 为例):

对于不理解回调的同学,请自行了解概念

public  T execute(StatementCallback action) throws DataAccessException {
    // 通过工具类 DataSourceUtils 获取一个连接
    Connection con = DataSourceUtils.getConnection(obtainDataSource());
    // 一个 Statement 空实例,PreparedStatement, CallableStatement 类似
    Statement stmt = null;
    try {
        stmt = con.createStatement();   // 通过连接(Connection)获取一个 Statement
        applyStatementSettings(stmt);   // 配置 Statement 参数
        // 回调执行 doInXXX() 方法, 并获得 result
        T result = action.doInStatement(stmt);  
        handleWarnings(stmt);
        return result;
    }
    catch (SQLException ex) {
        // Release Connection early, to avoid potential connection pool deadlock
        // in the case when the exception translator hasn't been initialized yet.
        String sql = getSql(action);
        JdbcUtils.closeStatement(stmt);
        stmt = null;
        DataSourceUtils.releaseConnection(con, getDataSource());
        con = null;
        throw translateException("StatementCallback", sql, ex);
    }
    finally {
        JdbcUtils.closeStatement(stmt);
        DataSourceUtils.releaseConnection(con, getDataSource());
    }
}

对于 Statement, PreparedStatement, CallableStatement 的区别,首先下图表现了三个接口的继承关系。

  • Statement 可以支持静态 SQL 语句
  • PreparedStatement 支持可变参数的 SQL 语句
  • CallableStatement 支持可变参数的 SQL 语句,并且支持定制 DB 的输出结果

Spring-JDBC 源码学习(1) —— JdbcTemplate_第3张图片

你可能感兴趣的:(Spring-JDBC)