spring-reading./spring-transaction/spring-transaction-jdbcTemp...
linlei ef784196ba spring事务源码分析 2024-05-28 15:22:28 +08:00
..
src/main/java/com/xcs/spring spring事务源码分析 2024-05-28 15:22:28 +08:00
README.md spring事务源码分析 2024-05-28 15:22:28 +08:00
pom.xml spring事务源码分析 2024-05-28 15:22:28 +08:00

README.md

JdbcTemplate

一、基本信息

✒️ 作者 - Lex 📝 博客 - 掘金 📚 **源码地址 ** - github

二、基本描述

JdbcTemplate是Spring Framework中的一个关键类用于简化JDBC编程提供了简洁的方法执行SQL查询、更新和批处理操作同时处理了JDBC资源的获取、释放和异常处理使得数据库操作更加简单、高效和安全。

三、主要功能

  1. 简化的JDBC操作

    • JdbcTemplate封装了JDBC的复杂性提供了简洁的方法来执行SQL查询、更新等操作无需手动管理连接、语句和结果集。
  2. 异常处理

    • JdbcTemplate自动捕获并转换JDBC异常为Spring的DataAccessException使异常处理更加便捷无需编写繁琐的异常处理代码。
  3. 参数化查询

    • 支持使用占位符进行参数化查询防止SQL注入攻击并且可以简化SQL语句的构建和维护。
  4. 批处理操作

    • 支持批处理操作可以一次性执行多个SQL语句提高数据库操作的效率。
  5. 回调机制

    • 提供了回调机制,可以在执行数据库操作前后插入自定义逻辑,如日志记录、性能监控等。
  6. 支持ORM框架

    • 可以与Spring的ORM框架如Spring Data JPA、Spring Data JDBC结合使用提供更高层次的数据库访问抽象。

四、最佳实践

使用JdbcTemplate执行SQL查询操作。首先通过配置数据库连接信息创建了一个SimpleDriverDataSource 对象来管理数据源,并创建了一个DataSourceTransactionManager对象用于事务管理。然后,通过这些对象创建了一个JdbcTemplate 实例。接着,使用query方法执行了一个查询操作,将查询结果映射为Scores对象的列表,最后打印输出了查询结果。

public class JdbcTemplateDemo {

    public static void main(String[] args) throws Exception {
        // 数据库连接 URL格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称
        String url = "jdbc:mysql://localhost:3306/spring-reading";
        // 数据库用户名
        String username = "root";
        // 数据库密码
        String password = "123456";

        // 创建 SimpleDriverDataSource 对象,用于管理数据源
        SimpleDriverDataSource dataSource = new SimpleDriverDataSource(new Driver(), url, username, password);
        // 创建 DataSourceTransactionManager 对象,用于管理事务
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
        // 创建 JdbcTemplate 对象,用于执行 SQL 语句
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        List<Scores> scoresList = jdbcTemplate.query("select * from scores ", new RowMapper<Scores>() {
            @Override
            public Scores mapRow(ResultSet rs, int rowNum) throws SQLException {
                Scores scores = new Scores();
                scores.setId(rs.getLong("id"));
                scores.setScore(rs.getLong("score"));
                return scores;
            }
        });

        scoresList.forEach(System.out::println);
    }
}

Scores类是一个简单的Java Bean。

public class Scores {

    private long id;

    private long score;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public long getScore() {
        return score;
    }

    public void setScore(long score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Scores{" +
                "id=" + id +
                ", score=" + score +
                '}';
    }
}

运行结果,从数据库查询出的分数记录。

Scores {
    id = 1715586394553, score = 40
}

Scores {
    id = 1715587289293, score = 90
}

Scores {
    id = 1715588070751, score = 20
}

Scores {
    id = 1715668592566, score = 25
}

四、源码分析

org.springframework.jdbc.core.JdbcTemplate#query(java.lang.String, org.springframework.jdbc.core.RowMapper<T>) 方法中用于执行SQL查询并将结果映射为对象列表。它接受SQL查询语句和一个RowMapper接口实现作为参数并将查询结果通过RowMapperResultSetExtractor转换为对象列表后返回。

/**
 * 执行给定的 SQL 查询,并将结果映射为对象列表。
 *
 * @param sql SQL查询语句
 * @param rowMapper 用于将每行结果映射为对象的 RowMapper 接口实现
 * @param <T> 返回结果的类型
 * @return 查询结果的对象列表
 * @throws DataAccessException 如果查询失败或转换结果时发生异常
 */
@Override
public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
    // 调用重载的 query 方法,将结果通过 RowMapperResultSetExtractor 转换为对象列表
    return result(query(sql, new RowMapperResultSetExtractor<>(rowMapper)));
}

org.springframework.jdbc.core.JdbcTemplate#query(java.lang.String, org.springframework.jdbc.core.ResultSetExtractor<T>) 方法中执行SQL查询并使用ResultSetExtractor提取结果。它接受SQL查询语句和一个ResultSetExtractor接口实现作为参数通过Statement对象执行SQL查询并将结果集交给ResultSetExtractor进行处理最终返回ResultSetExtractor提取的结果。

/**
 * 执行给定的 SQL 查询,并使用指定的 ResultSetExtractor 提取结果。
 *
 * @param sql SQL查询语句
 * @param rse 用于提取结果的 ResultSetExtractor 接口实现
 * @param <T> 返回结果的类型
 * @return 查询结果,如果没有结果则返回 null
 * @throws DataAccessException 如果查询失败或提取结果时发生异常	
 */
@Nullable
@Override
public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
    // 确保 SQL 查询语句和 ResultSetExtractor 不为空
    Assert.notNull(sql, "SQL must not be null");
    Assert.notNull(rse, "ResultSetExtractor must not be null");

    // 如果开启了调试模式则记录SQL查询语句
    if (logger.isDebugEnabled()) {
        logger.debug("Executing SQL query [" + sql + "]");
    }

    /**
     * 内部类,用于执行查询操作的回调。
     */
    class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
        /**
         * 在 Statement 中执行查询并提取结果。
         */
        @Override
        @Nullable
        public T doInStatement(Statement stmt) throws SQLException {
            ResultSet rs = null;
            try {
                // 执行查询语句,并获取结果集
                rs = stmt.executeQuery(sql);
                // 使用 ResultSetExtractor 提取结果
                return rse.extractData(rs);
            } finally {
                // 关闭结果集
                JdbcUtils.closeResultSet(rs);
            }
        }

        /**
         * 获取查询的 SQL 语句。
         */
        @Override
        public String getSql() {
            return sql;
        }
    }

    // 执行查询,并返回结果
    return execute(new QueryStatementCallback(), true);
}

org.springframework.jdbc.core.JdbcTemplate#execute(org.springframework.jdbc.core.StatementCallback<T>, boolean) 方法中执行数据库操作回调并根据需要关闭资源。它接受一个数据库操作回调对象和一个布尔值参数布尔值参数指示是否在执行完操作后关闭资源。在方法内部首先获取数据库连接然后创建Statement对象并应用设置。接着执行数据库操作回调并处理操作过程中的警告信息。如果操作过程中发生了SQLException会及时释放连接并抛出DataAccessException。最后在finally块中根据需要关闭Statement和连接资源。

/**
 * 执行给定的数据库操作回调,并根据需要关闭资源。
 *
 * @param action 数据库操作回调对象
 * @param closeResources 是否关闭资源的标志,如果为 true则在执行完操作后关闭资源否则不关闭
 * @param <T> 返回结果的类型
 * @return 执行操作的结果
 * @throws DataAccessException 如果执行操作失败
 */
@Nullable
private <T> T execute(StatementCallback<T> action, boolean closeResources) throws DataAccessException {
    // 确保回调对象不为空
    Assert.notNull(action, "Callback object must not be null");

    // 获取连接
    Connection con = DataSourceUtils.getConnection(obtainDataSource());
    Statement stmt = null;
    try {
        // 创建 Statement
        stmt = con.createStatement();
        // 应用 Statement 的设置
        applyStatementSettings(stmt);
        // 执行数据库操作
        T result = action.doInStatement(stmt);
        // 处理警告信息
        handleWarnings(stmt);
        return result;
    } catch (SQLException ex) {
        // 在可能发生连接池死锁的情况下,尽早释放连接以避免死锁
        String sql = getSql(action);
        JdbcUtils.closeStatement(stmt);
        stmt = null;
        DataSourceUtils.releaseConnection(con, getDataSource());
        con = null;
        // 转换异常并抛出
        throw translateException("StatementCallback", sql, ex);
    } finally {
        // 如果需要关闭资源,则在 finally 中关闭 Statement 和连接
        if (closeResources) {
            JdbcUtils.closeStatement(stmt);
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }
}