本文主要是介绍Spring源代码分析(12)---JdbcTemplate(ORM我也能行),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
ai 上一节中,我们已经对JdbcDaoSupport和JdbcTemplate有了一定的了解。但是,我们只是初步的了解了JdbcTemplate,至此Spring也只是让我们更方便的获取连接。其实Spring提供了很多强大的功能,使得JdbcTemplate访问数据库,下面,让我们从来看看:JdbcTemplate:
- public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {
- return execute(new SimplePreparedStatementCreator(sql), action);
- }
- 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;
- }
- }
- 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;
- //利用JdbcTemplate的nativeJdbcExtractor的属性,将这个从数据源得到的数据库连接
- //(有可能是代理类,而不是原生态的数据库连接)中抽取出原生的数据库连接对象
- 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) {
- //同样,用本地JDBC抽取出原生的查询语句;
- psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);
- }
- //执行该查询语句,并且进行处理;
- Object result = action.doInPreparedStatement(psToUse);
- handleWarnings(ps.getWarnings());
- return result;
- }
- catch (SQLException ex) {
- // Release Connection early, to avoid potential connection pool deadlock
- // in the case when the exception translator hasn't been initialized yet.
- 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) {
- // We don't trust the JDBC driver: It might throw RuntimeException or Error.
- 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 {
- //用户可以自定义connection操作;
- Object doInConnection(Connection con) throws SQLException, DataAccessException;
- }
- public Object execute(StatementCallback action) throws DataAccessException {
- public interface StatementCallback {
- //扩展statement操作;
- Object doInStatement(Statement stmt) throws SQLException, DataAccessException;
- }
其实,我们利用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 + "]" : ""));
- }
- //利用了上面介绍的扩展点之一,查询到了结果集合,并且用ResultSetExtractor对结果的集合进行了处理;
- 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<Person> customers=new ArrayList()<Person>;
- 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 {
- /*
- 处理单行。
- person.setName(rs.getString(1));
- person.setAge(rs.getInt(2));
- return person;
- */
- void processRow(ResultSet rs) throws SQLException;
- }
- public interface ResultReader extends RowCallbackHandler {
- //再此基础上可以实现处理多行;
- List getResults();
- }
- public interface RowMapper {
- //实现行对应;并且行与行之间可以进行判断;
- //这也是他比单纯的RowCallBackHandler好的地方;
- 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;
- }
- }
- }
从下面的类图结构,可以清晰的看见适配器模式的影子;
我们能够用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);
- }
- public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {
- if (isCompiled()) {
- throw new InvalidDataAccessApiUsageException("Cannot add parameters once the query is compiled");
- }
- this.declaredParameters.add(param);
- }
- 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");
- }
- }
- }
- 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();
- }
- /**
- * Hook method that subclasses may override to post-process compilation.
- * This implementation does nothing.
- * @see #compileInternal
- */
- 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());
}
这篇关于Spring源代码分析(12)---JdbcTemplate(ORM我也能行)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!