黑猴子的家:JDBC -> 自己封装通用的增删改查工具

1、MyCRUDUtils 工具

package com.yinggu.utils;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

 * 此类用于演示封装通用的增删改查工具方法
 * 
 * @author:黑猴子的家
 * @博客 :https://www.jianshu.com/u/37fd8e2dff4c
 * 
 * 功能: 1、封装通用的增删改 
 *             (1)适合任何的增删改sql语句 
 *             (2)适合任何表
 *       2、通用的查询 
 *             (1)适合任何的查询sql语句
 *             (2)适合任何表

public class MyCRUDUtils {
    /**
     * 功能:通用的查询多条记录
     * 
     * @param sql
     *            待查询的sql
     * @param clazz
     *            class对象
     * @param objects
     *            占位符
     * @return 单条对象
     * @throws Exception
     */
    public static <T> List<T> queryMulti(
                    String sql, Class<T> clazz, 
                    Object... objects) throws Exception {
        // 1.获取连接
        Connection connection = JDBCUtils.getConnection();
        // 2.执行sql
        // 2-1 获取预编译命令对象
        PreparedStatement statement = connection.prepareStatement(sql);
        // 2-2 设置占位符
        for (int i = 0; i < objects.length; i++) {
            statement.setObject(i + 1, objects[i]);
        }
        // 2-3 执行
        ResultSet set = statement.executeQuery();
        // 获取元数据结果集对象
        ResultSetMetaData metaData = set.getMetaData();
        List<T> list = new ArrayList<>();
        while (set.next()) {
            // 1.通过反射创建对应类型的对象
            T t = clazz.newInstance();
            /*
             * 需求: 
             * 封装该条记录的对象,并为该位对象的属性赋值,然后返回
             * 反射的步骤:
             * 1.通过反射创建对应类型的对象 
             * T t = clazz.newInstance();
             * 
             * 2.为属性赋值 遍历里面每一个属性:?列数、列名
             * 
             * 假如,知道每一个列名(属性名) for(int i=1;i<=列数;i++){ //获取列名?
             * Field field = clazz.getDeclaredFiled(属性名);
             * field.setAccessible(true); field.set(t,set.get(列索引或列名)); }
             * 3.返回该对象 return t;
             */

            // 获取结果集的列数
            int columnCount = metaData.getColumnCount();
            for (int i = 1; i <= columnCount; i++) {// 遍历每个列(每个属性)
                // 2.为属性赋值
                // 获取列名?
                String columnName = metaData.getColumnLabel(i);
                // clazz.getDeclaredField(columnName);  
                //实体类和数据库,大小写最好一样,java是区分大小写的
                //如果不一样, sql语句要起别名
                Field field = clazz.getDeclaredField(columnName);
                field.setAccessible(true);
                field.set(t, set.getObject(columnName));
            }
            list.add(t);
        }
        JDBCUtils.closeConnection(set, statement, connection);
        return list;
    }

