【Java】Spring JDBCTemplate 实现

  • Post author:
  • Post category:java


JDBCTemplate是使用模板设计模式的典范。

通过使用接口做一定的抽象,避免了jdbc的样板代码。

关于jdbc的样板代码这里就不做介绍了,大家应该都知道,这里直接看JDBCTemplate。

String sql = "select * from user";
        jdbcTemplate.query("", new Object[]{},new RowMapper<Object>() {
            @Override
            public Object mapRow(ResultSet resultSet, int i) throws SQLException {
                return null;
            }
        });

这里举一个query的例子,一般我们都是像上面这种方式来使用的,下面看下query的实现:

    public <T> List<T> query(String sql, @Nullable Object[] args, RowMapper<T> rowMapper) throws DataAccessException {
        return (List)result(this.query((String)sql, (Object[])args, (ResultSetExtractor)(new RowMapperResultSetExtractor(rowMapper))));
    }

这一步,将我们的rowMapper封装在一个Extractor里:

public class RowMapperResultSetExtractor<T> implements ResultSetExtractor<List<T>> {
    private final RowMapper<T> rowMapper;
    private final int rowsExpected;

    public RowMapperResultSetExtractor(RowMapper<T> rowMapper) {
        this(rowMapper, 0);
    }

    public RowMapperResultSetExtractor(RowMapper<T> rowMapper, int rowsExpected) {
        Assert.notNull(rowMapper, "RowMapper is required");
        this.rowMapper = rowMapper;
        this.rowsExpected = rowsExpected;
    }

    public List<T> extractData(ResultSet rs) throws SQLException {
        List<T> results = this.rowsExpected > 0 ? new ArrayList(this.rowsExpected) : new ArrayList();
        int var3 = 0;

        while(rs.next()) {
            results.add(this.rowMapper.mapRow(rs, var3++));
        }

        return results;
    }
}

这里有一个extractData方法,调用了传入的rowMapper来生成一个list。应该会在query里面被调用。

再往下看query方法:

    @Nullable
    public <T> T query(String sql, @Nullable Object[] args, ResultSetExtractor<T> rse) throws DataAccessException {
        return this.query(sql, this.newArgPreparedStatementSetter(args), rse);
    }

这里又将我们传入的sql参数封装在一个Setter里:

    protected PreparedStatementSetter newArgPreparedStatementSetter(@Nullable Object[] args) {
        return new ArgumentPreparedStatementSetter(args);
    }

public class ArgumentPreparedStatementSetter implements PreparedStatementSetter, ParameterDisposer {
    @Nullable
    private final Object[] args;

    public ArgumentPreparedStatementSetter(@Nullable Object[] args) {
        this.args = args;
    }

    public void setValues(PreparedStatement ps) throws SQLException {
        if (this.args != null) {
            for(int i = 0; i < this.args.length; ++i) {
                Object arg = this.args[i];
                this.doSetValue(ps, i + 1, arg);
            }
        }

    }

    protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {
        if (argValue instanceof SqlParameterValue) {
            SqlParameterValue paramValue = (SqlParameterValue)argValue;
            StatementCreatorUtils.setParameterValue(ps, parameterPosition, paramValue, paramValue.getValue());
        } else {
            StatementCreatorUtils.setParameterValue(ps, parameterPosition, -2147483648, argValue);
        }

    }

    public void cleanupParameters() {
        StatementCreatorUtils.cleanupParameters(this.args);
    }
}

其内部有一个setValues方法,入参是我们传入的sql语句,然后该方法内部会将参数写入sql语句中。

再往下看query:

    @Nullable
    public <T> T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
        return this.query((PreparedStatementCreator)(new JdbcTemplate.SimplePreparedStatementCreator(sql)), (PreparedStatementSetter)pss, (ResultSetExtractor)rse);
    }

这一步将传入的string的sql语句封装在一个creator里:

    private static class SimplePreparedStatementCreator implements PreparedStatementCreator, SqlProvider {
        private final String sql;

        public SimplePreparedStatementCreator(String sql) {
            Assert.notNull(sql, "SQL must not be null");
            this.sql = sql;
        }

        public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
            return con.prepareStatement(this.sql);
        }

        public String getSql() {
            return this.sql;
        }
    }

内部的createPrepareStatement方法就生成了原生的jdbc里的statment语句。

再往下:

 @Nullable
    public <T> T query(PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss, final ResultSetExtractor<T> rse) throws DataAccessException {
        Assert.notNull(rse, "ResultSetExtractor must not be null");
        this.logger.debug("Executing prepared SQL query");
        return this.execute(psc, new PreparedStatementCallback<T>() {
            @Nullable
            public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
                ResultSet rs = null;

                Object var3;
                try {
                    if (pss != null) {
                        pss.setValues(ps);
                    }

                    rs = ps.executeQuery();
                    var3 = rse.extractData(rs);
                } finally {
                    JdbcUtils.closeResultSet(rs);
                    if (pss instanceof ParameterDisposer) {
                        ((ParameterDisposer)pss).cleanupParameters();
                    }

                }

                return var3;
            }
        });
    }

这里使用了一个PreparedStatementCallback封装了之前的sql的creator,setter以及rowmapper,将他们组合在一起调用execute方法。该方法是一个偏内部的方法,虽然暴露给了外部,但是不应该直接调用,除非特殊需求。因为需要知道太多template内部实现了。

@Nullable
    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 (this.logger.isDebugEnabled()) {
            String sql = getSql(psc);
            this.logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
        }

        Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
        PreparedStatement ps = null;

        Object var13;
        try {
            ps = psc.createPreparedStatement(con);
            this.applyStatementSettings(ps);
            T result = action.doInPreparedStatement(ps);
            this.handleWarnings((Statement)ps);
            var13 = result;
        } catch (SQLException var10) {
            if (psc instanceof ParameterDisposer) {
                ((ParameterDisposer)psc).cleanupParameters();
            }

            String sql = getSql(psc);
            JdbcUtils.closeStatement(ps);
            ps = null;
            DataSourceUtils.releaseConnection(con, this.getDataSource());
            con = null;
            throw this.translateException("PreparedStatementCallback", sql, var10);
        } finally {
            if (psc instanceof ParameterDisposer) {
                ((ParameterDisposer)psc).cleanupParameters();
            }

            JdbcUtils.closeStatement(ps);
            DataSourceUtils.releaseConnection(con, this.getDataSource());
        }

        return var13;
    }

从这里大致可以看到template的模式:

将传入的sql生成一条jdbc的statment并且设置参数(如果需要的话);

执行;

将结果调用mapper转换。

除了queryForObject以外,其与方法都是返回一个list的。

execute方法封装了样板代码,这样只有这一个函数有样板代码,其他的函数都只负责自己的逻辑,包括生成sql,插入参数,结果转换等等。这些就是抽象部分,也是模板设计模式的体现。

query函数的api:

query:返回一个list,一般需要传入一个mapper。一般都用这个方法,可以定制结果转换。

queryForObject:返回一个object。两种用法,一种是传入一个class,通常用来返回一个标量值,比如count,sum这种,具体类型由传入的参数类型来指定,最终在底层实现上会由一个SingleMapper来转换结果,原理大概就是直接将返回的sql结果集转成传入的类型。

另一种也是传入一个mapper,可以直接返回一个POJO。

queryForList:其实是上面方法的list版本。



版权声明:本文为u010900754原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。