在 web 开发中,我们需要用 dao 从数据库中查询数据,此时我们可以定义一个 BaseDao, 就是用于做一些增删改查的基础 DAO, 其后的其他的具体 DAO, 只需要继承这个 DAO, 然后再根据具体的业务逻辑去写具体方法就行,实现代码重用.
这是增删改查的基础代码,利用 DBUtils 写的.
下面是具体的代码,我一一对其解释.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| package com.xxx.dao;
import com.xxx.utils.JDBCUtils; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.sql.Connection; import java.sql.SQLException; import java.util.List;
public class BaseDao<T> {
private QueryRunner runner = new QueryRunner(); private Class<T> type;
public BaseDao () { Class<? extends BaseDao> clz = this.getClass(); System.out.println("BaseDao构造器被执行, clz为: " + clz);
Type genericSuperclass = clz.getGenericSuperclass(); System.out.println("带泛型的父类类型实际类型为: " + genericSuperclass.getClass());
ParameterizedType p = (ParameterizedType) genericSuperclass; Type[] types = p.getActualTypeArguments(); type = (Class<T>) types[0]; }
public T getBean (String sql, Object... params) { T t = null; Connection conn = JDBCUtils.getConnection(); try { t = runner.query(conn, sql, new BeanHandler<T>(type), params); } catch ( SQLException e ) { e.printStackTrace(); } finally { JDBCUtils.releaseConnection(conn); } return t; }
public List<T> getBeanList (String sql, Object... params) { List<T> list = null; Connection connection = JDBCUtils.getConnection(); try { list = runner.query(connection, sql, new BeanListHandler<T>(type), params); } catch ( SQLException e ) { e.printStackTrace(); } finally { JDBCUtils.releaseConnection(connection); } return list; }
public int update (String sql, Object... params) { int count = 0; Connection conn = JDBCUtils.getConnection(); try { count = runner.update(conn, sql, params); } catch ( SQLException e ) { e.printStackTrace(); } finally { JDBCUtils.releaseConnection(conn); } return count; } }
|
其中 udpate 方法,可以实现增删改.
查询方法,单独查询一个对象和查询一组对象.
要说的部分在查询这里。在 BeanListHandler 和 BeanHandler 这里后面都有一个 type 参数传到了构造器中,这个参数就是具体要查询类的 Bean 对象。由于我们这里是在 BaseDao 里无法写具体的子类 Class, 所以我们先 private Class<T> type;
声明了一个 type 给他传递了一个引用.
那么我们这个 BaseDao 被子类继承后,如何传递这个具体的 bean 对象过来呢.
就是说在这个 Dao 的查询方法里,他怎么知道查询后的数据赋给哪个 bean 对象?
本文重点要说的就是这里了.
我们将 BaseDao 传递一个泛型参数,写成: public class BaseDao<T>
然后我们将具体的子类对象通过泛型传递过来,接着在构造器中获取具体的泛型类型.
解释一下这个构造器的作用.
首先来说一下,这个 BaseDao 的构造器会在何时执行.
在 JAVA 基础那里我们知道,在初始化子类之前会初始化父类的构造器,在子类中,我们也是把 super () 语句写在子类构造器的第一行,且 java 规定必须是第一行,这样的目的就是让子类在初始化之前确保父类被初始化,
而这个 super () 就是调用父类的构造器,所以在BaseDao这个构造器中的this, 它的指针是指向的具体实现类的子类引用.
那么 this.getClass () 获取到的就是子类的类型.
其次,我们再通过反射获取这个子类的父类类型,毫无疑问它的父类肯定就是这个 BaseDao.
由于这个 BaseDao 是带泛型的,所以我们应该调用 clz.getGenericSuperclass();
这个方法,而不是调用 clz.superClass();
获取到了带泛型的父类之后,由于这个泛型真实类型是参数化泛型,所以我们还需要强转成 ParameterizedType p = (ParameterizedType) genericSuperclass;
.
接下来,Type[] types = p.getActualTypeArguments();
这个方法开始真正的获取泛型列表了,因为我们知道泛型可以写多个,所以这里返回值是一个数组。但我们这里只有一个泛型,
所以 type = (Class<T>) types[0];
直接获取数组下标为 0 那个泛型就可以.
经过这一些列的获取,我们就可以拿到传递过来的具体子类 Bean 对象.
然后之前通过 private Class<T> type;
声明的 type 就被赋值为子类的类型,就可以用于 BeanListHandler 和 BeanHandler 使用了.
1 2 3 4 5
| Class<? extends BaseDao> clz = this.getClass(); System.out.println("BaseDao构造器被执行, clz为: " + clz);
Type genericSuperclass = clz.getGenericSuperclass(); System.out.println("带泛型的父类类型实际类型为: " + genericSuperclass.getClass());
|
上面 2 条语句的输出为:
BaseDao 构造器被执行,clz 为: class com.xxx.dao.impl.UserDaoImpl
带泛型的父类类型实际类型为: class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl