0
点赞
收藏
分享

微信扫一扫

JdbcTemplate源码解析



先写一个测试代码

package jdbc;


import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;


public class JDBCTest {
public static void main(String[] args) {
//加载spring
ClassPathResource resource = new ClassPathResource(
"applicationContext-common2.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader bdf = new XmlBeanDefinitionReader(factory);
bdf.loadBeanDefinitions(resource);


//获得jdbctemplate
JdbcTemplate jt = (JdbcTemplate) factory.getBean("jdbcTemplate");


String sql = "select * from dream where personid=?";
final List<Map<String, String>> list = new ArrayList<Map<String, String>>(); // 一定要用final定义
Object[] params = new Object[] { 1 };

//调用query方法
//注意 query有三个参数 sql params还有一个RowCallbackHandler
jt.query(sql, params, new RowCallbackHandler() {


@Override
public void processRow(ResultSet rs) throws SQLException {
Map<String, String> u = new HashMap<String, String>();
u.put("id", rs.getString("id"));
u.put("description", rs.getString("description"));
list.add(u);


}
});

for ( Map<String, String> u :list) {
System.out.println(u.get("description"));
}
//HERE IS A SIMPLE EXAMPLE
//EXAMPLE EXAMPLE


}
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>

前期构造方法的调用

我们能追踪到JDBCTemplate的query方法,如下:


//JDBCTemplate.java
public void query(String sql, Object[] args, RowCallbackHandler rch) throws DataAccessException {
query(sql, newArgPreparedStatementSetter(args), rch);
}

关于statement与preparedstatement的区别,我默认大家都明白。


算了还是写出来吧


String name="董磊峰2";  


int id=18;  


String sql="insert into admininfo (Aname,Aid) values(?,?)";  


PreparedStatement st=conn.prepareStatement(sql);  


st.setString(1, name);         //给第一个栏位注入String型的数值name  这个不是从0开始  


st.setInt(2, id);  


st.execute(); 


我们使用sql生成preparedstatement,再把参数传递进去。


newArgPreparedStatementSetter方法返回了一个ArgPreparedStatementSetter类,而ArgPreparedStatementSetter方法实现了PreparedStatementSetter


public interface PreparedStatementSetter {


/**
* Set parameter values on the given PreparedStatement.
* @param ps the PreparedStatement to invoke setter methods on
* @throws SQLException if a SQLException is encountered
* (i.e. there is no need to catch SQLException)
*/
void setValues(PreparedStatement ps) throws SQLException;
}


很清楚,PreparedStatementSetter就是写参数的嘛


干的事情就类似于上面的PreparedStatement的setString,setInt


//JDBCTemplate.java


public void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch) throws DataAccessException {


query(sql, pss, new RowCallbackHandlerResultSetExtractor(rch));


}


这个RowCallbackHandlerResultSetExtractor是个干什么的?大家看看它的构造方法包含我们在前面的RowCallbackHandler。


猜也能猜出来,这个是对最后处理逻辑的调用。


举个最简单的例子,我们自己写的RowCallbackHandler的参数是一个ResultSet,等到循环处理多条数据的时候,就得循环的调用processRow,那么在哪里调用呢?


在RowCallbackHandlerResultSetExtractor里



/**
* Adapter to enable use of a RowCallbackHandler inside a ResultSetExtractor.
* <p>Uses a regular ResultSet, so we have to be careful when using it:
* We don't use it for navigating since this could lead to unpredictable consequences.
*/
private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor<Object> {


private final RowCallbackHandler rch;


public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch) {
this.rch = rch;
}


public Object extractData(ResultSet rs) throws SQLException {
while (rs.next()) {
this.rch.processRow(rs);
}
return null;
}
}



继续往下看


public <T> T query(String sql, PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
return query(new SimplePreparedStatementCreator(sql), pss, rse);
}

SimplePreparedStatementCreator又是什么鬼?


SimplePreparedStatementCreator实现了PreparedStatementCreator接口


public interface PreparedStatementCreator {


/**
* Create a statement in this connection. Allows implementations to use
* PreparedStatements. The JdbcTemplate will close the created statement.
* @param con Connection to use to create statement
* @return a prepared statement
* @throws SQLException there is no need to catch SQLExceptions
* that may be thrown in the implementation of this method.
* The JdbcTemplate class will handle them.
*/
PreparedStatement createPreparedStatement(Connection con) throws SQLException;
}

现在明白了吧,SimplePreparedStatementCreator就是生成PreparedStatement的呗




说到这,我有几个不是问题的问题


正常的使用PreparedStatement的顺序是


先生成PreparedStatement,然后注入参数,最后处理结果


怎么JDBCTemplate几个query的调用顺序是先生成ArgPreparedStatementSetter然后再生成SimplePreparedStatementCreator?


反过来不是更符合大家的思考顺序吗?




最终的处理


下面就是最终的大boss了:


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");