    /**
     * 功能:通用的查询单条记录
     * orm  ->  object  relation  mapping
     * 对象关系映射
     * 
     * 表的设计  ->  java 中类的设计
     * 表中的每一条记录,相当于java中的一个对象
     * 表中的每一个字段,相当于java中的每一个属性
     * 
     * 需求
     * 封装查询的单条记录的对象,并为该对象的属性赋值,然后返回
     * 
     * @param sql
     *            待查询的sql
     * @param clazz
     *            class对象
     * @param objects
     *            占位符
     *            
     * <T> T  泛型
     * @return 单条对象
     * @throws Exception
     */
    public static <T> T querySingle(
            String sql, Class<T> clazz, 
            Object... objects) throws Exception {
        // 1.获取连接
        Connection connection = JDBCUtils.getConnection();
        // 2.执行sql
        // 2-1 获取预编译命令对象
        PreparedStatement statement = connection.prepareStatement(sql);
        // 2-2 设置占位符
        for (int i = 0; i < objects.length; i++) {
            statement.setObject(i + 1, objects[i]);
        }
        // 2-3 执行
        ResultSet set = statement.executeQuery();
        // 获取元数据结果集对象
        ResultSetMetaData metaData = set.getMetaData();
        // 1.通过反射,创建对应类型的对象
        T t = clazz.newInstance();
        if (set.next()) {
            /*
             * 反射的步骤
             * 需求: 封装该条记录的对象,并为该位对象的属性赋值,然后返回
             * 反射的步骤:
             * 1.通过反射创建对应类型的对象 
             * T t = clazz.newInstance();
             * 
             * 2.为属性赋值 遍历里面每一个属性:?列数、列名
             * 假如,知道每一个列名(属性名) for(int i=1;i<=列数;i++){ //获取列名?
             * 
             * Field field = clazz.getDeclaredFiled(属性名);
             * field.setAccessible(true); 
             * field.set(t,set.get(列索引或列名)); }
             * 
             * 3.返回该对象 return t;
             * 
             */
            // 获取结果集的列数
            int columnCount = metaData.getColumnCount();
            for (int i = 1; i <= columnCount; i++) {// 遍历每个列(每个属性)
                // 2.为属性赋值
                // 获取列名或者属性名
                String columnName = metaData.getColumnLabel(i);
                Field field = clazz.getDeclaredField(columnName);
                //得到or激活
                field.setAccessible(true);
                //赋值
                field.set(t, set.getObject(columnName));
            }
        }
        JDBCUtils.closeConnection(set, statement, connection);
        return t;
    }

    /**
     * 功能:通用的增删改 注意:连接不用关闭
     * 
     * @param connection
     *            使用的连接对象
     * @param sql
     *            待执行的sql语句
     * @param objects
     *            占位符的参数列表
     * @return 受影响的行数
     * @throws Exception
     */
    public static int update(
            Connection connection, 
            String sql, Object... objects) throws Exception {
        // 2.执行
        // 2-1.获取预编译命令对象
        PreparedStatement statement = connection.prepareStatement(sql);
        // 2-2.设置占位符
        for (int i = 0; i < objects.length; i++) {
            statement.setObject(i + 1, objects[i]);
        }
        // 2-3.执行
        int update = statement.executeUpdate();
        // 3.关闭
        JDBCUtils.closeConnection(null, statement, null);
        return update;
    }

    /**
     * 功能:通用的增删改
     * 
     * @param sql
     *            待执行的sql语句
     * @param objects
     *            占位符的参数列表
     * @return 受影响的行数
     * @throws Exception
     */
    public static int update(String sql, Object... objects) throws Exception {
        // 1.获取连接
        Connection connection = JDBCUtils.getConnection();
        // 2.执行
        // 2-1.获取预编译命令对象
        PreparedStatement statement = connection.prepareStatement(sql);
        // 2-2.设置占位符
        for (int i = 0; i < objects.length; i++) {
            statement.setObject(i + 1, objects[i]);
        }
        // 2-3.执行
        int update = statement.executeUpdate();
        // 3.关闭
        JDBCUtils.closeConnection(null, statement, connection);
        return update;
    }
}

2、ResultSetMetaData类

(1)可用于获取关于 ResultSet 对象中列的类型和属性信息的对象
(2)ResultSetMetaData meta = rs.getMetaData();
           getColumnName(int column):获取指定列的名称
           getColumnLabel(int column):获取指定列的别名
           getColumnCount():返回当前 ResultSet 对象中的列数
           getColumnTypeName(int column):检索指定列的数据库特定的类型名称            getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位
           isNullable(int column):指示指定列中的值是否可以为 null
           isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的

3、ExamStudent 实体类

