Java学习笔记三:DButils的通用数据库方法解析

2 篇文章 0 订阅

DButils的通用数据库方法有两种,一种是用于增删改的Update方法,一种是用于查询的Query方法。

根据其通用性的思想,对update方法和query方法的底层实现进行解析。

根据DButils的两个方法思考,思路是:

1.传入带占位符(?)的数据库语句,如:insert into bank values(null,?,?)。

2.传入通用型的参数,并且传入的参数个数可变。

Update(增删改):

//通用的增删改
    /*
    * @param sql 需要操作的sql语句
    * @param args 可变参数,...表示有几个占位符就传入几个参数,args相当于一个可变数组
    * */
    public void update(String sql,Object ... args) throws SQLException {

        Connection conn = null;
        PreparedStatement ps = null;


        try {
            conn = JDBCutil02.getConn();
            ps = conn.prepareStatement(sql);

            for (int i=0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }

            ps.executeUpdate();
        } finally {
            JDBCutil02.release(ps,conn);
        }
    }

通过(Object ... name)(可变参数)的方式,可以传入任意个数的参数,并且Object类型可以接受任意类型的参数。

然后通过for循环,遍历args 数组,通过PrepareStatement的setObject方法,一个个为占位符赋值。

但是这样有隐患,那就是如果输入的参数个数与占位符不一样,遍历次数就会有问题,将会出错。

所以为了解决这个隐患,使用参数元数据 ParameterMetaData 来获取参数SQL中有多少个问号,通过计算占位符的数量,从而消除遍历次数不正确的问题。

优化后的Update:

 /*
    元数据:描述数据的数据
    * 优化版的update语句,防止可变参数数量传错,以问号个数为准
    * 通过 参数元数据ParameterMetaData 来获取参数中有多少个问号
    * */
    public void update02(String sql,Object ... args) throws SQLException {

        Connection conn = null;
        PreparedStatement ps = null;


        try {
            conn = JDBCutil02.getConn();
            ps = conn.prepareStatement(sql);

            //元数据
            //获取到有几个问号,占位符
            ParameterMetaData metaData = ps.getParameterMetaData();
            int count = metaData.getParameterCount();

            for (int i=0;i<count;i++){//以问号的数量来遍历
                ps.setObject(i+1,args[i]);
            }

            ps.executeUpdate();
        } finally {
            JDBCutil02.release(ps,conn);
        }
    }

Query的底层实现稍微上有点复杂,前两个问题与update一样,占位符个数和参数,稍有不同的时候,查询语句会返回结果集,并且数据库返回的结果集需要进行封装,还需要将封装的结果集返回,所以query的重点就在:

数据获取与数据封装

解决方法:由于我们不清楚具体要封装成什么对象,所以选择将结果集交给调用者自行去封装。

  1. 首先,先声明一个泛型接口(ResultSetHandler<T>),里面定义一个返回值为泛型(T),参数为ResultSet 的方法(T Handle(ResultSet rs)),也即是泛型方法。并且将query也定义为泛型方法,返回值为T。
  2. 然后在 query泛型方法中添加一个 ResultSetHandler 接口参数,即:(public <T> T query(String sql,ResultSetHandler<T> handler,Object ... args)
  3. 然后在 query 中调用参数 handler 的 handle 方法封装数据,并且获取封装好的数据(T t = (T) handler.Handle(rs);),最后返回封装好的数据。

使用方法

  • 通过匿名实现类的方式,实例化 query 中的ResultSetHandler 接口参数(指向实现类的引用,相当于接口的向上转型),并且实现其中的handle方法,在handle方法中对结果集(rs)进行封装,而封装的数据对象类型由调用者实例化接口时传入。如(new ResultSetHandler<Bank>(){}),最后返回封装好的数据对象类型。

具体代码:

ResultSetHandler接口:

import java.sql.ResultSet;
import java.sql.SQLException;

public interface ResultSetHandler<T> {
    /*
    * 定义了数据封装的规则。规范
    * */
    T Handle(ResultSet rs);
}

Query 方法:

/*查询
    可能出现的问题
    select * from aa
    select * from aa where id =?
    select * from aa where name=? and gender=?
    问题一:数据获取,以及数据封装成什么对象返回。因为调用的地方需要的数据不一样,数据封装问题
    解决方法:将结果集交给用户自行去封装,定义一个ResultSetHandler的接口,里面声明一个handle方法。
    然后query方法中添加一个ResultSetHandler接口参数,然后通过匿名实现类的方式实现handle方法,然后对其中的结果集数据进行封装。

    */
    public <T> T query(String sql,ResultSetHandler<T> handler,Object ... args){
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            conn = JDBCutil02.getConn();
            ps = conn.prepareStatement(sql);

            //元数据
            //获取到有几个问号,占位符
            ParameterMetaData metaData = ps.getParameterMetaData();
            int count = metaData.getParameterCount();

            for (int i=0;i<count;i++){//以问号的数量来遍历
                ps.setObject(i+1,args[i]);
            }

            //执行查询工作,然后得到结果集
            ResultSet rs =  ps.executeQuery();
            //将结果集给调用者,让它去封装数据,并且返回封装好的数据
           T t = (T) handler.Handle(rs);
           return t;
        }
        catch (Exception e){
            e.printStackTrace();
        }finally {
            JDBCutil02.release(ps,conn);
        }
        return null;
    }

Query 方法的调用:

/*
* ResultSetHandler参数其实等同于
* ResultSetHandler handle = new A();A是具体实现类,这里使用了匿名实现类的方式,指向实现类的引用,相当于接口的向上转型。
* */

   @Test
   public void testQuery(){
       Bank bank = query("select * from bank where id = ?", new ResultSetHandler<Bank>() {

           @Override
           public Bank Handle(ResultSet rs) {
               try {
                   Bank bank = new Bank();
                   while (rs.next()){
                       String name = rs.getString("name");
                       double money = rs.getDouble("money");

                       bank.setName(name);
                       bank.setMoney(money);
                   }
                   return bank;
               } catch (SQLException e) {
                   e.printStackTrace();
               }
               return null;
           }
       },3);

       System.out.println(bank);
   }

总结

其上基本上就是DButils框架的update和query方法的解析,其中update方法比较简单,因为只需要执行即可。

query方法的重点主要就是在数据的封装方面,如何做到可以通用型的封装数据以及返回通用型的数据。

通过泛型类型的方式,让调用者指定封装数据的对象类型,并且将数据封装的工作交给调用者来进行,而无论使用匿名实现类的方式在调用query方法的时候再定义封装方法,还是提前写好继承接口的类作为参数传入。

核心思想都是将我们无法确定的数据封装,数据对象类型,交给调用者来处理,从而达成定义通用方法的目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值