首先说说为什么要用到回调这个方式,
我们在日常生活和工作中,有一种情况是这样的,做一件事,但是这件事中有些步骤是不确定如何做的,但是可以先约定好怎么做.对于程序来说,就是有一段业务,其中有几段小逻辑不确定如何做,但是我们可以先定义好一些方法(统一的参数和返回值)对应这些逻辑, 具体这些逻辑的实现交给具体执行的代码去实现.
下面举个例子,JDBC 访问,假设我们都用PreparedStatement来处理SQL, 我们都要初始化连接,初始化PreparedStatement,封装PreparedStatement插入SQL参数,执行SQL,获得ResultSet,封装ResultSet成我们要的对象,关闭连接,其中封装PreparedStatement插入SQL参数和封装ResultSet成我们要的对象是不确定的,我们就可以把这2步定义成回调函数,交给具体执行代码去做.
根据上面说的,我们可以定一个JdbcCallback接口来定义这个回调函数,
package com.balance.easycalendar.dao.template;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import com.balance.easycalendar.to.BaseTO;
public interface JdbcCallback {
public List<BaseTO> packResult(ResultSet rs) throws SQLException;
public void packParams(PreparedStatement stmt) throws SQLException;
}
再定义一个JdbcCallbackTemplate来执行具体的方法,其中引用一个JdbcCallback来完成不确定的步骤,
package com.balance.easycalendar.dao.template;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import com.balance.easycalendar.dao.exception.DAOException;
import com.balance.easycalendar.to.BaseTO;
import com.balance.easycalendar.util.MessageProperties;
public class JdbcCallbackTemplate {
private JdbcCallback temp;
public void setTemp(JdbcCallback temp) {
this.temp = temp;
}
protected String url;
protected String username;
protected String password;
protected String driver;
private PreparedStatement stmt = null;
private Connection con = null;
public JdbcCallbackTemplate(){
MessageProperties pro = MessageProperties.getInstance("jdbc.properties");
url = pro.getValue("db.url");
driver = pro.getValue("db.driver");
username = pro.getValue("db.username");
password = pro.getValue("db.password");
}
public List<BaseTO> query(String sql) throws DAOException{
try{
Class.forName(driver);
con = DriverManager.getConnection(url, username, password);
stmt = con.prepareStatement(sql);
temp.packParams(stmt);
ResultSet rs = stmt.executeQuery();
return temp.packResult(rs);
}catch(Exception e){
throw new DAOException(e);
}finally{
try{
stmt.close();
con.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
public boolean excute(String sql) throws DAOException{
try{
Class.forName(driver);
con = DriverManager.getConnection(url, username, password);
stmt = con.prepareStatement(sql);
temp.packParams(stmt);
return stmt.execute();
}catch(Exception e){
throw new DAOException(e);
}finally{
try{
stmt.close();
con.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
这个Template定义了2个方法, excute用来执行增加,修改,删除等,query用来执行查询,在这2个方法里,封装参数,封装result都不确定,所以用了上面定义的回调函数来实行,具体实现交给具体的实现类去做.(MessageProperties 是我写的一个资源引用类,可以参考我的另一篇blog:利用多例模式编写配置文件读取器 )
下面我们看看具体的实现,
BaseDAO里实例化一个jdbcCallbackTemplate
package com.balance.easycalendar.dao;
import com.balance.easycalendar.dao.template.JdbcCallbackTemplate;
import com.balance.easycalendar.util.MessageProperties;
public abstract class BaseDAO {
protected JdbcCallbackTemplate jdbcCallbackTemplate = new JdbcCallbackTemplate();
public String getSql(String sqlId){
MessageProperties pro = MessageProperties.getInstance("sqls.properties");
return pro.getValue(sqlId);
}
}
定义一个TaskDAO,用来执行具体的数据库访问.首先给jdbcCallbackTemplate注入实例化的JdbcCallback(实现具体的回调函数),然后就可以调用Template里的query和excute方法了.
package com.balance.easycalendar.dao;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.balance.easycalendar.dao.exception.DAOException;
import com.balance.easycalendar.dao.template.JdbcCallback;
import com.balance.easycalendar.dao.template.JdbcCallbackAdapter;
import com.balance.easycalendar.to.BaseTO;
import com.balance.easycalendar.to.TaskTO;
public class TaskDAO extends BaseDAO{
public TaskTO getTask(final String taskId) throws DAOException {
jdbcCallbackTemplate.setTemp(new JdbcCallback(){
@Override
public void packParams(PreparedStatement stmt) throws SQLException {
stmt.setString(1, taskId);
}
@Override
public List<BaseTO> packResult(ResultSet rs) throws SQLException {
List<BaseTO> tasks = new ArrayList<BaseTO>();
while (rs.next()) {
TaskTO taskTO = new TaskTO();
taskTO.setTaskId(rs.getString("TASK_ID"));
taskTO.setTaskName(rs.getString("TASK_NAME"));
taskTO.setTaskDesc(rs.getString("TASK_DESC"));
tasks.add(taskTO);
}
return tasks;
}
});
List<BaseTO> tasks = jdbcCallbackTemplate.query(getSql("task.search.bytaskid"));
return (TaskTO)tasks.get(0);
}
public boolean insertTask(final String taskId, final String taskName, final String taskDesc) throws DAOException {
jdbcCallbackTemplate.setTemp(new JdbcCallbackAdapter(){
@Override
public void packParams(PreparedStatement stmt) throws SQLException {
stmt.setString(1, taskId);
stmt.setString(2, taskName);
stmt.setString(3, taskDesc);
}
});
return jdbcCallbackTemplate.excute(getSql("task.insert"));
}
public boolean delTask(final String taskId) throws DAOException {
jdbcCallbackTemplate.setTemp(new JdbcCallbackAdapter(){
@Override
public void packParams(PreparedStatement stmt) throws SQLException {
stmt.setString(1, taskId);
}
});
return jdbcCallbackTemplate.excute(getSql("task.delete.bytaskid"));
}
}
sql我也用MessageProperties来读取了,定义在一个单独的properties文件里,
task.search.bytaskid = select TASK_ID,TASK_NAME,TASK_DESC from TB_TASKS where TASK_ID = ?
task.insert = insert into TB_TASKS (TASK_ID,TASK_NAME,TASK_DESC) values (?,?,?)
task.delete.bytaskid = Delete from TB_TASKS where TASK_ID = ?
JdbcCallbackAdapte是JdbcCallback的一个默认适配器,提供了packResult一个空实现,因为excute不需要封装result对象。
好了,这个例子就是这样了.