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的区别,我默认大家都明白。
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; }
/** * 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又是什么鬼?
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的呗
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的呢?
/** * 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; } }
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中处理过程是这样的
sql = "select description from dream where personid=?"; String description= jt.queryForObject(sql, new Object[] { 8 },String.class); System.out.println(description);上面的查询方式,只能查询出表中的 一条记录的一个字段
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; }另外 我得说明一下
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)