BaseDao接口的过人之处在于:一般是提供从数据库 增加、删除、修改记录、查询所有记录、查询符合某个条件记录、取得某条记录等方法的底层数据操作自定义类。
由于我们可能操作多个数据库表,这样就需要为每个表提供一个操作他的类 xxDAO, 这些DAO继承BaseDAO 就可以省略很多重复代码(从数据库 增加、删除、修改记录、查询所有记录、查询符合某个条件记录、取得某条记录等方法的代码)。
其次对于泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
Java语言引入泛型的好处是安全简单。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
泛型在使用中还有一些规则和限制:
1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
3、泛型的类型参数可以有多个。
4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上成为“有界类型”。
5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(java.lang.String);
一般在涉及DAO开发时,常用到的增删改查方法会封装到一个基类(BaseDAO),对于各个数据表的基本维护业务都需要用到增删改查等方法。
若对每张表都编写一套增删改差方法未必有些麻烦,并且不符合编码的基本准则。一般,我们可以将这些功能的所公用的部分封装为一个对象,或者是类,此类是所有DAO的基类,可以称为:BaseDAO。
由于此类接收到不同的操作对象,故需泛型的支持。
下面,我通过以下实例代码,为此知识进行展示,供大家参考
package cn.kgc.kb08.jdbc.dao;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Base:通用
* Dao:DataBase Access Object 数据库访问对象
*/
public class BaseDao {
private String url;
private String username;
private String password;
/**
* 构造方法:传入驱动和连接字符串
* @param driver 驱动字符串
* @param url 连接字符串
* @param username 数据库连接账号
* @param password 数据库连接密码
*/
public BaseDao(final String driver, String url, String username, String password) {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
System.out.println("数据库驱动装载失败,系统强制退出");
//关闭数据库
System.exit(-1);
}
this.url = url;
this.username = username;
this.password = password;
}
/**
* 自定义方法:获取连接对象
* @return 连接对象
* @throws SQLException
*/
private Connection getCon() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
/**
* 自定义方法:获取预编译执行对象
* @param con 连接对象
* @param SQL 数据库操作命令
* @param params 动态参数:预编译参数
* @return 预编译执行对象
* @throws SQLException
*/
private PreparedStatement getPst(Connection con,final String SQL,Object...params) throws SQLException {
PreparedStatement pst = con.prepareStatement(SQL);
if (null!=params && params.length>0){
for (int i = 0; i < params.length; i++) {
pst.setObject(i+1,params[i]);
}
}
return pst;
}
/**
* 自定义方法:执行增删改操作
* @param pst 执行对象
* @return int
* @throws SQLException
*/
private int update(PreparedStatement pst) throws SQLException {
return pst.executeUpdate();
}
/**
* 自定义方法:执行查询操作
* @param pst 执行对象
* @return 结果集
* @throws SQLException
*/
private ResultSet query(PreparedStatement pst) throws SQLException {
return pst.executeQuery();
}
/**
* 自定义方法:释放资源
* @param acs 动态参数:所有需要释放的资源(注意顺序)
*/
private void close(AutoCloseable...acs){
for (AutoCloseable ac : acs) {
if (null!=ac){
try {
ac.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 自定义方法:封装数据库的增删改操作
* @param SQL 数据库命令
* @param params 动态参数: 预编译命令参数
* @return int: -1:异常 0:失败 >0表示成功(多条操作)
*/
public int exeUpd(final String SQL,final Object...params){
int rst = 0;
Connection con = null;
PreparedStatement pst = null;
try {
con = getCon();
pst = getPst(con,SQL,params);
rst = update(pst);
} catch (SQLException e) {
rst = -1;
}finally {
close(pst,con);
}
return rst;
}
/**
* 自定义方法:封装查询单个值
* @param c 需要单个值的实际类型:基本类型的包装类型
* @param SQL 数据库命令
* @param params 动态参数:预编译参数值
* @param <T> 泛型
* @return T
*/
public <T> T exeSingle(Class<T> c,final String SQL,Object...params){
T t = null;
Connection con = null;
PreparedStatement pst = null;
ResultSet rst = null;
try {
con = getCon();
pst = getPst(con,SQL,params);
rst = query(pst);
if (null!=rst && rst.next()){
t = c.getConstructor(String.class).newInstance(rst.getObject(1).toString());
}
} catch (Exception e) {
e.printStackTrace();
}finally {
close(rst,pst,con);
}
return t;
}
/**
* 自定义方法:反射解析实体参数类型的set方法,并以属性名为键,以方法对象为值进行存储
* @param c 实体类的实际类型
* @return HashMap<String, Method>
*/
private HashMap<String, Method> parseMethod(Class c) throws Exception{
HashMap<String, Method> mapMethod = new HashMap<>();
final String PREFIX = "set";
for (Method method : c.getDeclaredMethods()) {
String name = method.getName();
if (!name.startsWith(PREFIX)){
continue;
}
name = name.substring(3);
name = name.substring(0,1).toLowerCase()+name.substring(1);
mapMethod.put(name,method);
}
return mapMethod;
}
/**
* 自定义方法:解析结果集携带的表结构(表字段名称)
* @param md 结构对象
* @return Stirng[]
* @throws SQLException
*/
private String[] parseStruct(ResultSetMetaData md) throws SQLException {
String[] names = new String[md.getColumnCount()];
for (int i = 0; i < names.length; i++) {
names[i] = md.getColumnLabel(i+1);
}
return names;
}
/**
* 自定义方法:封装查询多行多列
* @param c 需要实际类型(实体自定义类型)
* @param SQL 数据库命令
* @param params 动态参数:预编译参数值
* @param <T> 泛型
* @return List<T>
*/
public <T> List<T> exeQuery(Class<T> c,final String SQL,Object...params){
List<T> list = null;
Connection con = null;
PreparedStatement pst = null;
ResultSet rst = null;
try {
con = getCon();
pst = getPst(con,SQL,params);
rst = query(pst);
if (null!=rst && rst.next()){
list = new ArrayList<>();
Map<String,Method> map = parseMethod(c);
String[] names = parseStruct(rst.getMetaData());
do{
T t = c.newInstance();
for (String name : names) {
map.get(name).invoke(t,rst.getObject(name));
}
list.add(t);
}while (rst.next());
}
} catch (Exception e) {
e.printStackTrace();
}finally {
close(rst,pst,con);
}
return list;
}
}