首先說說為什麼要用到回調這個方式,
我們在日常生活和工作中,有一種情況是這樣的,做一件事,但是這件事中有些步驟是不確定如何做的,但是可以先約定好怎麼做.對於程序來說,就是有一段業務,其中有幾段小邏輯不確定如何做,但是我們可以先定義好一些方法(統一的參數和返回值)對應這些邏輯, 具體這些邏輯的實現交給具體執行的代碼去實現.
下面舉個例子,JDBC 訪問,假設我們都用PreparedStatement來處理SQL, 我們都要初始化連接,初始化PreparedStatement,封裝PreparedStatement插入SQL參數,執行SQL,獲得ResultSet,封裝ResultSet成我們要的對象,關閉連接,其中封裝PreparedStatement插入SQL參數和封裝ResultSet成我們要的對象是不確定的,我們就可以把這2步定義成回調函數,教給具體執行代碼去做.
根據上面說的,我們可以定一個JdbcCallback接口來定義這個回調函數,
Java代碼
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來完成不確定的步驟,
Java代碼
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
Java代碼
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方法瞭.
Java代碼
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文件裡,
Sql代碼
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 = ?