所谓回调,就是在执行某个程序时,具体的封装处理由第三方类来实现,简单一点说就是记录内部,再出来(由第三方类可以对数据进行处理),再返回去继续执行,这个过程就是回调。想要程序具有记录内部的功能就必须定义一个规范,也就是接口,即你的程序出来被其他类处理了,但你规定了还要返回原程序。
下面看一个简单的例子:
/** * @描述:回调函数--记录内部,再出来返回去的过程就叫回调 * @author cxie */ public class CopyOfCallBackDemo { public static void main(String[] args) { QRunner run = new QRunner(); run.query("cxie",new RunnerHandler1(){ @Override public void handler(String name) { System.err.println(name+"1"); } }); } } /** * 定义调用类 */ class QRunner{ public void query(String sql,RunnerHandler1 rh){ //调用规范的实现类 System.err.println(sql+"o"); rh.handler(sql); } } /** * 定义回调规范 */ interface RunnerHandler1{ void handler(String name); }程序的执行结果如下
下面再看一个高级一点的例子,通过定义泛型,使回调具有任意的返回值
<span style="font-size:18px;">package hd.cx.dbutilTest; import java.util.List; import java.util.Map; /** * @描述:回调函数--记录内部,再出来返回去的过程就叫回调 * @author 刘楚燮 * @拓展:通过定义泛型,使回调具有任意的返回值 * */ public class CallBackDemo { public static void main(String[] args) { Runner run = new Runner(); run.query("zhangsan",new RunnerHandler<List<Map<String, Object>>>()/** 定义了该方法的规范*/{ @Override public List<Map<String,Object>> handler(String name){ System.err.print(name+"1"); return null; } }); } } /** * 定义一个调用类 */ class Runner{ public <T>/**这里是定义泛型*/ T query(String sql,RunnerHandler<T>/**这里是使用泛型*/ rh){ //调用一规范的实现类 System.err.println(sql+"0"); return rh.handler(sql); } } /** * 定义一个回调规范:接口 */ interface RunnerHandler<T>{ T handler(String name); }</span>
程序的运行结果同样如下
由此可以看出程序先执行调用类再回调回去执行接口定义的方法。
接下来是简单模拟Queryrunner中的两种方法来来编写自己的Queryrunner(一种是封装成Map放到List中,一种是封装到JavaBean中,暂时先不使用回调函数,大概了解一下原理)
<span style="font-size:18px;">import java.lang.reflect.Method; import java.sql.Connection; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.sql.DataSource; import java.sql.Statement; /** * @描述:模拟queryrunner编写一个封装数据库操作的方法 * @author cxie * */ public class QueryRunner { private DataSource ds; public QueryRunner(){ } public QueryRunner(DataSource ds){ this.ds=ds; } /** * 只封装List<Map> */ public List<Map<String,Object>> query(String sql){ //封装数据用 List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();//声明返回对象 Connection con = null; try { con = ds.getConnection(); //执行查询 Statement st = con.createStatement(); ResultSet rs = st.executeQuery(sql); //分析结果集 ResultSetMetaData rsmd = rs.getMetaData(); //获取列数 int cols = rsmd.getColumnCount(); System.err.println("列数:"+cols); //遍历数据 while(rs.next()){ Map<String,Object> mm = new HashMap<String, Object>(); //遍历列 for(int i = 0;i<cols;i++){//0,1,2,3 //获取列名 String colName = rsmd.getColumnName(i+1);//1,2,3,4 //获取数据 Object val = rs.getObject(i+1); //封装到Map里面 mm.put(colName, val); } list.add(mm); } } catch (Exception e) { throw new RuntimeException(e); }finally{ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } return list; } /** * 封装成List<bean> * <T>定义泛型 * List<T>使用泛型 * Class<T> 接收一个T类型的字节码对象 * Type是class父类 * 记得bean中要覆盖toString方法 */ public <T> List<T> queryForBean(String sql,Class<T> cls){ //声明返回对象 List<T> list = new ArrayList<T>(); //获取连接 Connection con = null; try{ con = ds.getConnection(); Statement st = con.createStatement(); ResultSet rs = st.executeQuery(sql); ResultSetMetaData rsmd = rs.getMetaData(); //获取列数 int cols = rsmd.getColumnCount(); while(rs.next()){ //每次遍历一行,应该实例化一个bean T t = cls.newInstance(); //遍历所有列 for(int i = 0;i<cols;i++){ String colName = rsmd.getColumnName(i+1);//获取列名 colName = colName.toLowerCase();//全部转为小写 //将colName转成setXxx String methodName = "set"+colName.substring(0,1).toUpperCase()+colName.substring(1).toLowerCase(); //通过数据库获取数据库对应的java类型QName //varchar - java.lang.String , int -- java.lang.Integer String javaType = rsmd.getColumnClassName(i+1); //反射出T的方法 try{ Method method = cls.getMethod(methodName, Class.forName(javaType)); //执行,查询数据库 Object value = rs.getObject(colName); method.invoke(t, value);//调用JavaBean的set方法把值set进去 }catch(Exception e){ //e.printStackTrace(); } } list.add(t); } }catch(Exception e){ e.printStackTrace(); }finally{ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } return list; } }</span>
本次使用的数据库连接方法仍然是c3p0连接池,具体实现方法请看我上篇博客:通过使用反射+动态代理+注解来实现对事务的控制
接下来编写一个测试方法:
<span style="font-size:18px;">package hd.cx.demo; import java.util.List; import java.util.Map; import org.junit.Test; import hd.cx.utils.DataSourceUtils; import hd.cx.queryrunner.QueryRunner; public class DaoTest { @Test public void test1(){ QueryRunner run = new QueryRunner(DataSourceUtils.getDatasSource()); String sql = "select * from user"; List<Map<String, Object>> list = run.query(sql); System.out.println(list); } }</span>
封装成List<bean>的测试类如下:
<span style="font-size:18px;">@Test public void test2(){ QueryRunner run = new QueryRunner(DataSourceUtils.getDatasSource()); String sql = "select * from user"; List<User> list = run.queryForBean(sql,User.class); System.out.println(list); }</span>
运行结果
接下来是模拟dbutils中的方法,使用回调函数编写一个自己的BeanListHandler,方法大致跟上面的一样,具体代码如下
回调函数:
<span style="font-size:18px;"> /** * 有回调的查询 */ public <T> T query(String sql,MyHandler<T> mh){ T t = null; //声明conn Connection con = null; try { con = ds.getConnection(); Statement st = con.createStatement(); ResultSet rs = st.executeQuery(sql); //让回调去执行数据库封装 t = mh.handler(rs); } catch (Exception e) { e.printStackTrace(); }finally{ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } return t; } </span>
被调用的第三方类:
具体实现都差不多就不写注释了
<span style="font-size:18px;">package hd.cx.queryrunner; import java.lang.reflect.Method; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.util.ArrayList; import java.util.List; public class MyBeanListHandler<T> implements MyHandler<List<T>> { private Class<T> cls; /**传入bean的字节码*/ public MyBeanListHandler(Class<T> cls){ this.cls=cls; } @Override public List<T> handler(ResultSet rs) { List<T> list = new ArrayList<T>(); try { ResultSetMetaData rsmd = rs.getMetaData(); int cols = rsmd.getColumnCount(); while(rs.next()){ T t = cls.newInstance(); for(int i = 0; i<cols;i++){ String colsName = rsmd.getColumnName(i+1); String methodName = "set" + colsName.substring(0,1) .toUpperCase() +colsName.substring(1).toLowerCase(); String javaType = rsmd.getColumnClassName(i+1); try { Method mm = cls.getMethod(methodName, Class.forName(javaType)); Object val = rs.getObject(i+1); mm.invoke(t, val); } catch (Exception e) { //异常不要处理 } } list.add(t); } } catch (Exception e) { } return list; } }</span>
定义的规范,也就是接口:
<span style="font-size:18px;">package hd.cx.queryrunner; /** * @描述:定义规范,接受rs结果集 * @author cxie */ import java.sql.ResultSet; public interface MyHandler<T> { T handler(ResultSet rs); }</span>
接下来编写一个测试方法
<span style="font-size:18px;"> @Test public void test3(){ QueryRunner run = new QueryRunner(DataSourceUtils.getDatasSource()); String sql = "select * from user"; List<User> list = run.query(sql, new MyBeanListHandler<User>(User.class)); System.out.println(list); }</span>
再来看一下dbutils中beanlisthandler的执行方法
<span style="font-size:18px;">@Test public void test4(){ org.apache.commons.dbutils.QueryRunner run = new org.apache.commons.dbutils.QueryRunner(DataSourceUtils.getDatasSource()); String sql = "select * from user"; List<User> list = null; try { list = run.query(sql, new BeanListHandler<User>(User.class)); } catch (SQLException e) { e.printStackTrace(); } System.out.println(list); }</span>
执行结果都是一样的,这里就不列出来了。
如果仔细一点的话就能发现,当调用dbutils中的方法时需要捕获异常,而调用MyBeanListHandler时确实不用,原因在于编写MyBeanListHandler时已经对异常进行了捕获,所以我们可以重写dbutils中的Queryrunner呢,捕获每个方法的异常,这样每次调用的时候就不必要try-catch了。其实说白了很简单,就是继承dbutils的Queryrunner,对父类的方法进行try-catch覆盖,
下面是dbutils的源码和jar包的下载地址
http://pan.baidu.com/s/1kT2y9FL
下面是覆盖了Queryrunner方法的完整代码如下
<span style="font-size:18px;">package hd.cx.utils; import java.beans.PropertyDescriptor; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.sql.DataSource; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; /** * @描述:重写Queryrunner,避免try-catch的麻烦 * @author cxie * */ public class MyQueryRunner extends QueryRunner{ public MyQueryRunner(){} public MyQueryRunner(DataSource ds){ super(ds); } @Override public int[] batch(Connection conn, String sql, Object[][] params) { try { return super.batch(conn, sql, params); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public int[] batch(String sql, Object[][] params){ try { return super.batch(sql, params); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public void fillStatement(PreparedStatement stmt, Object... params){ try { super.fillStatement(stmt, params); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public void fillStatementWithBean(PreparedStatement stmt, Object bean, PropertyDescriptor[] properties){ try { super.fillStatementWithBean(stmt, bean, properties); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public void fillStatementWithBean(PreparedStatement stmt, Object bean, String... propertyNames){ try { super.fillStatementWithBean(stmt, bean, propertyNames); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override protected PreparedStatement prepareStatement(Connection conn, String sql){ try { return super.prepareStatement(conn, sql); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override protected Connection prepareConnection(){ try { return super.prepareConnection(); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public <T> T query(Connection conn, String sql, Object param, ResultSetHandler<T> rsh){ try { return super.query(conn, sql, rsh); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public <T> T query(Connection conn, String sql, Object[] params, ResultSetHandler<T> rsh){ try { return super.query(conn, sql, params, rsh); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params){ try { return super.query(conn, sql, rsh, params); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh){ try { return super.query(conn, sql, rsh); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public <T> T query(String sql, Object param, ResultSetHandler<T> rsh){ try { return super.query(sql, param, rsh); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public <T> T query(String sql, Object[] params, ResultSetHandler<T> rsh){ try { return super.query(sql, params, rsh); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params){ try { return super.query(sql, rsh, params); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public <T> T query(String sql, ResultSetHandler<T> rsh){ try { return super.query(sql, rsh); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override protected void rethrow(SQLException cause, String sql, Object... params){ try { super.rethrow(cause, sql, params); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public int update(Connection conn, String sql){ try { return super.update(conn, sql); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public int update(Connection conn, String sql, Object param){ try { return super.update(conn, sql, param); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public int update(Connection conn, String sql, Object... params){ try { return super.update(conn, sql, params); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public int update(String sql){ try { return super.update(sql); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public int update(String sql, Object param){ try { return super.update(sql, param); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override public int update(String sql, Object... params){ try { return super.update(sql, params); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override protected void close(Connection conn){ try { super.close(conn); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override protected void close(Statement stmt){ try { super.close(stmt); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } @Override protected void close(ResultSet rs){ try { super.close(rs); } catch (SQLException e) { throw new RuntimeException(e.getMessage(),e); } } } </span>
如此这般,就可以省去try-catch的麻烦了,由于篇幅的问题,这里就不写测试类了。
使用回调函数,简单模拟dbutils中Queryrunner的工作原理,并重写Queryrunner,使其使用起来更加简单方便