jdbcTemplate源码解析

应用

JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);
        String sql="select * from user1 where username=?";
        User user=jdbcTemplate.queryForObject(sql, new RowMapper<User>(){

            @Override
            public User mapRow(ResultSet res, int arg1) throws SQLException {
                // TODO Auto-generated method stub
                String username=res.getString("username");
                String pass=res.getString("pass");
                User user=new User();
                user.setUsername(username);
                user.setPass(pass);
                System.out.println(res);
                return user;
            }

        },"zengyuan");
在使用jdbcTemplate一直有个疑问,这个mapRow方法中的ResultSet对象是如何传进去的,关于这个问题,开始有以下几点猜想:

① 因为是spring,所以本身就首先想到了AOP,可能是将一个返回ResultSet的对象的通知直接织入到mapRow的方法之中。
② 后台调用mapRow方法,然后通过创建对象的形式将ResultSet放入该方法中。

源码分析

第一步:查看queryForObject()方法
源码:

@Override
    public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException {
        List<T> results = query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper, 1));
        return DataAccessUtils.requiredSingleResult(results);
    }

可以看到该方法调用了query()方法,并创建了一个RowMapperResultSetExtractor()对象,将我创建的RowMapper对象传入到该类中,我们进入该类中,这个类很重要,我们来分析下源码:

public class RowMapperResultSetExtractor<T> implements ResultSetExtractor<List<T>> {

    private final RowMapper<T> rowMapper;

    private final int rowsExpected;


    /**
     * Create a new RowMapperResultSetExtractor.
     * @param rowMapper the RowMapper which creates an object for each row
     */
    public RowMapperResultSetExtractor(RowMapper<T> rowMapper) {
        this(rowMapper, 0);
    }

    /**
     * Create a new RowMapperResultSetExtractor.
     * @param rowMapper the RowMapper which creates an object for each row
     * @param rowsExpected the number of expected rows
     * (just used for optimized collection handling)
     */
    public RowMapperResultSetExtractor(RowMapper<T> rowMapper, int rowsExpected) {
        Assert.notNull(rowMapper, "RowMapper is required");
        this.rowMapper = rowMapper;
        this.rowsExpected = rowsExpected;
    }


    @Override
    public List<T> extractData(ResultSet rs) throws SQLException {
        List<T> results = (this.rowsExpected > 0 ? new ArrayList<T>(this.rowsExpected) : new ArrayList<T>());
        int rowNum = 0;
        while (rs.next()) {
            results.add(this.rowMapper.mapRow(rs, rowNum++));
        }
        return results;
    }
可以看到该类结构如下:

① RowMapper rowMapper;
该变量是通过构造器将我们创建的RowMapper对象赋值给该类变量。
② int rowsExpected;
该int型变量也是通过构造器赋值的,初始值设为1.该变量表示取出来的记录数量。
③ extractData()方法
该方法很重要,我们看下该方法首先创建了一个大小为rowsExpected的集合容器,然后调用RowMapper的mapRow()方法,将我们封装的对象装入集合中。
到这一步我已经猜到,现在只需调用RowMapperResultSetExtractor的extractData()方法,然后返回集合对象就可以了,果不其然,我们继续往下看代码。
第二步:调用query()方法
其实jdbcTemplate类中用到了很多的方法重载,所以有时候你会看晕,我们顺着第一步的query()点进去,可以看到以下代码:

@Override
    public <T> T query(String sql, Object[] args, ResultSetExtractor<T> rse) throws DataAccessException {
        return query(sql, new ArgPreparedStatementSetter(args), rse);

我们可以看到在query()方法中继续调用了重载的query()方法,我想这一步传进去的new ArgPreparedStatementSetter(args)对象,我就不分析了,大家应该能够想到这个类中肯定是向PreparedStatement中注入参数,接着继续下一步:

@Override
    public <T> T query(String sql, PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
        return query(new SimplePreparedStatementCreator(sql), pss, rse);
    }
我只能说大师的代码真的包装了好多层,不过每一层都是有用的,估计到这大家有些着急了,不过也终于见到庐山真面目了,我们来分析下这个query()方法,源码如下:
public <T> T query(
            PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
            throws DataAccessException {

        Assert.notNull(rse, "ResultSetExtractor must not be null");
        logger.debug("Executing prepared SQL query");

        return execute(psc, new PreparedStatementCallback<T>() {
            @Override
            public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
                ResultSet rs = null;
                try {
                    if (pss != null) {
                        pss.setValues(ps);
                    }
                    rs = ps.executeQuery();
                    ResultSet rsToUse = rs;
                    if (nativeJdbcExtractor != null) {
                        rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
                    }
                    return rse.extractData(rsToUse);
                }
                finally {
                    JdbcUtils.closeResultSet(rs);
                    if (pss instanceof ParameterDisposer) {
                        ((ParameterDisposer) pss).cleanupParameters();
                    }
                }
            }
        });
    }
大家可以看到在该方法中返回了一个execute方法,我们先进入该方法中看下:
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;
        }

可以大致看到该方法调用了PreparedStatementCallback类中的doInPreparedStatement方法,在这个方法中我们传入了PreparedStatement ,并且调用了executeQuery方法执行sql语句返回ResultSet对象,然后调用我们前面说的RowMapperResultSetExtractor类中的extractData方法,返回最后封装的集合对象。
到这里我们就得到了我们最后想要的结果。

总结

讲了这么多,确实有些乱,不过大家仔细的看下程序执行的过程基本都能看懂,我总结下JdbcTemplate,大致如下:
① 该模板类的底层还是对jdbc的代码进行了封装,不过返回对象的时候需要我们自己进行封装,在这点上我觉得手动封装这块完全可以交给spring的ioc属性注入就可以实现。
② 该类的底层用到了一个关键类RowMapperResultSetExtractor,该类作用是创建list集合,并将查询到的对象加入该集合中返回;还有一个关键方法exec()方法,该方法执行jdbc代码返回ResultSet结果集。
③ 我们可以看到query()方法,调用了几次重载,第一次作用是创建RowMapper对象,第二次调用是PerparedStatement的sql参数注入,而最后一次调用是返回了结果集集合对象。这点开始我是有疑问的,为啥不一次性的调用query()方法直接执行以上三步呢?最后想了想,如果用一个方法实现,那么程序的复用性就很差,你不同的需求肯定会造成大量的代码重复,所以只能这样不断的拆分小功能,然后利用组合的方式实现我们所需要的功能,这种设计思想很重要,值得学习。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值