JDBC(七)操作模板:JdbcTemplate

怎么做重构:

思想:大家来找茬.
两段功能相同的代码,发现他们的共同点,提取出去(把操作模板提出取出).

把共同的代码提取到:
1:可以提取到父类中--->不推荐,容易导致子类爆炸.
2:使用组合方式,推荐.

抽取JdbcTemplate的重要性:
1):凡是搞过JDBC,2~3年的程序员,都可以轻松的写出JdbcTemplate.
2):学习,如何做重构.
3):类似于dbutils组件/Spring JdbcTemplate.
4):之后做的高级查询+分页+深入重构设计,都会基于JdbcTemplate来.
5):模拟Hibernate.

没有操作模板的写法

Paste_Image.png

DML操作模板

JdbcTemplate

public class JdbcTemplate {
    
    public static int update(String sql,Object... params){
        Connection conn=null;
        PreparedStatement ps=null;
        try {
            conn=JdbcUtil.getConn();
            ps=conn.prepareStatement(sql);
            //设置占位参数
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1, params[i]);
            }
            return ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JdbcUtil.close(conn, ps, null);
        }
        return 0;
    }
}

test

public class JdbcTemplateTest {
    
    
    public void save(Student stu){
        String sql = "INSERT INTO t_student (NAME, age) VALUES (?,?);";
        Object[] params={stu.getName(),stu.getAge()};
        JdbcTemplate.update(sql, params);
    }
    
    
    public void delete(Long id){
        String sql = "DELETE FROM t_student WHERE id = ?";
        JdbcTemplate.update(sql, id);
    }
    
    public void update(Student stu){
        String sql = "UPDATE t_student SET name = ?,age= ? WHERE id =?";
        Object[] params={stu.getName(),stu.getAge(),stu.getId()};
        JdbcTemplate.update(sql, params);
    }

}

DQL操作模板

第一个版本

public static List<Student> query(String sql,Object... params){
        List<Student> list = new ArrayList<Student>();
        Connection conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {
            conn=JdbcUtil.getConn();
            ps=conn.prepareStatement(sql);
            //设置占位参数
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1, params[i]);
            }
            rs= ps.executeQuery();
            while(rs.next()){
                Student stu = new Student();
                stu.setName(rs.getString("name"));
                stu.setAge(rs.getInt("age"));
                stu.setId(rs.getLong("id"));
                list.add(stu);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JdbcUtil.close(conn, ps, null);
        }
        return list;
    }
public  Student getSingle(Long id){
        String sql = "SELECT * FROM t_student WHERE id = ?";
        List<Student> list=JdbcTemplate.query(sql, id);
        return list.size()==1?list.get(0):null;
    }
    
    
    public  List<Student> list(Long id){
        String sql = "SELECT * FROM t_student WHERE";
        return JdbcTemplate.query(sql);
    }

上面的代码:从查询学生对象上来说,没有一点问题.
但是:因为在JdbcTemplate的query方法中,写死了Student和t_student表的三个列---->该query方法只能操作当前的Student对象和t_student表.是不能操作Teacher对象/t_teacher表的.

第二个版本
不同的对象(不同的表),不同的表有不同的列,列都不同了,处理结果集的代码就不相同.
只能说:每一个DAO的查询方法都应该要处理结果集,但是怎么处理,只有各自的DAO才知道.
也就是说:处理结果集的行为,不应该作为模板中的代码,而是应该交给给自的DAO来完成,因为给自的DAO才知道各自表的列有哪一些.

规定:所有的处理结果集的方法都叫做handle(在Java中做规范就是接口).
IResultSetHandler

public interface IResultSetHandler {
    
    List handle(ResultSet rs) throws SQLException;

}

StudentResultSetHandler

public class StudentResultSetHandler implements IResultSetHandler {

    public List handle(ResultSet rs) throws SQLException {
        List<Student> list=new ArrayList<Student>();
        while(rs.next()){
            Student stu = new Student();
            stu.setName(rs.getString("name"));
            stu.setAge(rs.getInt("age"));
            stu.setId(rs.getLong("id"));
            list.add(stu);
        }
        return list;
    }

}

