`

模板方法和Callback回调应用实践-自己动手写JdbcTemplate(附源码)

阅读更多
    最近一直在研读spring源码和学习设计模式,想把自己的一些领悟与大家分享,前几天发了几篇简单的文章,可能由于文字过于简单,几次被评为新手贴,心中滴汗啊  没办法,工作太忙,大家都知道,写篇文章是要很大精力地~~~~~

今天恰有时间,把这两天的学习所得与大家分享,尽量写得详细一些,专家饶路走,新手觉得好赞一下(不要拍砖哦~~~~)。
文章源码在附件中

注:本文目的意不在“重复发明轮子”,而是借此文来探讨Spring JdbcTemplate的内部实现原理,掌握其运用精妙之处,以便在以后的“造轮运动”中能灵活运用

话回正转,这两天在读spring的jdbc模板,对Spring源码的精妙真是佩服得五体投地,极为经典。
spring中真是集设计模式之大成,而且用得是炉火纯青。模板方法(template method)就在spring中被大量使用,如:jdbcTemplate,hibernateTemplate,JndiTemplate以及一些包围的包装等都无疑使用了模板模式,但spring并不是单纯使用了模板方法,而是在此基础上做了创新,配合callback(回调)一起使用,用得极其灵活。

OK,为了防止文章再被拍砖,我写得更详细点吧,我们首先来回顾一下模板模式:
所谓模板板式,就是在父类中定义算法的主要流程,而把一些个性化的步骤延迟到子类中去实现,父类始终控制着整个流程的主动权,子类只是辅助父类实现某些可定制的步骤。

有些抽象???
好吧,我们用代码来说话吧:
首先,父类要是个抽象类:
public abstract class TemplatePattern {

	//模板方法
	public final void templateMethod(){
		
		method1();
		method2();//勾子方法
		method3();//抽象方法
	}
	private void method1(){
		System.out.println("父类实现业务逻辑");
	}
	public void method2(){
		System.out.println("父类默认实现,子类可覆盖");
	}
	protected abstract void method3();//子类负责实现业务逻辑
}


父类中有三个方法,分别是method1(),method2()和method3()。
method1()是私有方法,有且只能由父类实现逻辑,由于方法是private的,所以只能父类调用。
method2()是所谓的勾子方法。父类提供默认实现,如果子类觉得有必要定制,则可以覆盖父类的默认实现。
method3()是子类必须实现的方法,即制定的步骤。
由此可看出,算法的流程执行顺序是由父类掌控的,子类只能配合。

下面我们来写第一个子类:
public class TemplatePatternImpl extends TemplatePattern {

	@Override
	protected void method3() {
		System.out.println("method3()在子类TemplatePatternImpl中实现了!!");

	}

}

这个子类只覆盖了必须覆盖的方法,我们来测试一下:
TemplatePattern t1 = new TemplatePatternImpl();
t1.templateMethod();

在控制台中我们可以看到:
父类实现业务逻辑
父类默认实现,子类可覆盖
method3()在子类TemplatePatternImpl中实现了!!


OK,我们来看看勾子方法的使用:
定义第2个子类,实现勾子方法:
public class TemplatePatternImpl2 extends TemplatePattern {

	@Override
	protected void method3() {
		System.out.println("method3()在子类TemplatePatternImpl2中实现了!!");

	}

	/* (non-Javadoc)
	 * @see com.jak.pattern.template.example.TemplatePattern#method2()
	 */
	@Override
	public void method2() {
		System.out.println("子类TemplatePatternImpl2覆盖了父类的method2()方法!!");
	}
	
}


来测试一下:
TemplatePattern t2 = new TemplatePatternImpl2();
t2.templateMethod();

我们看控制台:
父类实现业务逻辑
子类TemplatePatternImpl2覆盖了父类的method2()方法!!
method3()在子类TemplatePatternImpl2中实现了!!


OK,经典的模板模式回顾完了(大家不要拍砖哦~~~~~~~~~~)

接下来,我们回到正题,自己模仿spring动手写一个基于模板模式和回调的jdbcTemplate。

回顾一下,spring为什么要封装JDBC API,对外提供jdbcTemplate呢(不要仍鸡蛋啊¥·%¥#%)
话说SUN的JDBC API也算是经典了,曾经在某个年代折服了一批人。但随着历史的发展,纯粹的JDBC API已经过于底层,而且不易控制,由开发人员直接接触JDBC API,会造成不可预知的风险。还有,数据连接缓存池的发展,也不可能让开发人员去手工获取JDBC了。

好了,我们来看一段曾经堪称经典的JDBC API代码吧:
	public List<User> query() {

		List<User> userList = new ArrayList<User>();
		String sql = "select * from User";

		Connection con = null;
		PreparedStatement pst = null;
		ResultSet rs = null;
		try {
			con = HsqldbUtil.getConnection();
			pst = con.prepareStatement(sql);
			rs = pst.executeQuery();

			User user = null;
			while (rs.next()) {

				user = new User();
				user.setId(rs.getInt("id"));
				user.setUserName(rs.getString("user_name"));
				user.setBirth(rs.getDate("birth"));
				user.setCreateDate(rs.getDate("create_date"));
				userList.add(user);
			}


		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			if(rs != null){
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			try {
				pst.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(!con.isClosed()){
					try {
						con.close();
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			
		}
		return userList;
	}


上面的代码要若干年前可能是一段十分经典的,还可能被作为example被推广。但时过境迁,倘若哪位程序员现在再在自己的程序中出现以上代码,不是说明该公司的开发框架管理混乱,就说明这位程序员水平太“高”了。
我们试想,一个简单的查询,就要做这么一大堆事情,而且还要处理异常,我们不防来梳理一下:
1、获取connection
2、获取statement
3、获取resultset
4、遍历resultset并封装成集合
5、依次关闭connection,statement,resultset,而且还要考虑各种异常
6、.....
啊~~~~ 我快要晕了,在面向对象编程的年代里,这样的代码简直不能上人容忍。试想,上面我们只是做了一张表的查询,如果我们要做第2张表,第3张表呢,又是一堆重复的代码:
1、获取connection
2、获取statement
3、获取resultset
4、遍历resultset并封装成集合
5、依次关闭connection,statement,resultset,而且还要考虑各种异常
6、.....

这时候,使用模板模式的时机到了!!!

通过观察我们发现上面步骤中大多数都是重复的,可复用的,只有在遍历ResultSet并封装成集合的这一步骤是可定制的,因为每张表都映射不同的java bean。这部分代码是没有办法复用的,只能定制。那就让我们用一个抽象的父类把它们封装一下吧:
public abstract class JdbcTemplate {

	//template method
	public final Object execute(String sql) throws SQLException{
		
		Connection con = HsqldbUtil.getConnection();
		Statement stmt = null;
		try {
 
			stmt = con.createStatement();
			ResultSet rs = stmt.executeQuery(sql);
			Object result = doInStatement(rs);//abstract method 
			return result;
		}
		catch (SQLException ex) {
			 ex.printStackTrace();
			 throw ex;
		}
		finally {
 
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(!con.isClosed()){
					try {
						con.close();
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			
		}
	}
	
	//implements in subclass
	protected abstract Object doInStatement(ResultSet rs);
}

在上面这个抽象类中,封装了SUN JDBC API的主要流程,而遍历ResultSet这一步骤则放到抽象方法doInStatement()中,由子类负责实现。
好,我们来定义一个子类,并继承上面的父类:
public class JdbcTemplateUserImpl extends JdbcTemplate {

	@Override
	protected Object doInStatement(ResultSet rs) {
		List<User> userList = new ArrayList<User>();
		
		try {
			User user = null;
			while (rs.next()) {

				user = new User();
				user.setId(rs.getInt("id"));
				user.setUserName(rs.getString("user_name"));
				user.setBirth(rs.getDate("birth"));
				user.setCreateDate(rs.getDate("create_date"));
				userList.add(user);
			}
			return userList;
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}

}

由代码可见,我们在doInStatement()方法中,对ResultSet进行了遍历,最后并返回。
有人可能要问:我如何获取ResultSet 并传给doInStatement()方法啊??呵呵,问这个问题的大多是新手。因为此方法不是由子类调用的,而是由父类调用,并把ResultSet传递给子类的。我们来看一下测试代码:
		String sql = "select * from User";
		JdbcTemplate jt = new JdbcTemplateUserImpl();
		List<User> userList = (List<User>) jt.execute(sql);


就是这么简单!!

文章至此仿佛告一段落,莫急!不防让我们更深入一些...

试想,如果我每次用jdbcTemplate时,都要继承一下上面的父类,是不是有些不方面呢?
那就让我们甩掉abstract这顶帽子吧,这时,就该callback(回调)上场了


所谓回调,就是方法参数中传递一个接口,父类在调用此方法时,必须调用方法中传递的接口的实现类。

那我们就来把上面的代码改造一下,改用回调实现吧:

首先,我们来定义一个回调接口:
public interface StatementCallback {
	Object doInStatement(Statement stmt) throws SQLException;
}


这时候,我们就要方法的签名改一下了:
private final Object execute(StatementCallback action) throws SQLException


里面的获取数据方式也要做如下修改:
Object result = action.doInStatement(stmt);//abstract method 


为了看着顺眼,我们来给他封装一层吧:
	public Object query(StatementCallback stmt) throws SQLException{
		return execute(stmt);
	}


OK,大功告成!
我们来写一个测试类Test.java测试一下吧:
这时候,访问有两种方式,一种是内部类的方式,一种是匿名方式

先来看看内部类的方式:
//内部类方式
	public Object query(final String sql) throws SQLException {
		class QueryStatementCallback implements StatementCallback {

			public Object doInStatement(Statement stmt) throws SQLException {
				ResultSet rs = stmt.executeQuery(sql);
				List<User> userList = new ArrayList<User>();

				User user = null;
				while (rs.next()) {

					user = new User();
					user.setId(rs.getInt("id"));
					user.setUserName(rs.getString("user_name"));
					user.setBirth(rs.getDate("birth"));
					user.setCreateDate(rs.getDate("create_date"));
					userList.add(user);
				}
				return userList;

			}

		}

		JdbcTemplate jt = new JdbcTemplate();
		return jt.query(new QueryStatementCallback());
	}


在调用jdbcTemplate.query()方法时,传一个StatementCallBack()的实例过去,也就是我们的内部类。

再来看看匿名方式:
//匿名类方式
	public Object query2(final String sql) throws Exception{
		
		JdbcTemplate jt = new JdbcTemplate();
		return jt.query(new StatementCallback() {
			
			public Object doInStatement(Statement stmt) throws SQLException {
				ResultSet rs = stmt.executeQuery(sql);
				List<User> userList = new ArrayList<User>();

				User user = null;
				while (rs.next()) {

					user = new User();
					user.setId(rs.getInt("id"));
					user.setUserName(rs.getString("user_name"));
					user.setBirth(rs.getDate("birth"));
					user.setCreateDate(rs.getDate("create_date"));
					userList.add(user);
				}
				return userList;

			}
		});
		
	}

相比之下,这种方法更为简洁。
为什么spring不用传统的模板方法,而加之以Callback进行配合呢?
试想,如果父类中有10个抽象方法,而继承它的所有子类则要将这10个抽象方法全部实现,子类显得非常臃肿。而有时候某个子类只需要定制父类中的某一个方法该怎么办呢?这个时候就要用到Callback回调了。

离spring jdbcTemplate再近一点
上面这种方式基本上实现了模板方法+回调模式。但离spring的jdbcTemplate还有些距离。
我们可以再深入一些。。。

我们上面虽然实现了模板方法+回调模式,但相对于Spring的JdbcTemplate则显得有些“丑陋”。Spring引入了RowMapper和ResultSetExtractor的概念。
RowMapper接口负责处理某一行的数据,例如,我们可以在mapRow方法里对某一行记录进行操作,或封装成entity。
ResultSetExtractor是数据集抽取器,负责遍历ResultSet并根据RowMapper里的规则对数据进行处理。
RowMapper和ResultSetExtractor区别是,RowMapper是处理某一行数据,返回一个实体对象。而ResultSetExtractor是处理一个数据集合,返回一个对象集合

当然,上面所述仅仅是Spring JdbcTemplte实现的基本原理,Spring JdbcTemplate内部还做了更多的事情,比如,把所有的基本操作都封装到JdbcOperations接口内,以及采用JdbcAccessor来管理DataSource和转换异常等。

接下来的主题:进行范型的改造
我们可能发现,上面的接口中返回的还都是Object对象,在范型日益盛行的今天,我们怎能忍受每次都要把得到的对象进行强制类型转换?那我们就要心情享用JDK1.5给我们带来的范型盛宴。

未完待续...


分享到:
评论
26 楼 _Yggd 2013-08-07  
嗯,大神学习了
25 楼 chinagdvea 2013-07-23  
为啥我觉得通过传入匿名类的方法实现方法调用,这是成了策略模式么?匿名类当做一个策略来传入
24 楼 finallygo 2012-12-28  
ameer 写道
finallygo 写道
ameer 写道
finallygo 写道
daquan198163 写道
finallygo 写道
daquan198163 写道
finallygo 写道
我的耦合的意思是你不要去引用HsqldbUtil这样的类,我的意见是设置一个类型为java.sql.Connection的属性,并提供它的set方法,这样不就不会与你那个类耦合了吗?而且也提供了灵活性.因为获取连接方式是很多种的,我觉得你写在HsqldbUtil里是不太好的,你可以看spring里的虽然看起来差不多,但是它提供了setDataSource的方法吧?
我之所以会考虑事务的方面是因为我也写过自己的JdbcTemplate,但是事务处理方面却不理想,所以想看看你是怎么考虑的.而且你这样还有个不好的地方就是你每次执行查询都要重新获得一次连接吧,这样是不是太消耗资源了?
当然还是要感谢你分享你的知识.

拜托,你这个建议会害死人的。Connection不是线程安全的,绝对不能这样用

汗,线程安全不安全要看你是怎么创建这个对象的吧,如果我每次都是用new的方式来创建jdbcTemplate对象,怎么会有线程不安全之说??
就算我使用单例的,难道你不知道有ThreadLocal这个类吗?

是你自己说的“设置一个类型为java.sql.Connection的属性”,还在那狡辩。
如果调用者需要“每次都是用new的方式来创建jdbcTemplate对象”,并且set进去一个connection,调完了还要负责关连接,那还要你这个jdbcTemplate做什么呢,jdbcTemplate不就是为了封装“连接获取、关闭、异常处理”这些逻辑的么?

我没有狡辩啊,我的意思是设置不设置"一个类型为java.sql.Connection的属性"和线程俺不安全没有必然的关系,还有就是"调完了还要负责关连接",这是什么意思,我也可以在jdbcTemplate里关闭啊,为什么不能呢?

真是够菜的,Spring要知道你把它的类用new来创建要活活气死了!
你还是先别自己去写什么jdbc template了,你水平还远远不够。先去理解一下Spring最最基本的IoC

Spring的核心就是,业务代码中需要使用到什么Bean或者Connection之类的资源,绝对不需要自己来创建,
要么委托给Spring的BeanFactory创建和注入,要么委托给第三方资源管理器,比如工厂类或者资源池
这才叫做“反转注入”

人家虽然使用了一个什么HsqldbUtil,我没有下载源代码看,但是从描述中能看出,这种将获取连接的方式委托出去就是一个很好的实践:
1:  获取连接这个操作的具体实现细节就给隐藏了,你管人家是完全重新new了一个资源,还是从已经管理好的连接池里找了个空闲的连接?
2:  在释放资源时,你只能硬生生的对connection执行真正的释放动作,我继续使用第三方工具对connection进行释放,你管我是真的释放了还是仅仅把连接进行了清理归还进了连接池?


什么时候你不再使用到new这个关键字,你再来和我们讨论Spring吧

从你刚才说要支持使用HsqldbUtil这个一个工具类,说明你对ioc的认识还是不清楚,建议你看看真正的spring JdbcTemplate的源码,你看看它是把获取数据库连接放到一个工具类中的么???

你扯淡吧就,1,死读书不如无书,我全文给你讲的是一个思想,你不理解没办法,那什么是硬伤
2, 非要看见棺材才掉泪??
org.springframework.jdbc.core.JdbcTemplate
源码第331行:
Connection con = DataSourceUtils.getConnection(getDataSource());

你看的是你自己写的JdbcTemplate硬说成是Spring的?

1.是你没理解核心思想吧,简单提示 下你,ioc和aop怎么配合使用的
2.它的datasource是从哪里来的??什么叫"没了解本质",你给我上了一课
23 楼 ameer 2012-12-28  
finallygo 写道
ameer 写道
finallygo 写道
daquan198163 写道
finallygo 写道
daquan198163 写道
finallygo 写道
我的耦合的意思是你不要去引用HsqldbUtil这样的类,我的意见是设置一个类型为java.sql.Connection的属性,并提供它的set方法,这样不就不会与你那个类耦合了吗?而且也提供了灵活性.因为获取连接方式是很多种的,我觉得你写在HsqldbUtil里是不太好的,你可以看spring里的虽然看起来差不多,但是它提供了setDataSource的方法吧?
我之所以会考虑事务的方面是因为我也写过自己的JdbcTemplate,但是事务处理方面却不理想,所以想看看你是怎么考虑的.而且你这样还有个不好的地方就是你每次执行查询都要重新获得一次连接吧,这样是不是太消耗资源了?
当然还是要感谢你分享你的知识.

拜托,你这个建议会害死人的。Connection不是线程安全的,绝对不能这样用

汗,线程安全不安全要看你是怎么创建这个对象的吧,如果我每次都是用new的方式来创建jdbcTemplate对象,怎么会有线程不安全之说??
就算我使用单例的,难道你不知道有ThreadLocal这个类吗?

是你自己说的“设置一个类型为java.sql.Connection的属性”,还在那狡辩。
如果调用者需要“每次都是用new的方式来创建jdbcTemplate对象”,并且set进去一个connection,调完了还要负责关连接,那还要你这个jdbcTemplate做什么呢,jdbcTemplate不就是为了封装“连接获取、关闭、异常处理”这些逻辑的么?

我没有狡辩啊,我的意思是设置不设置"一个类型为java.sql.Connection的属性"和线程俺不安全没有必然的关系,还有就是"调完了还要负责关连接",这是什么意思,我也可以在jdbcTemplate里关闭啊,为什么不能呢?

真是够菜的,Spring要知道你把它的类用new来创建要活活气死了!
你还是先别自己去写什么jdbc template了,你水平还远远不够。先去理解一下Spring最最基本的IoC

Spring的核心就是,业务代码中需要使用到什么Bean或者Connection之类的资源,绝对不需要自己来创建,
要么委托给Spring的BeanFactory创建和注入,要么委托给第三方资源管理器,比如工厂类或者资源池
这才叫做“反转注入”

人家虽然使用了一个什么HsqldbUtil,我没有下载源代码看,但是从描述中能看出,这种将获取连接的方式委托出去就是一个很好的实践:
1:  获取连接这个操作的具体实现细节就给隐藏了,你管人家是完全重新new了一个资源,还是从已经管理好的连接池里找了个空闲的连接?
2:  在释放资源时,你只能硬生生的对connection执行真正的释放动作,我继续使用第三方工具对connection进行释放,你管我是真的释放了还是仅仅把连接进行了清理归还进了连接池?


什么时候你不再使用到new这个关键字,你再来和我们讨论Spring吧

从你刚才说要支持使用HsqldbUtil这个一个工具类,说明你对ioc的认识还是不清楚,建议你看看真正的spring JdbcTemplate的源码,你看看它是把获取数据库连接放到一个工具类中的么???

你扯淡吧就,1,死读书不如无书,我全文给你讲的是一个思想,你不理解没办法,那什么是硬伤
2, 非要看见棺材才掉泪??
org.springframework.jdbc.core.JdbcTemplate
源码第331行:
Connection con = DataSourceUtils.getConnection(getDataSource());

你看的是你自己写的JdbcTemplate硬说成是Spring的?
22 楼 finallygo 2012-12-28  
ameer 写道
finallygo 写道
daquan198163 写道
finallygo 写道
daquan198163 写道
finallygo 写道
我的耦合的意思是你不要去引用HsqldbUtil这样的类,我的意见是设置一个类型为java.sql.Connection的属性,并提供它的set方法,这样不就不会与你那个类耦合了吗?而且也提供了灵活性.因为获取连接方式是很多种的,我觉得你写在HsqldbUtil里是不太好的,你可以看spring里的虽然看起来差不多,但是它提供了setDataSource的方法吧?
我之所以会考虑事务的方面是因为我也写过自己的JdbcTemplate,但是事务处理方面却不理想,所以想看看你是怎么考虑的.而且你这样还有个不好的地方就是你每次执行查询都要重新获得一次连接吧,这样是不是太消耗资源了?
当然还是要感谢你分享你的知识.

拜托,你这个建议会害死人的。Connection不是线程安全的,绝对不能这样用

汗,线程安全不安全要看你是怎么创建这个对象的吧,如果我每次都是用new的方式来创建jdbcTemplate对象,怎么会有线程不安全之说??
就算我使用单例的,难道你不知道有ThreadLocal这个类吗?

是你自己说的“设置一个类型为java.sql.Connection的属性”,还在那狡辩。
如果调用者需要“每次都是用new的方式来创建jdbcTemplate对象”,并且set进去一个connection,调完了还要负责关连接,那还要你这个jdbcTemplate做什么呢,jdbcTemplate不就是为了封装“连接获取、关闭、异常处理”这些逻辑的么?

我没有狡辩啊,我的意思是设置不设置"一个类型为java.sql.Connection的属性"和线程俺不安全没有必然的关系,还有就是"调完了还要负责关连接",这是什么意思,我也可以在jdbcTemplate里关闭啊,为什么不能呢?

真是够菜的,Spring要知道你把它的类用new来创建要活活气死了!
你还是先别自己去写什么jdbc template了,你水平还远远不够。先去理解一下Spring最最基本的IoC

Spring的核心就是,业务代码中需要使用到什么Bean或者Connection之类的资源,绝对不需要自己来创建,
要么委托给Spring的BeanFactory创建和注入,要么委托给第三方资源管理器,比如工厂类或者资源池
这才叫做“反转注入”

人家虽然使用了一个什么HsqldbUtil,我没有下载源代码看,但是从描述中能看出,这种将获取连接的方式委托出去就是一个很好的实践:
1:  获取连接这个操作的具体实现细节就给隐藏了,你管人家是完全重新new了一个资源,还是从已经管理好的连接池里找了个空闲的连接?
2:  在释放资源时,你只能硬生生的对connection执行真正的释放动作,我继续使用第三方工具对connection进行释放,你管我是真的释放了还是仅仅把连接进行了清理归还进了连接池?


什么时候你不再使用到new这个关键字,你再来和我们讨论Spring吧

从你刚才说要支持使用HsqldbUtil这个一个工具类,说明你对ioc的认识还是不清楚,建议你看看真正的spring JdbcTemplate的源码,你看看它是把获取数据库连接放到一个工具类中的么???
21 楼 ameer 2012-12-28  
finallygo 写道
daquan198163 写道
finallygo 写道
daquan198163 写道
finallygo 写道
我的耦合的意思是你不要去引用HsqldbUtil这样的类,我的意见是设置一个类型为java.sql.Connection的属性,并提供它的set方法,这样不就不会与你那个类耦合了吗?而且也提供了灵活性.因为获取连接方式是很多种的,我觉得你写在HsqldbUtil里是不太好的,你可以看spring里的虽然看起来差不多,但是它提供了setDataSource的方法吧?
我之所以会考虑事务的方面是因为我也写过自己的JdbcTemplate,但是事务处理方面却不理想,所以想看看你是怎么考虑的.而且你这样还有个不好的地方就是你每次执行查询都要重新获得一次连接吧,这样是不是太消耗资源了?
当然还是要感谢你分享你的知识.

拜托,你这个建议会害死人的。Connection不是线程安全的,绝对不能这样用

汗,线程安全不安全要看你是怎么创建这个对象的吧,如果我每次都是用new的方式来创建jdbcTemplate对象,怎么会有线程不安全之说??
就算我使用单例的,难道你不知道有ThreadLocal这个类吗?

是你自己说的“设置一个类型为java.sql.Connection的属性”,还在那狡辩。
如果调用者需要“每次都是用new的方式来创建jdbcTemplate对象”,并且set进去一个connection,调完了还要负责关连接,那还要你这个jdbcTemplate做什么呢,jdbcTemplate不就是为了封装“连接获取、关闭、异常处理”这些逻辑的么?

我没有狡辩啊,我的意思是设置不设置"一个类型为java.sql.Connection的属性"和线程俺不安全没有必然的关系,还有就是"调完了还要负责关连接",这是什么意思,我也可以在jdbcTemplate里关闭啊,为什么不能呢?

真是够菜的,Spring要知道你把它的类用new来创建要活活气死了!
你还是先别自己去写什么jdbc template了,你水平还远远不够。先去理解一下Spring最最基本的IoC

Spring的核心就是,业务代码中需要使用到什么Bean或者Connection之类的资源,绝对不需要自己来创建,
要么委托给Spring的BeanFactory创建和注入,要么委托给第三方资源管理器,比如工厂类或者资源池
这才叫做“反转注入”

人家虽然使用了一个什么HsqldbUtil,我没有下载源代码看,但是从描述中能看出,这种将获取连接的方式委托出去就是一个很好的实践:
1:  获取连接这个操作的具体实现细节就给隐藏了,你管人家是完全重新new了一个资源,还是从已经管理好的连接池里找了个空闲的连接?
2:  在释放资源时,你只能硬生生的对connection执行真正的释放动作,我继续使用第三方工具对connection进行释放,你管我是真的释放了还是仅仅把连接进行了清理归还进了连接池?


什么时候你不再使用到new这个关键字,你再来和我们讨论Spring吧
20 楼 whao189 2011-05-26  
刚好这两天看模板方法方法模式,

看了楼主的解释 正好有助于我对此模式的理解。但是对于spring 我一直有心去研究

但是源码从哪个地方看起我还不太清楚,楼主应该看spring 有一段时间了走过去的人

了,不知道能不能指点一下我让我少走些弯路!
19 楼 cai3178940 2010-12-14  
写的非常好啊,支持楼主,不过还是被投了好多新手贴。。。
楼上说用反射是怎么做的呢,我觉得像楼主说的用泛型就可以不用对每个pojo做一个jdbcTemplateImpl
18 楼 goolcona 2010-12-14  
说一点关于类映射的相关,可以考虑使用反射在组装这个User,这样的话就不必针对每个pojo做一个JdbcTepmlateImpl。
17 楼 xchao 2010-10-30  
回调方法的原理、设计、实现、示例讲解的很好!
学习了!
16 楼 finallygo 2010-10-26  
daquan198163 写道
finallygo 写道
daquan198163 写道
finallygo 写道
我的耦合的意思是你不要去引用HsqldbUtil这样的类,我的意见是设置一个类型为java.sql.Connection的属性,并提供它的set方法,这样不就不会与你那个类耦合了吗?而且也提供了灵活性.因为获取连接方式是很多种的,我觉得你写在HsqldbUtil里是不太好的,你可以看spring里的虽然看起来差不多,但是它提供了setDataSource的方法吧?
我之所以会考虑事务的方面是因为我也写过自己的JdbcTemplate,但是事务处理方面却不理想,所以想看看你是怎么考虑的.而且你这样还有个不好的地方就是你每次执行查询都要重新获得一次连接吧,这样是不是太消耗资源了?
当然还是要感谢你分享你的知识.

拜托,你这个建议会害死人的。Connection不是线程安全的,绝对不能这样用

汗,线程安全不安全要看你是怎么创建这个对象的吧,如果我每次都是用new的方式来创建jdbcTemplate对象,怎么会有线程不安全之说??
就算我使用单例的,难道你不知道有ThreadLocal这个类吗?

是你自己说的“设置一个类型为java.sql.Connection的属性”,还在那狡辩。
如果调用者需要“每次都是用new的方式来创建jdbcTemplate对象”,并且set进去一个connection,调完了还要负责关连接,那还要你这个jdbcTemplate做什么呢,jdbcTemplate不就是为了封装“连接获取、关闭、异常处理”这些逻辑的么?

我没有狡辩啊,我的意思是设置不设置"一个类型为java.sql.Connection的属性"和线程俺不安全没有必然的关系,还有就是"调完了还要负责关连接",这是什么意思,我也可以在jdbcTemplate里关闭啊,为什么不能呢?
15 楼 xchao 2010-10-25  
不错的文章,条理清晰,JAVA新手来学习了!
14 楼 daquan198163 2010-10-20  
finallygo 写道
daquan198163 写道
finallygo 写道
我的耦合的意思是你不要去引用HsqldbUtil这样的类,我的意见是设置一个类型为java.sql.Connection的属性,并提供它的set方法,这样不就不会与你那个类耦合了吗?而且也提供了灵活性.因为获取连接方式是很多种的,我觉得你写在HsqldbUtil里是不太好的,你可以看spring里的虽然看起来差不多,但是它提供了setDataSource的方法吧?
我之所以会考虑事务的方面是因为我也写过自己的JdbcTemplate,但是事务处理方面却不理想,所以想看看你是怎么考虑的.而且你这样还有个不好的地方就是你每次执行查询都要重新获得一次连接吧,这样是不是太消耗资源了?
当然还是要感谢你分享你的知识.

拜托,你这个建议会害死人的。Connection不是线程安全的,绝对不能这样用

汗,线程安全不安全要看你是怎么创建这个对象的吧,如果我每次都是用new的方式来创建jdbcTemplate对象,怎么会有线程不安全之说??
就算我使用单例的,难道你不知道有ThreadLocal这个类吗?

是你自己说的“设置一个类型为java.sql.Connection的属性”,还在那狡辩。
如果调用者需要“每次都是用new的方式来创建jdbcTemplate对象”,并且set进去一个connection,调完了还要负责关连接,那还要你这个jdbcTemplate做什么呢,jdbcTemplate不就是为了封装“连接获取、关闭、异常处理”这些逻辑的么?
13 楼 finallygo 2010-10-20  
daquan198163 写道
finallygo 写道
我的耦合的意思是你不要去引用HsqldbUtil这样的类,我的意见是设置一个类型为java.sql.Connection的属性,并提供它的set方法,这样不就不会与你那个类耦合了吗?而且也提供了灵活性.因为获取连接方式是很多种的,我觉得你写在HsqldbUtil里是不太好的,你可以看spring里的虽然看起来差不多,但是它提供了setDataSource的方法吧?
我之所以会考虑事务的方面是因为我也写过自己的JdbcTemplate,但是事务处理方面却不理想,所以想看看你是怎么考虑的.而且你这样还有个不好的地方就是你每次执行查询都要重新获得一次连接吧,这样是不是太消耗资源了?
当然还是要感谢你分享你的知识.

拜托,你这个建议会害死人的。Connection不是线程安全的,绝对不能这样用

汗,线程安全不安全要看你是怎么创建这个对象的吧,如果我每次都是用new的方式来创建jdbcTemplate对象,怎么会有线程不安全之说??
就算我使用单例的,难道你不知道有ThreadLocal这个类吗?
12 楼 daquan198163 2010-10-09  
finallygo 写道
我的耦合的意思是你不要去引用HsqldbUtil这样的类,我的意见是设置一个类型为java.sql.Connection的属性,并提供它的set方法,这样不就不会与你那个类耦合了吗?而且也提供了灵活性.因为获取连接方式是很多种的,我觉得你写在HsqldbUtil里是不太好的,你可以看spring里的虽然看起来差不多,但是它提供了setDataSource的方法吧?
我之所以会考虑事务的方面是因为我也写过自己的JdbcTemplate,但是事务处理方面却不理想,所以想看看你是怎么考虑的.而且你这样还有个不好的地方就是你每次执行查询都要重新获得一次连接吧,这样是不是太消耗资源了?
当然还是要感谢你分享你的知识.

拜托,你这个建议会害死人的。Connection不是线程安全的,绝对不能这样用
11 楼 jakoes 2010-10-09  
blackchoc 写道
感谢楼主分享。
现在我在试着重构手头的一个项目,不幸的是这个项目已经经历了n多人,从最初到现在已经很久了。所用的技术就是楼主传说中“高手”所用的sun jdbc api。哈哈

那个项目应该没有使用spring框架吧,如果使用spring框架,jdbcTemplate是首选(当然是在没有使用ORM框架的情况下)。
10 楼 ironsabre 2010-09-06  
blackchoc 写道
感谢楼主分享。
现在我在试着重构手头的一个项目,不幸的是这个项目已经经历了n多人,从最初到现在已经很久了。所用的技术就是楼主传说中“高手”所用的sun jdbc api。哈哈

你最好的选择我认为是引入JdbcTemplate,JdbcTemplate是对直接使用jdbc api的最好升级途径。
9 楼 blackchoc 2010-09-04  
感谢楼主分享。
现在我在试着重构手头的一个项目,不幸的是这个项目已经经历了n多人,从最初到现在已经很久了。所用的技术就是楼主传说中“高手”所用的sun jdbc api。哈哈
8 楼 ironsabre 2010-07-23  
finallygo 写道
我感觉有点问题吧,首先jdbctemplate好像没有与你这里的HsqldbUtil这种类耦合在一起吧,还有一个就是,你这里好像进行了一次查询之后数据库连接就关闭了,那我怎么做事务呢?


你难道还在用连接相关的事务吗?
7 楼 finallygo 2010-07-22  
说到spring,我有点小小的疑问,
我先说下我对spring的认识啊,spring中最主要的两个思想是ioc,aop
ioc就体现在工厂模式的应用,也就是说我们采用的都是面向接口的方式的编程,以后如果实现类发生变化了,可以通过修改配置文件而不用修改代码来实现系统的灵活性,但是实际上好像这种需求并不多啊,我们项目中都是一个接口,一个实现类,也就是说配置文件我们添加一个Dao的时候才去改配置文件,之后就再也不去动了,反而接口是经常变动的,因为需求是一直在增加的,所以给我的感觉就是接口成为了一个累赘,我改一个接口就要改一堆的东西(因为我们分了好几层,而一个方法的添加就从dao一直污染到顶层了),我觉得还不如直接创建一个实现类来的方便.
还有一个就是aop,我觉得主要就是用来做事务和日志的,但是这些用动态代理就可以实现了,我觉得spring太复杂了,没有必要用的
不知道我这两个思想有什么问题,麻烦你讲解一下.

相关推荐

    Spring源代码解析(三):Spring_JDBC.doc

    总的来说,Spring JDBC的JdbcTemplate是Spring框架中用于数据库操作的关键组件,通过回调接口和模板方法设计,它极大地简化了数据库访问的代码,同时也提供了良好的异常处理和资源管理机制。理解和掌握JdbcTemplate...

    多步提交示例中各个类的调用顺序,包含源码及配置文件

    4. **Callback/Listener机制**:为了确保每一步的正确执行,可能会使用回调或监听器机制。例如,`AfterCompletionCallback`可以在事务完成后执行某些清理或通知任务。这些回调类会在适当的时间点被事务管理器调用。 ...

    闭路头钉铆合机sw16可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    闭路头钉铆合机sw16可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    级联H桥储能系统中的相间与相内SOC均衡及解耦控制技术研究

    内容概要:本文深入探讨了级联H桥储能系统中的关键技术和挑战,特别是在相间和相内SOC均衡方面。文中详细介绍了相间SOC均衡和相内SOC均衡的重要性及其对系统性能的影响。为了实现高效的SOC均衡,文章讨论了多种控制技术,包括解耦控制、单极倍频载波移相调制、零序电压注入法和负序电压注入法。此外,还涉及了蓄电池充放电控制的具体方法和技术细节,如恒流-恒压充电和防止过充过放的措施。通过这些技术的应用,能够显著提高储能系统的效率和稳定性。 适合人群:从事电力电子、储能系统设计和控制领域的研究人员、工程师和技术爱好者。 使用场景及目标:适用于需要深入了解级联H桥储能系统的工作原理和控制策略的研究人员和工程师。主要目标是掌握相间和相内SOC均衡的方法,以及解耦控制和其他关键技术的实际应用,以优化储能系统的性能。 其他说明:文章提供了大量的伪代码和公式推导,帮助读者更好地理解和实现这些复杂的控制技术。同时,强调了实际调试中的注意事项,如参数整定和避免系统振荡等问题。

    c语言奔跑的火柴人游戏源码.zip

    c语言奔跑的火柴人游戏源码.zip

    tini-0.19.0-1.el8.x64-86.rpm.tar.gz

    1、文件说明: Centos8操作系统tini-0.19.0-1.el8.rpm以及相关依赖,全打包为一个tar.gz压缩包 2、安装指令: #Step1、解压 tar -zxvf tini-0.19.0-1.el8.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm

    书本胶订切边设备sw18_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    书本胶订切边设备sw18_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    编程语言TypeScript核心特性解析:静态类型检查与面向对象编程助力大型应用开发及团队协作

    内容概要:TypeScript是由微软开发的开源编程语言,作为JavaScript的超集,它在JS基础上增加了静态类型定义和对ES6+特性的支持,旨在提升大型应用开发的效率与代码质量。TypeScript具有静态类型检查、类型推断、接口和类、装饰器、模块系统五大特点。其中,静态类型检查能在编译期捕捉类型错误,增强代码健壮性;类型推断让开发者无需频繁声明变量类型;接口和类使代码结构更模块化;装饰器提供了一种灵活的元编程手段;模块系统支持代码按需加载。此外,TypeScript最终编译为JavaScript,确保了广泛的环境兼容性。 适合人群:前端开发工程师,尤其是参与大型项目或团队协作的开发者。 使用场景及目标:①适用于开发大型应用程序,保证代码的可维护性;②在团队协作中,提高代码可读性和减少bug;③借助静态类型检查,在开发阶段发现并解决潜在性能问题。 其他说明:推荐学习官方文档、入门教程等资源,GitHub仓库也有助于深入理解TypeScript的功能和应用场景。

    草莓采摘机器人sw22可编辑+仿真视频_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    草莓采摘机器人sw22可编辑+仿真视频_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    基于Comsol的激光熔覆熔池流动数值模拟:探究马兰戈尼对流及S活性元素影响

    内容概要:本文详细探讨了利用Comsol软件对激光熔覆过程中熔池流动行为进行数值模拟的方法和技术。主要内容涵盖熔池内主要驱动力如马兰戈尼对流、表面张力、重力和浮力的作用机理及其相互影响,特别关注了S活性元素对表面张力系数的影响,导致马兰戈尼对流方向发生变化,从而使熔池表面形成凸起。文中提供了具体的MATLAB和Python代码片段用于定义表面张力系数随温度变化的关系,并介绍了如何在Comsol中设置相关物理场和边界条件。此外,还讨论了网格处理方法和一些有趣的模拟现象,如熔池表面波动结构和涡量场特征。 适合人群:从事材料科学、激光加工技术研究的专业人士,以及对多物理场耦合仿真感兴趣的科研工作者。 使用场景及目标:适用于希望通过数值模拟深入了解激光熔覆过程中熔池内部复杂流动行为的研究人员。目标是优化激光熔覆工艺参数,提高涂层质量和性能。 其他说明:文中提到的模型和方法不仅有助于理论研究,还可以指导实际工程应用中的工艺改进。同时强调了参数校验的重要性,避免因错误设置而导致不合理的结果。

    Java项目源码带论文(jsp+servlet+javabean)l.zip

    Java项目源码带论文(jsp+servlet+javabean)l.zip

    各院校专业录取分数线.zip

    各院校专业录取分数线.zip

    openssl-1.0.2k-fips

    windows版本的openssl-1.0.2k-fips,用openssl-1.0.2k和openssl-fips-2.0.16和集成编译的,下载之后环境变量配置bin目录就可用

    螺母压装方案sw16可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    螺母压装方案sw16可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    基于组态王6.53的3层立体车库(9车位)自动化仿真程序设计与实现

    内容概要:本文详细介绍了使用组态王6.53开发的3层立体车库(9车位)仿真程序。首先概述了车库的结构特点,接着讲解了如何在组态王中创建工程、绘制图形界面以及定义和关联变量。随后重点阐述了通过命令语言实现车辆进出逻辑的方法,包括车位状态判断、平移和升降控制等关键步骤。此外,还探讨了调试过程中遇到的问题及其解决方案,如变量同步、动画速度调整等。最后分享了一些实用技巧,如虚拟PLC配置、状态机设计和报警机制等。 适合人群:从事自动化控制系统开发的技术人员,尤其是对组态王有一定了解的工程师。 使用场景及目标:适用于希望深入了解组态王应用开发、掌握自动化控制逻辑实现方法的学习者。目标是能够独立完成类似的自动化仿真项目。 其他说明:文中提供了大量实例代码片段,帮助读者更好地理解和实践相关知识点。同时提醒注意一些常见的陷阱和技术难点,如PLC通信延迟、脚本执行优先级等问题。

    基于LabVIEW的模拟采集卡与互相关定位算法的气体管道泄漏检测系统

    内容概要:本文详细介绍了利用LabVIEW平台构建的气体管道泄漏检测系统的实现方法和技术细节。系统采用NI的模拟采集卡进行信号采集,并运用互相关算法计算泄漏点位置。文中涵盖了硬件选型、采样率设定、互相关算法配置、噪声处理以及系统优化等多个方面的内容。通过实际案例展示了系统的应用效果,强调了生产者/消费者模式在提高系统稳定性和响应速度方面的作用。 适合人群:从事工业自动化、管道安全监控领域的工程师和技术人员。 使用场景及目标:适用于需要实时监测气体管道泄漏情况的企业和机构,旨在提供一种低成本、高效的解决方案,减少因泄漏带来的安全隐患和经济损失。 其他说明:文中提到的一些具体技术和实现细节,如互相关算法的具体配置、噪声处理方法等,对于理解和实施类似项目非常有帮助。同时,作者分享了许多实践经验,有助于避免常见的错误和陷阱。

    10车位三层四列立体车库:组态王6.53与西门子200PLC仿真实现及应用

    内容概要:本文详细介绍了10车位三层四列立体车库的自动化控制系统的设计与实现。主要内容涵盖西门子200PLC的IO分配表设计、PLC编程要点以及组态王6.53的工程创建、设备定义、变量定义、画面设计和动画连接等方面。文中不仅提供了具体的IO分配表和梯形图逻辑代码,还深入探讨了PLC与组态王之间的交互细节,如动画绑定、定时器设置、互锁逻辑等。此外,作者还分享了一些调试经验和常见问题解决方案,如防止传感器抖动、优化画面流畅度等。 适合人群:从事工业自动化控制领域的工程师和技术人员,特别是对PLC编程和HMI设计有一定基础的学习者。 使用场景及目标:适用于需要理解和掌握立体车库自动化控制系统的人员,旨在帮助他们熟悉PLC与HMI的联合调试方法,提高实际项目的开发效率和稳定性。 其他说明:文末附有完整的工程文件下载链接,包括组态王HMI文件、S7-200程序和IO表说明文档,方便读者进行实践操作。

    【新能源汽车电子电气】基于功能域划分的动力系统通信网络与控制器需求分析:电子电气架构设计要点

    内容概要:本文详细介绍了新能源汽车电子电气系统功能需求及其架构特点。首先,文章阐述了新能源汽车的分类及其与传统汽车控制系统的主要差异,特别是动力系统的转变对电驱动系统、电池管理系统及电控系统提出的更高要求。接着,重点分析了新能源汽车动力域的通信网络,对比了CAN、CANFD、FlexRay和车载以太网四种网络技术的优缺点,指出CANFD和FlexRay是当前动力域内部通信网络的主流选择。此外,文章还探讨了动力域控制器的功能要求,强调了其在通信支持、软件实时性、安全等级和信息安全方面的关键作用。最后,文章讨论了新能源汽车底盘系统的特点,特别是线控底盘作为未来发展方向的重要性,以及底盘域控制器在智能驾驶中的核心地位。 适用人群:从事汽车电子工程及相关领域的工程师、研究人员和技术人员,尤其是关注新能源汽车发展的专业人士。 使用场景及目标:①帮助工程师理解新能源汽车电子电气架构的设计思路和技术难点;②为研发人员提供动力域通信网络选型的参考依据;③指导技术人员掌握动力域控制器和底盘域控制器的功能需求及发展趋势。 阅读建议:本文内容涵盖广泛,建议读者根据自身专业背景和兴趣点进行有针对性的阅读。对于初学者,可以从新能源汽车的基本概念入手,逐步深入到具体的通信网络和技术细节;对于有一定经验的研发人员,可以重点关注动力域控制器和线控底盘的相关内容,以获取最新的技术和应用趋势。

    飞机起落架落下试验塔sw10可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    飞机起落架落下试验塔sw10可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    基于Matlab/Simulink的模块化多电平换流器(MMC)仿真与控制优化

    内容概要:本文详细介绍了如何在Matlab/Simulink环境下构建和优化模块化多电平换流器(MMC)的仿真模型。主要内容涵盖子模块电容电压均衡、环流抑制、调制策略以及仿真参数设置等方面的技术细节。文中提供了具体的代码示例,如子模块参数初始化、电容排序控制、环流抑制控制器配置等,并分享了许多实用的经验和技巧,帮助读者避免常见的错误。 适合人群:从事电力电子研究和技术开发的专业人士,尤其是对MMC仿真感兴趣的工程师和研究人员。 使用场景及目标:适用于希望深入了解MMC内部机制及其控制方法的研究人员,旨在提高仿真效率和准确性,确保最终设计能够稳定可靠地运行。 其他说明:文章不仅提供了详细的理论解释,还包括大量实战经验和故障排除指南,有助于读者更好地理解和应用相关技术。同时强调了在实际硬件部署前进行充分仿真的重要性。

Global site tag (gtag.js) - Google Analytics