为什么要使用basedao以及源码解析

 

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;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值