查询模板

public static List query(String sql,IResultSetHandler handler,Object... params){
        
        Connection conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {
            conn=JdbcUtil.getConn();
            ps=conn.prepareStatement(sql);
            //设置占位参数
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1, params[i]);
            }
            rs= ps.executeQuery();
            return handler.handle(rs);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JdbcUtil.close(conn, ps, null);
        }
        return new ArrayList();
    }

具体查询调用

public  List<Student> list(Long id){
        String sql = "SELECT * FROM t_student";
        List<Student> list=JdbcTemplate.query(sql,new StudentResultSetHandler());
        return list;
    }

第三个版本
第二个操作模板:query方法,还不太高级.如果要查询结果总数:

SELECT COUNT(id) FROM t_student; 

应该返回一个整数,而不是集合。也就说:处理结果只会,到底应该返回什么类型,其实是不确定的,不同调用者的返回类型是不一样的.不能使用List就以偏概全.
解决方案:使用泛型.

public interface IResultSetHandler2<T> {
    
    T handle(ResultSet rs) throws SQLException;
}
public class StudentResultSetHandler2 implements IResultSetHandler2<List<Student>>{

    public List<Student> handle(ResultSet rs) throws SQLException {
        List<Student> list=new ArrayList<Student>();
        while(rs.next()){
            Student stu = new Student();
            stu.setName(rs.getString("name"));
            stu.setAge(rs.getInt("age"));
            stu.setId(rs.getLong("id"));
            list.add(stu);
        }
        return list;
    }
}
//查询全部对象
    public  List<Student> list2(){
        String sql = "SELECT * FROM t_student";
        List<Student> list=JdbcTemplate.query2(sql,new StudentResultSetHandler2());
        System.out.println("count:"+list.size());
        return list;
    }
    
    
    //查询数量
    public  void list3(){
        String sql = "SELECT COUNT(id) FROM t_student";
        Long count=JdbcTemplate.query2(sql,new IResultSetHandler2<Long>(){

            public Long handle(ResultSet rs) throws SQLException {
                if(rs.next()){
                    return rs.getLong(1);
                }
                return 0L;
            }
        });
        System.out.println("count:"+count);
    }

第四个版本
在第三个版本的基础上,但是冗余了具体的databean,可以再抽象:

模拟Hibernate

public class Hibernate {
    
    
    public static void save(Object obj){
        try {
            //获取对象对应的表名
            String tableName=obj.getClass().getSimpleName();
            Table table=obj.getClass().getAnnotation(Table.class);
            if(table!=null){
                tableName=table.getValue();
            }
            StringBuilder sql=new StringBuilder();
            sql.append("INSERT INTO").append(tableName).append("(");
            StringBuilder columnSql=new StringBuilder(); //拼接需要插入哪些列表的sql:
            StringBuilder placeHolderSql=new StringBuilder();//拼接占位符的SQL:??
            List<Object> params=new ArrayList<Object>();  //参数
            BeanInfo beanInfo=Introspector.getBeanInfo(obj.getClass(),Object.class);
            PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor pd : pds) {
                //对象中的属性名
                String propertyName=pd.getName();
                if("id".equals(propertyName)){
                    columnSql.append(propertyName).append(",");
                    placeHolderSql.append("?").append(",");
                    //获取属性的值,调用属性的getter方法
                    Object val=pd.getReadMethod().invoke(obj);
                    params.add(val);
                }
            }
            //删除最后一个
            columnSql.deleteCharAt(columnSql.length()-1);
            placeHolderSql.deleteCharAt(placeHolderSql.length()-1);
            sql.append(columnSql);
            sql.append(") VALUES (");
            sql.append(placeHolderSql);
            sql.append(")");
            System.out.println("SQL= "+sql);
            System.out.println("params= "+params);
            JdbcTemplate.update(sql.toString(), params.toArray());
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容