package com.yinggu.bean;
public class ExamStudent {
    private int FlowID;
    private int Type;
    private String IDCard;
    private String ExamCard;
    private String StudentName;
    private String Location;
    private int Grade;
    @Override
    public String toString() {
        return "ExamStudent [FlowID=" + FlowID +
                          ", Type=" + Type + 
                          ", IDCard=" + IDCard + 
                          ", ExamCard=" + ExamCard+ 
                          ", StudentName=" + StudentName + 
                          ", Location=" + Location + 
                          ", Grade=" + Grade + "]";
    }
    public ExamStudent() {
        super();
    }
    public ExamStudent(
                        int flowID, 
                        int type, 
                        String iDCard, 
                        String examCard, 
                        String studentName, 
                        String location,
                        int grade) {
        super();
        FlowID = flowID;
        Type = type;
        IDCard = iDCard;
        ExamCard = examCard;
        StudentName = studentName;
        Location = location;
        Grade = grade;
    }
    public int getFlowID() {
        return FlowID;
    }
    public void setFlowID(int flowID) {
        FlowID = flowID;
    }
    public int getType() {
        return Type;
    }
    public void setType(int Type) {
        this.Type = Type;
    }
    public String getIDCard() {
        return IDCard;
    }
    public void setIDCard(String iDCard) {
        IDCard = iDCard;
    }
    public String getExamCard() {
        return ExamCard;
    }
    public void setExamCard(String examCard) {
        ExamCard = examCard;
    }
    public String getStudentName() {
        return StudentName;
    }
    public void setStudentName(String studentName) {
        StudentName = studentName;
    }
    public String getLocation() {
        return Location;
    }
    public void setLocation(String location) {
        Location = location;
    }
    public int getGrade() {
        return Grade;
    }
    public void setGrade(int grade) {
        Grade = grade;
    }
}

4、使用 MyCRUDUtils 工具做查询练习

(1) 单个查询

package com.yinggu.demo3;

import java.util.List;
import org.junit.Test;
import com.yinggu.bean.ExamStudent;
import com.yinggu.utils.MyCRUDUtils;

 * @author:黑猴子的家
 * @博客 :https://www.jianshu.com/u/37fd8e2dff4c

public class TestCRUD {

    /**
     * 查询单个
     * @throws Exception
     */
    @Test
    public void testSelectSingle() throws Exception {
        ExamStudent examstudent = MyCRUDUtils.querySingle(
              "select * from examstudent where flowid = ? ",
                ExamStudent.class, 1);
        System.out.println(examstudent);
    }

}

(2) 多个查询

package com.yinggu.demo3;

import java.util.List;
import org.junit.Test;
import com.yinggu.bean.ExamStudent;
import com.yinggu.utils.MyCRUDUtils;

public class TestCRUD {
    /**
     * 查询多个
     * @throws Exception
     */
    @Test
    public void testSelectMulti() throws Exception {
        List<ExamStudent> list = MyCRUDUtils.queryMulti(
              "select * from examstudent where flowid >= ? ", ExamStudent.class,1);
        for (ExamStudent examStudent : list) {
            System.out.println(examStudent);
        }
    }
}

5、改写PreparedStatement 查询操作

使用MyCRUDUtils工具改写PreparedStatement 查询练习

package com.yinggu.demo3;
import java.util.Scanner;
import org.junit.Test;
import com.yinggu.bean.ExamStudent;
import com.yinggu.utils.MyCRUDUtils;

 * 使用PreparedStatement实现增删改查 使用MyCRUDUtils
 * @author:黑猴子的家
 * @博客 :https://www.jianshu.com/u/37fd8e2dff4c

public class TestPreparedStatementByUtils {
    Scanner input = new Scanner(System.in);
    /*
     * 此方法未使用 不用看, 看最后边两个方法
     */
    @Test
    public void testSelect() throws Exception {
        System.out.println("请选择要输入的类型");
        System.out.print("a、身份证号");
        System.out.print("b、准考证号");
        char key = input.next().charAt(0);
        switch (key) {
        case 'a':
            selectByIdCard();
            break;
        case 'b':
            selectByExamCard();
            break;
        default:
            break;
        }
    }