//注意匿名类
//execute一共两个参数 一个是PreparedStatementCreator 一个是PreparedStatementCallback
//***************3
return execute(psc, new PreparedStatementCallback<T>() {
public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
ResultSet rs = null;
try {
if (pss != null) {
pss.setValues(ps);
}
rs = ps.executeQuery(); //最最核心的一步 调用jdk的接口
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 {

//检测参数不为null

Connection con = DataSourceUtils.getConnection(getDataSource());
PreparedStatement ps = null;
try {
Connection conToUse = con;


//说实话 下面的代码 我也不知道是干什么的
//不过我在测试的时候
//nativeJdbcExtractor为null
//我们暂且不管这块
if (this.nativeJdbcExtractor != null &&
this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}


//*******1 获得PreparedStatement
ps = psc.createPreparedStatement(conToUse);
//这里是给PreparedStatement设置一些参数 基本不怎么用 不用深究
applyStatementSettings(ps);
PreparedStatement psToUse = ps;

//跟上面的conToUse一样 暂且不管
if (this.nativeJdbcExtractor != null) {
psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);
}
//************2 回调方法 我们得回到代码3处
T result = action.doInPreparedStatement(psToUse);
handleWarnings(ps);
return result;
}
catch (SQLException ex) {
//
}
finally {
//
}
}

那代码1处具体是怎么获得PreparedStatement的呢?


先别看源码,我们自己使用PreparedStatement的时候是什么生成的呢?


PreparedStatement st=conn.prepareStatement(sql); 


所以猜一下都能猜出来的,代码1处的psc的默认实现是SimplePreparedStatementCreator


/**
* Simple adapter for PreparedStatementCreator, allowing to use a plain SQL statement.
*/
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;
}
}



OK,代码1结束后,我们已经有了不带参数的PreparedStatementCreator




代码2处的action.doInPreparedStatement(psToUse);


这就是回调,我们得去前面看



new PreparedStatementCallback<T>() {
public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
ResultSet rs = null;
try {
if (pss != null) {
pss.setValues(ps); //给PreparedStatement 插入值
}
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();
}
}
}
}



总结

String sql = "select * from dream  where personid=?";
final List<Map<String, String>> list = new ArrayList<Map<String, String>>(); // 一定要用final定义
Object[] params = new Object[] { 1 };
jt.query(sql, params, new RowCallbackHandler() {


@Override
public void processRow(ResultSet rs) throws SQLException {
Map<String, String> u = new HashMap<String, String>();
u.put("id", rs.getString("id"));
u.put("description", rs.getString("description"));
list.add(u);


}
});

类似上面的调用方式,在Spring的JDBCTemplate中处理过程是这样的


1 生成给PreparedStatement注入参数的类---ArgPreparedStatementSetter
2 生成最后处理查询结果的RowCallbackHandlerResultSetExtractor,它内部循环调用了上面RowCallbackHandler的processRow方法
3 new一个生成PreparedStatement的类SimplePreparedStatementCreator
4 获得connection并通过SimplePreparedStatementCreator生成PreparedStatement
5 使用回调,处理PreparedStatement
6 向PreparedStatement中插入值
7 rs = ps.executeQuery();
8 调用RowCallbackHandlerResultSetExtractor的extractData方法,里面循环调用上面RowCallbackHandler的processRow方法



那么如果是下面的调用方式呢


sql = "select description from dream  where personid=?";
String description= jt.queryForObject(sql, new Object[] { 8 },String.class);
System.out.println(description);

上面的查询方式,只能查询出表中的

一条记录的一个字段

1 会把String.class包装成一个SingleColumnRowMapper


2 根据SingleColumnRowMapper生成RowMapperResultSetExtractor,这个RowMapperResultSetExtractor与上面的RowCallbackHandlerResultSetExtractor是兄弟,他们都实现了ResultSetExtractor接口。


3 在上面的第8步,这里是调用RowMapperResultSetExtractor的extractData方法


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;
}

另外 我得说明一下


queryForObject有两个,我们刚才使用的是第二个,只能查一行记录的一个字段


但是第一个queryForObject可以查到一条数据里的所有字段,并返还一个map。


public <T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException {
List<T> results = query(sql, rowMapper);
return DataAccessUtils.requiredSingleResult(results);
}


public <T> T queryForObject(String sql, Class<T> requiredType) throws DataAccessException {
return queryForObject(sql, getSingleColumnRowMapper(requiredType));
}

至于别的queryForList,跳到第一个queryForObject里。queryForObject(String sql, RowMapper<T> rowMapper)


queryForInt会跳到第二个queryForObject。 queryForObject(String sql, Class<T> requiredType)




我们看第二个queryForObject,它传递的是SingleColumnRowMapper


queryForList最后传递的是ColumnMapRowMapper,它与SingleColumnRowMapper是兄弟,都实现而了RowMapper接口。






举报

相关推荐

0 条评论