Spring JDBC Framework
引自 :学习经典:Spring JDBC Framework
这里记录我对Spring JDBC框架的学习。由于Spring JDBC和我之前做的工作有很多共同之处,学习经典Framework的设计,取长补短,为我所用。
在这里,先佩服一下Rod JohnSon,他对数据库,JDBC的理解非常深。看Spring jdbc框架的设计和源代码,带给了我很多以前没有想到的东西。
我们知道,Spring JDBC的主要目标是为了简化JDBC的编程,方便我们构建健壮的应用程序。这里,它的一个基本设计理念,就是将JDBC编程中变化的和不变化的分开。
在JDBC中,什么是变化的?毫无疑问,SQL语句是变化的。那什么是不变化的?正确的使用JDBC的方式是不变化的。
先看一段代码。
- public List getAvailableSeatlds(DataSource ds, int performanceld,
- int seatType) throws ApplicationException {
- String sql = "SELECT seat_id AS id FROM available_seats " +
- "WHERE performance_id = ? AND price_band_id = ?";
- List seatlds = new LinkedList();
- Connection con = null;
- PreparedStatement ps = null;
- ResultSet rs = null;
- try {
- con = ds.getConnection(); //1。建立Connection
- ps = con.prepareStatement(sql); //2。创建preparedStatement
- ps.setlnt(1, performanceld); //3。设置ps的参数
- ps.setlnt(2, seatType);
- rs = ps.executeQuery(); //4.执行查询
- while (rs.next()) { //5.解析ResultSet
- int seatld = rs.getlnt(1);
- seatlds.add(new Integer(seatld));
- }
- rs.close(); //6.关闭资源,做好善后工作。rs,ps,connection
- ps.close();
- }
- catch (SQLException ex) {
- throw new ApplicationException ("Couldn't run query [" + sql + "]", ex);
- }
- finally {
- try {
- if (con != null)
- con.close(); //如果没有连接池的话,不要轻易关。connection属于耗费资源:)
- }
- catch (SQLException ex) {
- // Log and ignore
- }
- }
- return seatlds;
- }
从上面看,什么是不变的。首先,咱们这个使用JDBC的方式是良好的,正确的,也是不变的,也就是workflow不变。其次,这里头的很多操作是不变的,比如说:关闭资源,处理异常。
什么是变的?设置PreparedStament的参数是变化的,利用PreparedStatement做什么是变化的。
还有什么是变的?取得Connection可能是变化的,我们可以从ConnectionPool中取,也可以裸从Database取。
还有什么是变的?在主工作流之外,还可以对PreparedStament设置一些属性。比如fetchSize等。
还有什么是变的?解析ResultSet是变的。但是可以抽象,都是从结果集中取得你想要的东西。
很好。经过分析,我们会自然而然的想到Template设计模式。用模板方法来描述我们的工作流。对于固定的操作,我们会把它建模为一些帮助类,利用这些类来完成固定操作,这些操作在Template方法中被调用。
对于哪些可以变的方法。我们也发现,其实它要实现的功能是一样的。抽象起来,我们可以用一些接口来描述这些功能。比如说数据库连接管理的功能。
设计取决于我们考虑问题的深度,以及我们对过程划分的粒度。
下面,我们阅读Spring JDBC Template的代码吧。好好享受一下。下面几个接口是对变化的部分进行建模:)
- public interface PreparedStatementCreator {
- PreparedStatement createPreparedStatement (Connection conn)
- throws SQLException;
- }
使用方法就是:
- PreparedStatementCreator psc = new PreparedStatementCreator() {
- public PreparedStatement createPreparedStatement (Connection conn)
- throws SQLException {
- PreparedStatement ps = conn. prepareStatement (
- "SELECT seat_id AS id FROM available_seats WHERE " +
- "performance_id = ? AND price_band_id = ?");
- ps.setInt(1, performanceId);
- ps.setInt(2, seatType);
- return ps;
- }
- };
- public interface PreparedStatementSetter {
- void setValues(PreparedStatement ps) throws SQLException;
- }
- public interface RowCallbackHandler {
- void processRow(ResultSet rs) throws SQLException;
- }
使用方式:
- RowCallbackHandler rch = new RowCallbackHandler() {
- public void processRow(ResultSet rs) throws SQLException {
- int seatId = rs.getInt(1) ;
- list.add(new Integer (seatId) );//典型的inner class的应用,list为外部类的变量。
- }
- };
- public interface ResultSetExtractor {
- Object extractData(ResultSet rs) throws SQLException, DataAccessException;
- }
下面是JdbcTemplate中提供的模板方法。该方法完成对数据库的查询:),看看和上面最初的代码有多少不同。这里除了取数据库连接没有之外,其它的操作都已经有了:),并且很健壮。
这个execute()方法非常关键。
- 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); //取得不变的SQL部分。
- 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);//就是给ps来设置参数用的。ps.setInt(1, 0);
- }
- rs = ps.executeQuery();//执行查询
- ResultSet rsToUse = rs;
- if (nativeJdbcExtractor != null) {
- rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
- }
- return rse.extractData(rsToUse); // ResultSetExtractor从ResultSet中将值取出来就OK了。
- }
- finally {
- //最后的善后工作还是需要做好的:) rs.close(),把ps的相关参数清除掉。
- JdbcUtils.closeResultSet(rs);
- if (pss instanceof ParameterDisposer) {
- ((ParameterDisposer) pss).cleanupParameters();
- }
- }
- }
- });
- }
Are you ready?看看execute()方法吧。
- 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);
- }
- //创建PreparedStatement
- ps = psc.createPreparedStatement(conToUse);
- applyStatementSettings(ps);//这个方法是设置ps的一些属性,我平时不用,Spring框架倒是考虑得相当全的说。
- PreparedStatement psToUse = ps;
- if (this.nativeJdbcExtractor != null) {
- psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);
- }
- //调用Callback来完成PreparedStatement的设值。就是调用上面的doInPreparedStatement来使用ps。
- Object result = action.doInPreparedStatement(psToUse);
- SQLWarning warning = ps.getWarnings();
- throwExceptionOnWarningIfNotIgnoringWarnings(warning);
- return result;
- }
- //如果有错误的话,那么就开始ps.close(), connection.close();
- 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.close();
- ps = null;
- DataSourceUtils.releaseConnection(con, getDataSource()); /
- con = null;
- throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);
- }
- //不管怎么
样,ps.close(), Connection.close()吧,当然这里是releaseConnection。在我的程序
中,Connection只有一个,没有ConnectionPool,当然不会去close Connection。一般来讲,如果没有
Connection的线程池的话,我们肯定也不会经常的关闭Connection,得到Connection。毕竟这个东西非常耗费资源。 - finally {
- if (psc instanceof ParameterDisposer) {
- ((ParameterDisposer) psc).cleanupParameters();
- }
- JdbcUtils.closeStatement(ps);
- DataSourceUtils.releaseConnection(con, getDataSource());
- }
- }
JdbcTemplate完成了负责的操作,客户只需要调用query()就可以完成查询操作了。当然,JdbcTemplate会实现很多带其它参数的方法,以方便你的使用。Template设计模式被发扬广大了,我自己的程序中也主要是利用了Template。
继续看看DataSourceUtils:这个专门用于管理数据库Connection的类。
- public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
- try {
- return doGetConnection(dataSource);
- ~~~~~~ //这个方法很舒服,Spring Framework中到处有这样的方法。为什么要委派到这个动作方法?
- }
- catch (SQLException ex) {
- throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
- }
- }
这里的doGetConnection就稍微复杂一点了。但是如果没有事务同步管理器的话,那就比较简单。
只是在Connection上多了一个ConnecionHolder类用于持有Connection,实现ConnectionPool的一点小功能。
- public static Connection doGetConnection(DataSource dataSource) throws SQLException {
- Assert.notNull(dataSource, "No DataSource specified");
- ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
- ~~~~~//Connection的持有器。通过持有器得到Connection。
- if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
- conHolder.requested();
- if (!conHolder.hasConnection()) {
- logger.debug("Fetching resumed JDBC Connection from DataSource");
- conHolder.setConnection(dataSource.getConnection());
- }
- return conHolder.getConnection();
- }
- // Else we either got no holder or an empty thread-bound holder here.
- logger.debug("Fetching JDBC Connection from DataSource");
- Connection con = dataSource.getConnection();
- ……
- return con;
- }
- public class ConnectionHolder extends ResourceHolderSupport {
- private Connection currentConnection; //当前的Connection
- private ConnectionHandle connectionHandle; //Connection的处理器,因此可以通过该类完成对connection的管理。
- public ConnectionHolder(Connection connection) {
- this.connectionHandle = new SimpleConnectionHandle(connection);
- }
- public ConnectionHolder(ConnectionHandle connectionHandle) {
- Assert.notNull(connectionHandle, "ConnectionHandle must not be null");
- this.connectionHandle = connectionHandle;
- }
- public Connection getConnection() {
- Assert.notNull(this.connectionHandle, "Active Connection is required");
- if (this.currentConnection == null) {
- this.currentConnection = this.connectionHandle.getConnection();
- }
- return this.currentConnection;
- }
- public void released() {
- super.released();
- if (this.currentConnection != null) {
- this.connectionHandle.releaseConnection(this.currentConnection);
- this.currentConnection = null;
- }
- }
- public interface ConnectionHandle {
- /**
- * Fetch the JDBC Connection that this handle refers to.
- */
- Connection getConnection();
- /**
- * Release the JDBC Connection that this handle refers to.
- * @param con the JDBC Connection to release
- */
- void releaseConnection(Connection con);
- }
最后看一下SimpleConnectionHandle,这个ConnectionHandle的简单实现类。就只有一个Connection可管理。如果有多个Connection可管理的话,这里就是ConnectionPool了:)
- public class SimpleConnectionHandle implements ConnectionHandle {
- private final Connection connection;
- /**
- * Create a new SimpleConnectionHandle for the given Connection.
- * @param connection the JDBC Connection
- */
- public SimpleConnectionHandle(Connection connection) {
- Assert.notNull(connection, "Connection must not be null");
- this.connection = connection;
- }
- /**
- * Return the specified Connection as-is.
- */
- public Connection getConnection() {
- return connection;
- }
- /**
- * This implementation is empty, as we're using a standard
- * Connection handle that does not have to be released.
- */
- public void releaseConnection(Connection con) {
- }
- public String toString() {
- return "SimpleConnectionHandle: " + this.connection;
- }
- }
一路下来,真是很爽。Spring JDBC Framework真的可谓是深耕细作,这里只是管中窥豹了。类的职责设计得非常清除,同时有良好的设计模式支持,同时提供良好的编程接口,用户基本上只需要了结JdbcTemplate的API就可以了。厉害,厉害。
Spring JDBC Framework的更多相关文章
- Spring JDBC Framework详解——批量JDBC操作、ORM映射
转自:https://blog.csdn.net/yuyulover/article/details/5826948 一.spring JDBC 概述 Spring 提供了一个强有力的模板类JdbcT ...
- spring jdbc 查询结果返回对象、对象列表
首先,需要了解spring jdbc查询时,有三种回调方式来处理查询的结果集.可以参考 使用spring的JdbcTemplate进行查询的三种回调方式的比较,写得还不错. 1.返回对象(queryF ...
- spring jdbc获取插入记录的主键id
在JDBC3.0规范中,当新增记录时,允许将数据库自动产生的主键值绑定到Statement或PreparedStatement中.使用Statement时,可以通过以下方法绑定主键值: int exe ...
- Spring JDBC实现查询
1 db.properties jdbc.user=root jdbc.password=920614 jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbc ...
- Spring JDBC
转载:博客主页:http://blog.csdn.NET/chszs 一.概述 在Spring JDBC模块中,所有的类可以被分到四个单独的包:1)core即核心包,它包含了JDBC的核心功能.此包内 ...
- Spring学习进阶(四) Spring JDBC
Spring JDBC是Spring所提供的持久层技术.主要目的是降低使用JDBC API的门槛,以一种更直接,更简洁的方式使用JDBC API.在Spring JDBC里用户仅需要做哪些比不可少的事 ...
- Spring JDBC常用方法详细示例
Spring JDBC使用简单,代码简洁明了,非常适合快速开发的小型项目.下面对开发中常用的增删改查等方法逐一示例说明使用方法 1 环境准备 启动MySQL, 创建一个名为test的数据库 创建Mav ...
- Spring JDBC 访问MSSQL
在Spring中对底层的JDBC做了浅层的封装即JdbcTemplate,在访问数据库的DAO层完全可以使用JdbcTemplate完成任何数据访问的操作,接下来我们重点说说Spring JDBC对S ...
- Spring JDBC主从数据库配置
通过昨天学习的自定义配置注释的知识,探索了解一下web主从数据库的配置: 背景:主从数据库:主要是数据上的读写分离: 数据库的读写分离的好处? 1. 将读操作和写操作分离到不同的数据库上,避免主服务器 ...
随机推荐
- App测试从入门到精通之交叉事件测试
交叉事件测试又叫事件或者叫冲突测试.对于正在运行的应用,若进入短信,电话等其他软件响应的情况,不会影响所测试应用,且会保证应用都能正确运行.下面我来看一下关于交叉测试中,我们测试人员需要考虑的一些测试 ...
- 编写高质量代码改善C#程序的157个建议——建议53:必要时应将不再使用的对象引用赋值为null
建议53:必要时应将不再使用的对象引用赋值为null 在CLR托管的应用程序中,存在一个“根”的概念,类型的静态字段.方法参数.以及局部变量都可以作为“根”的存在(值类型不能作为“根”,只有引用类型的 ...
- 如何快速搭建基于python+appium的自动化测试环境
首先申明本文是基本于Python与Android来快速搭建Appium自动化测试环境: 主要分为以下几个步骤: 前提条件: 1)安装与配置python环境,打开 Python官网,找到“Downloa ...
- tornado设置cookie过期时间(expires time)
具体的tornado设置过期时间的东西, 我也是查资料才发现的, 现在就贴代码吧 用户登录之后, 设置cookie, 我使用set_secure_cookie的, 它默认是有个30天的过期时间, 导致 ...
- Android Bundle传递数据
1.传递普通数据 Intent intent=new Intent(MainActivity.this,TwoActivity.class); Bundle bundle=new Bundle(); ...
- python 日志模块工具类
#!/usr/bin/env python # -*- coding: utf-8 -*- import logging # logName 日志中的某个格式化的字段名,logFile生成的日志文件名 ...
- C#在线运行--cmd方法
此次C#在线运行采用cmd.exe用csc对文件进行编译,然后再运行的思路实现在线运行的效果.不过会生成二个文件(.cs和.exe),可能需要定期清除临时文件夹. 首先利用时间戳生成唯一文件名, ...
- 模拟Springboot二:内置tomcat
既然要将tomcat内置到项目中,并且能够成功的启动项目就要知道 tomcat 做了哪些事情 ,那么就必须先搞明白 一个 普通的web项目是如何被我们本地配置的tomcat启动并运行的 (1). 先 ...
- OC语言自定义打印
1.为了全文通用,选择在PCH文件中写: // // 版权所有:Copyright © 2018年 Lelight. All rights reserved. // 创 建 者: Lelight // ...
- JAVA学习必须掌握的框架,不看后悔
Web应用,最常见的研发语言是Java和PHP. 后端服务,最常见的研发语言是Java和C/C++. 大数据,最常见的研发语言是Java和Python. 可以说,Java是现阶段中国互联网公司中,覆盖 ...