    /**
     * 按准考证查询
     * @throws Exception
     */
    private void selectByExamCard() throws Exception {
        System.out.println("请输入准考证号:");
        String examCard = input.next();
        String sql = "select * from examstudent where examCard=?";
        ExamStudent student = MyCRUDUtils.querySingle(
            sql, ExamStudent.class, examCard);
        System.out.println(student);
    }

    /**
     * 按身份证号查询 使用 了 MyCRUDUtils
     * @throws Exception
     */
    private void selectByIdCard() throws Exception {
        System.out.println("请输入身份证号:");
        String idCard = input.next();
        // ------------------------以下为连接数据库的步骤-------------------
        String sql = "select * from examstudent where idcard=?";
        ExamStudent student = MyCRUDUtils.querySingle(
            sql, ExamStudent.class, idCard);
        System.out.println(student);
    }

    /**
     * 使用了MyCRUDUtils
     * @throws Exception
     */
    @Test
    public void testInsert() throws Exception {
        System.out.println("请输入考生的详细信息");
        System.out.print("Type:");
        int type = input.nextInt();
        System.out.print("IDCard:");
        String idCard = input.next();
        System.out.print("ExamCard:");
        String examCard = input.next();
        System.out.print("StudentName:");
        String studentName = input.next();
        System.out.print("location:");
        String location = input.next();
        System.out.print("Grade:");
        int grade = input.nextInt();
        // ------------------------以下为连接数据库的步骤-------------------
        String sql = "insert into examstudent values(null,?,?,?,?,?,?)";
        int update = MyCRUDUtils.update(
             sql, type, idCard, examCard, studentName, location, grade);
        System.out.println(update > 0 ? "succss" : "failure");
    }
}

6、优化MyCrudUtils

尖叫提示:优化 LSMyCrudUtils 由刘松提供

package com.alex.utils;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

public class LSMyCrudUtils {
    //查询多个对象
    public static <T> List<T> queryList(String sql, Class<T> clazz, Object... objects) throws Exception {
        return performDbOperation(sql, objects).apply(statement -> {
            try (ResultSet set = statement.executeQuery()) {
                List<T> list = new ArrayList<>();
                while (set.next()) {
                    list.add(assembleObject(clazz, set));
                }
                return list;
            }
        });
    }
    //查询单个对象
    public static <T> T querySingle(String sql, Class<T> clazz, Object... objects) throws Exception {
        return performDbOperation(sql, objects).apply(statement -> {
            try (ResultSet set = statement.executeQuery()) {
                if (set.next()) {
                    return assembleObject(clazz, set);
                }
                return null;
            }
        });
    }

    //增删改方法
    public static int update(String sql, Object... objects) throws Exception {
        return performDbOperation(sql, objects).apply(PreparedStatement::executeUpdate);
    }

    //通过反射把查询结果集组装成对象
    private static <T> T assembleObject(Class<T> clazz, ResultSet set) throws Exception {
        T t = clazz.newInstance();
        final ResultSetMetaData metaData = set.getMetaData();
        final int columnCount = metaData.getColumnCount();

        for (int i = 1; i <= columnCount; i++) {
            String columnName = metaData.getColumnLabel(i);
            Field field = clazz.getDeclaredField(columnName);
            field.setAccessible(true);
            field.set(t, set.getObject(columnName));
        }
        return t;
    }

    //组装sql占位符参数,并提炼代码,减少代码冗余
    private static DbOperate performDbOperation(String sql, Object[] objects) throws Exception {
        Connection connection = JDBCUtils.getConnection();
        PreparedStatement statement = connection.prepareStatement(sql);
        for (int i = 0; i < objects.length; i++) {
            statement.setObject(i + 1, objects[i]);
        }   
        return new DbOperate() {
            @Override
            public <R> R apply(DbFunction<R> dbFunction) throws Exception {
                try {
                    return dbFunction.execute(statement);
                } finally {
                    JDBCUtils.closeConnection(null, statement, connection);
                }
            }
        };
    }

    //函数式接口
    @FunctionalInterface
    interface DbFunction<R> {
        R execute(PreparedStatement statement) throws Exception;
    }

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