ai 上一节中,我们已经对JdbcDaoSupport和JdbcTemplate有了一定的了解。但是,我们只是初步的了解了JdbcTemplate,至此Spring也只是让我们更方便的获取连接。其实Spring提供了很多强大的功能,使得JdbcTemplate访问数据库,下面,让我们从来看看:
JdbcTemplate:
- public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {
- return execute(new SimplePreparedStatementCreator(sql), action);
- }
而在JdbcTemplate中有内部类如下:
- 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 sql;
- }
- }
SimplePreparedStatementCreator负责从sql String生成查询语句PreparedStatement对象;
- public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action)
- throws DataAccessException {
- Assert.notNull(psc, "PreparedStatementCreator must not be null");
- Assert.notNull(action, "Callback object must not be null");
-
- 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);
- }
-
- Object result = action.doInPreparedStatement(psToUse);
- handleWarnings(ps.getWarnings());
- return result;
- }
- catch (SQLException ex) {
-
-
- if (psc instanceof ParameterDisposer) {
- ((ParameterDisposer) psc).cleanupParameters();
- }
- String sql = getSql(psc);
- psc = null;
- JdbcUtils.closeStatement(ps);
- ps = null;
- DataSourceUtils.releaseConnection(con, getDataSource());
- con = null;
- throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);
- }
- finally {
- if (psc instanceof ParameterDisposer) {
- ((ParameterDisposer) psc).cleanupParameters();
- }
-
- JdbcUtils.closeStatement(ps);
- DataSourceUtils.releaseConnection(con, getDataSource());
- }
- }
查询关闭语句:
- public static void closeStatement(Statement stmt) {
- if (stmt != null) {
- try {
- stmt.close();
- }
- catch (SQLException ex) {
- logger.debug("Could not close JDBC Statement", ex);
- }
- catch (Throwable ex) {
-
- logger.debug("Unexpected exception on closing JDBC Statement", ex);
- }
- }
- }
执行查询回调方法,给开发者留下了扩展的接口,典型的模板方式的应用:
- public interface PreparedStatementCallback {
-
- Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException;
- }
接下来,我们可以见到具体的应用;
- public Object execute(ConnectionCallback action) throws DataAccessException
对应于:
- public interface ConnectionCallback {
-
- Object doInConnection(Connection con) throws SQLException, DataAccessException;
- }
- public Object execute(StatementCallback action) throws DataAccessException {
同理对应于:
- public interface StatementCallback {
-
- Object doInStatement(Statement stmt) throws SQLException, DataAccessException;
- }
以上,我们可以看见Spring位我们准备了三中对象层次的扩展点;
其实,我们利用Spring给我们提供的功能,Jdbc一样也能实现一些程度上的ORM,虽然,有限,但是却能让我们在保证数据库连接效率的同时,也能享受到oo的数据库访问机制;
- public Object query(PreparedStatementCreator psc, ResultSetExtractor rse) throws DataAccessException {
- return query(psc, null, rse);
- }
- public Object query(
- PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)
- throws DataAccessException {
- Assert.notNull(rse, "ResultSetExtractor must not be null");
- if (logger.isDebugEnabled()) {
- String sql = getSql(psc);
- logger.debug("Executing SQL query" + (sql != null ? " [" + sql + "]" : ""));
- }
-
- return execute(psc, new PreparedStatementCallback() {
- public Object 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();
- }
- }
- }
- });
- }
结果集的解压器接口如下:
- public interface ResultSetExtractor {
-
- Object extractData(ResultSet rs) throws SQLException, DataAccessException;
- }
如下:
- public class CoreyExtractor implements ResultSetExtractor {
- public Object extractData(ResultSet rs) throws SQLException,
- DataAccessException {
- List customers=new ArrayList();
- while(rs.next()){
- Person customer=new Person();
- customer.setName(rs.getString(1));
- customer.setAge(rs.getInt(2));
- customers.add(customer);
- }
- return customers;
- }
- }
这样,就能返回对象了;
同样,存在一下方法:
- public List query(String sql, Object[] args, RowMapper rowMapper)
- throws DataAccessException {
- return query(sql, args, new RowMapperResultReader(rowMapper));
- }
- public interface RowCallbackHandler {
-
-
-
- void processRow(ResultSet rs) throws SQLException;
- }
- public interface ResultReader extends RowCallbackHandler {
-
-
- List getResults();
- }
- public interface RowMapper {
-
-
-
- Object mapRow(ResultSet rs, int rowNum) throws SQLException;
- }
- public List query(String sql, Object[] args, int[] argTypes, RowMapper rowMapper)
- throws DataAccessException {
- return query(sql, args, argTypes, new RowMapperResultReader(rowMapper));
- }
- public List query(String sql, Object[] args, RowCallbackHandler rch)
- throws DataAccessException {
- return query(sql, new ArgPreparedStatementSetter(args), rch);
- }
- public List query(String sql, PreparedStatementSetter pss, final RowCallbackHandler rch)
- throws DataAccessException {
- return (List) query(sql, pss, new RowCallbackHandlerResultSetExtractor(rch));
- }
从对单行的处理,一直委托到给结果集解压器处理;而这个具体的解压器如下:
- private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor {
- private final RowCallbackHandler rch;
- public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch) {
- this.rch = rch;
- }
- public Object extractData(ResultSet rs) throws SQLException {
- //一行行的处理;实现了RowCallBackHandler
- while (rs.next()) {
- this.rch.processRow(rs);
- }
- //实现整个结果集的处理,实现了RowCallBackHandler的子接口ResultReader;
- if (this.rch instanceof ResultReader) {
- return ((ResultReader) this.rch).getResults();
- }
- else {
- return null;
- }
- }
- }
通过这样的一种机制,我们可以直接把结果集合转化成为对象数组;让我们可以像Hibernate一样,直接从数据库里面拿到对象;
从下面的类图结构,可以清晰的看见适配器模式的影子;
我们能够用jdbc从数据库里面直接查询对象,那么有人不禁想问,我们能不能想Hibernate一样,把对象直接更新到数据库里面去呢,答案是肯定的,下面,就像我们来看一下:
我们就要用到SqlUpdate:
经典的运用了模板模式;如下实现;
- public class CoreySqlUpdate extends SqlUpdate {
-
- public CoreySqlUpdate(DataSource ds){
- super(ds,"insert into Persons(id,name,age) values(?,?,?)");
- declareParameter(new SqlParameter(Types.INTEGER));
- declareParameter(new SqlParameter(Types.VARCHAR));
- declareParameter(new SqlParameter(Types.INTEGER));
- compile();
- }
-
- public int insertPerson(Person person){
- super.update(person.getId(),person.getName(),person.getAge());
- }
- }
我们再来看一下实现代码:
- public SqlUpdate(DataSource ds, String sql) {
- setDataSource(ds);
- setSql(sql);
- }
设置了数据源和执行sql代码;
- public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {
- if (isCompiled()) {
- throw new InvalidDataAccessApiUsageException("Cannot add parameters once the query is compiled");
- }
- this.declaredParameters.add(param);
- }
将参数类型注册到declaredParameters;
- public final void compile() throws InvalidDataAccessApiUsageException {
- if (!isCompiled()) {
- if (getSql() == null) {
- throw new InvalidDataAccessApiUsageException("Property 'sql' is required");
- }
- try {
- this.jdbcTemplate.afterPropertiesSet();
- }
- catch (IllegalArgumentException ex) {
- throw new InvalidDataAccessApiUsageException(ex.getMessage());
- }
-
- compileInternal();
- this.compiled = true;
- if (logger.isDebugEnabled()) {
- logger.debug("RdbmsOperation with SQL [" + getSql() + "] compiled");
- }
- }
- }
编译sql语句;
- protected final void compileInternal() {
-
- this.preparedStatementFactory = new PreparedStatementCreatorFactory(getSql(), getDeclaredParameters());
- this.preparedStatementFactory.setResultSetType(getResultSetType());
- this.preparedStatementFactory.setUpdatableResults(isUpdatableResults());
- this.preparedStatementFactory.setReturnGeneratedKeys(isReturnGeneratedKeys());
- if (getGeneratedKeysColumnNames() != null) {
- this.preparedStatementFactory.setGeneratedKeysColumnNames(getGeneratedKeysColumnNames());
- }
- this.preparedStatementFactory.setNativeJdbcExtractor(getJdbcTemplate().getNativeJdbcExtractor());
-
- onCompileInternal();
- }
-
- protected void onCompileInternal() {
- }
在执行更新的时候,首先进行数据验证:
- protected void validateParameters(Object[] parameters) throws InvalidDataAccessApiUsageException {
- checkCompiled();
- int declaredInParameters = 0;
- Iterator it = this.declaredParameters.iterator();
- while (it.hasNext()) {
- SqlParameter param = (SqlParameter) it.next();
- if (param.isInputValueProvided()) {
- if (!supportsLobParameters() &&
- (param.getSqlType() == Types.BLOB || param.getSqlType() == Types.CLOB)) {
- throw new InvalidDataAccessApiUsageException(
- "BLOB or CLOB parameters are not allowed for this kind of operation");
- }
- declaredInParameters++;
- }
- }
- validateParameterCount((parameters != null ? parameters.length : 0), declaredInParameters);
- }
在执行查询语句:
- int rowsAffected = getJdbcTemplate().update(newPreparedStatementCreator(params));
- protected final PreparedStatementCreator newPreparedStatementCreator(Object[] params) {
- return this.preparedStatementFactory.newPreparedStatementCreator(params);
- }
执行的查询语句是工厂生成的;
如此一来,我们就能够往数据库中直接插入对象,不过再次之前的对象与数据库之间的映射规则我们必须制定,就好比Hibernate的hbm.xml文件一样;
在这一小节中,体现了回调的实现是采用外部类的模板模式,面向接口编程,而我们可以提供具体的回调接口,比如缺省适配器等等,将扩展的点进一步专业细化,比如由PreparedStatementCallBack回调接口的接口方法中实现中,再次采用模板模式模式,我们可以实现ResultSet与对象的映射;
public Object query(final String sql, final
ResultSetExtractor rse) throws DataAccessException {
Assert.notNull(sql, "SQL must not be null");
Assert.notNull(rse, "ResultSetExtractor must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL query [" + sql + "]");
}
class QueryStatementCallback implements StatementCallback, SqlProvider {
public Object doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
ResultSet rsToUse = rs;
if (nativeJdbcExtractor != null) {
rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
}
return rse.extractData(rsToUse);
}
finally {
JdbcUtils.closeResultSet(rs);
}
}
public String getSql() {
return sql;
}
}
return execute(new QueryStatementCallback());
}