一、dbutils的核心就是回调函数,可以说如果没有回调函数的思想,dbutils是不可能被开发出来的。
对于dbutils中的QuryRunner类,向该类的query方法提供不同的参数,可以得到不同类型的返回值类型,但是该方法并非是重载方法,这里借助回调函数和泛型可以实现和重载方法相同的效果,而且灵活性更高。
二、简单回调函数结构。
1.首先需要一个处理句柄的顶级接口,这是回调规范。
interface RunnerHandler<T> { T handler(String str); }
2.需要一个类似于QueryRunner类的运行类,这是调用类
class Runner { public <T> T query(String str,RunnerHandler<T> rh) { return rh.handler(str); } }
3.一个测试类。
public class CallBackDemo { public static void main(String args[]) { Runner run=new Runner(); run.query("你好",new RunnerHandler<List<Map<String,Object>>>(){ @Override public List<Map<String, Object>> handler(String str) { System.out.println(str); return null; } }); } }
4.运行结果
你好
5.疑问:废了这么大的劲,为什么不直接在测试代码中进行打印输出呢?
实际上打印输出在这里只是一个举例,能干的事情不仅仅是打印输出,虽然只是一个简单的打印输出,但是已经能够在一定程度上说明回调函数是怎么回事儿了。
三、模拟dbutils
0.c3p0数据库连接池工具。
1 package day17.regular.utils; 2 /** 3 * 使用c3p0创建的连接池。 4 */ 5 import java.sql.Connection; 6 import java.sql.SQLException; 7 8 import javax.sql.DataSource; 9 10 import com.mchange.v2.c3p0.ComboPooledDataSource; 11 12 public class DataSourceUtils_C3P0 { 13 private static DataSource ds=null; 14 static{ 15 ds=new ComboPooledDataSource("namedconfig"); 16 } 17 public static Connection getConnection(){ 18 Connection conn=null; 19 try { 20 conn=ds.getConnection(); 21 } catch (SQLException e) { 22 e.printStackTrace(); 23 } 24 return conn; 25 } 26 public static DataSource getDataSource(){ 27 return ds; 28 } 29 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <c3p0-config> 3 <!-- 默认配置,只可以出现一次 --> 4 <default-config> 5 <!-- 连接超时设置30秒 --> 6 <property name="checkoutTimeout">30000</property> 7 <!-- 30秒检查一次connection的空闲 --> 8 <property name="idleConnectionTestPeriod">30</property> 9 <!--初始化的池大小 --> 10 <property name="initialPoolSize">2</property> 11 <!-- 最多的一个connection空闲时间 --> 12 <property name="maxIdleTime">30</property> 13 <!-- 最多可以有多少个连接connection --> 14 <property name="maxPoolSize">10</property> 15 <!-- 最少的池中有几个连接 --> 16 <property name="minPoolSize">2</property> 17 <!-- 批处理的语句--> 18 <property name="maxStatements">50</property> 19 <!-- 每次增长几个连接 --> 20 <property name="acquireIncrement">3</property> 21 <property name="driverClass">com.mysql.jdbc.Driver</property> 22 <property name="jdbcUrl"> 23 <![CDATA[jdbc:mysql://10.6.112.200:3306/test?useUnicode=true&characterEncoding=UTF-8]]> 24 </property> 25 <property name="user">root</property> 26 <property name="password">5a6f38</property> 27 </default-config> 28 29 <named-config name="namedconfig"> 30 <!-- 连接超时设置30秒 --> 31 <property name="checkoutTimeout">30000</property> 32 <!-- 30秒检查一次connection的空闲 --> 33 <property name="idleConnectionTestPeriod">30</property> 34 <!--初始化的池大小 --> 35 <property name="initialPoolSize">2</property> 36 <!-- 最多的一个connection空闲时间 --> 37 <property name="maxIdleTime">30</property> 38 <!-- 最多可以有多少个连接connection --> 39 <property name="maxPoolSize">4</property> 40 <!-- 最少的池中有几个连接 --> 41 <property name="minPoolSize">2</property> 42 <!-- 批处理的语句--> 43 <property name="maxStatements">50</property> 44 <!-- 每次增长几个连接 --> 45 <property name="acquireIncrement">2</property> 46 <property name="driverClass">com.mysql.jdbc.Driver</property> 47 <property name="jdbcUrl"> 48 <![CDATA[jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8]]> 49 </property> 50 <property name="user">root</property> 51 <property name="password">5a6f38</property> 52 </named-config> 53 </c3p0-config>
1.首先定义一个接口ResultSetHandler,该接口是BeanListHandler等类的父接口,借口中定义了唯一一个方法handler();(调用规范)
public interface ResultSetHandler<T> { public T handler(ResultSet rs); }
2.自定义QueryRunner类(调用类)
package day17.kdyzm.CallBack; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.sql.DataSource; public class QueryRunner { private DataSource ds; public QueryRunner(DataSource ds){ this.ds=ds; } public <T> T query(String sql,ResultSetHandler<T> rsh) { T t=null; Connection conn=null; try { conn=ds.getConnection(); Statement st=conn.createStatement(); ResultSet rs=st.executeQuery(sql); t=rsh.handler(rs); } catch (SQLException e) { e.printStackTrace(); } finally{ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } return t; } }
3.定义javabean
package day17.kdyzm.CallBack; public class Person { private String id; private String name; private int age; private String sex; public Person() { } public Person(String id, String name, int age, String sex) { super(); this.id = id; this.name = name; this.age = age; this.sex = sex; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { //这里一旦改成int,则反射的时候就找不到方法了,该怎么修改才能解决这个问题?使用返回值类型得到set方法中的参数类型 this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + "]"; } }
4.定义一个具体类用于实现ResultSetHandler接口。这里使用BeanListHandler,返回值一定是List,但是泛型参数类型不确定,直到给出确切的泛型类型。
package day17.kdyzm.CallBack; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; /** * 实现了ResultSetHandler接口的实现类。 * @author kdyzm * */ public class BeanListHandler<T> implements ResultSetHandler<List<T>>{ private Class<T> clazz; public BeanListHandler(Class<T>clazz){ this.clazz=clazz; } @Override public List<T> handler(ResultSet rs) { List<T>list=new ArrayList<T>(); try { ResultSetMetaData rsdm=rs.getMetaData(); int columnNum=rsdm.getColumnCount(); while(rs.next()) { T t=clazz.newInstance(); for(int i=0;i<columnNum;i++) { String columnName=rsdm.getColumnName(i+1); String methodName="set"+columnName.substring(0,1).toUpperCase()+ columnName.substring(1).toLowerCase(); String methodName_p="get"+columnName.substring(0,1).toUpperCase()+ columnName.substring(1).toLowerCase(); // String classType=rsdm.getColumnClassName(i+1);//通过这种方式获得的返回类型有问题。 Method method_p=clazz.getMethod(methodName_p); Method method=clazz.getMethod(methodName, method_p.getReturnType()); Object obj=rs.getObject(columnName); method.invoke(t, obj); } list.add(t); } } catch (SQLException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return list; } }
5.测试
package day17.kdyzm.CallBack; import java.util.List; import javax.sql.DataSource; import day17.regular.utils.DataSourceUtils_C3P0; /** * 测试回调函数的测试类 * @author kdyzm * */ public class Test { public static void main(String[] args) { DataSource ds=DataSourceUtils_C3P0.getDataSource(); QueryRunner run=new QueryRunner(ds); String sql="select * from people"; List<Person>list=run.query(sql, new BeanListHandler<Person>(Person.class)); for(Person p:list) { System.out.println(p); } System.out.println("回调函数开发成功!"); } }
6.运行结果
c3p0数据库初始化日志信息略; Person [id=001, name=张三, age=12, sex=男] Person [id=002, name=李四, age=13, sex=男] Person [id=003, name=王五, age=15, sex=男] 回调函数开发成功!