• 自动化测试--封装JDBCUnit


    在进行测试的时候,经常需要对数据库进行操作。我们知道,通过代码与数据库交互,需要以下几步:

    1.加载驱动

    之前有盆友问我,为什么Selenium操作浏览器的时候,非要下载浏览器驱动?为啥对数据库进行操作的时候,直接加载driver就可以了呢?--------之类稍作解释:浏览器并没有供一个API给java来直接操作浏览器,而是提供了一套API给selenium中的XXXDriver.exe(如Chrome的ChromeDriver)来对自己进行各种操作,此时我们想自动化测试浏览器就要下载selenium官网上的各浏览器驱动,通过这个浏览器驱动再去操作浏览器;而各类数据库,如mySql是提供了一套api直接给java等各种开发语言使用,来直接对数据库进行各种操作。---这种情况下,我们直接加载数据库的driver即可。

    驱动加载的工作原理:

    Class.forName("com.mysql.jdbc.Driver");

    在加载某一 Driver 类时,它应该创建自己的实例并向 DriverManager 注册该实例

    在初始化的时候会执行注册当前Driver给DriverManager

    驱动加载一次就可了,所以一般写在static程序块中。

    2.创建数据库链接

    加载驱动之后,就可以通过DriverManager来管理并使用该驱动。通过DriverManager.getConnection()方法来获得一个数据库连接。

    遍历所有之前已经注册过的驱动,能成功建立连接的则返回。

    3.准备sql

    4.创建数据库陈述对象/预编译陈述对象

    作者这里选择的是预编译陈述对象,因为可以防止Sql注入。sql注入的在这里不再赘述。

    preparedStatement p=connect.preparedStatement(sql);

    作用是创建一个参数化的preparedStatement对象参数化的sql语句来发送到数据库。

    5.执行sql

    一般select使用p.executeQuery()来执行,返回的是一个resultSet的结果集

    update、delete、insert使用executeUpdate() ,对于dml语言,返回的就是受影响的行数

    6.关闭之前打开的链接、resultSet和PreparedStatement

    如果每次操作数据库,都要将上述语句都写一遍,会比较繁琐,代码看起来会比较臃肿。为了方便,通常将其封装为一个帮助类。关于数据库操作的封装:

    1.只有查询会返回一个结果集,增删改只会返回受影响的行数---------------可以将增删改合并成一个方法

    2.查询之后,我希望将查询结果对应到某个类中。比如我们在做自动化测试的时候,会选择把所有的数据库表映射成我们代码中的类A。此时,我希望从数据库中查询到的数据存储到类A的对象中。----------封装一个select方法,使其将查询结果保存到我们的类对象中。

    3.查询之后,我们可能只是看一下结果,并不想把结果存放到某个类的对象。这时,就可能会考虑将查询结果放到一个list列表中。-----------封装一个方法,将我们的查询结果保存到list中

    4.获得一个数据库链接的操作,也封装成方法,每次调用就可以返回一个数据库连接。

    5.关闭数据库连接、关闭数据集、关闭预编译陈述,在每次数据库操作完成之后,都要去依次关闭。因此考虑也将其封装到一个方法中。

    基于上面的考虑,笔者完成了下面的代码:

    1.包含了获得数据库连接方法和关闭方法的类

    package jdbc;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.Properties;
    
    public class jdbConnUnit {
        static Connection conn = null;
        static {
            try {
                //加载驱动,此时会自动向driverManager注册该driver的实例
                Class.forName("com.mysql.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                System.out.println("找不到这个类jdbc.driver");
                e.printStackTrace();
            }
        }
        
        
        
        /*
         * 提供一个获取数据库连接的方法
         * String properties参数 是数据库配置文件的绝对地址
         */
         static Connection getConn(String properties) {
            
             
            try {
                //通常将数据库连接信息写在一个配置文件中
                //创建一个Properties对象
                Properties pro = new Properties();
                //创建一个流,怼到我们的配置文件上
                FileInputStream stream = new FileInputStream(properties);
                //将流加载到配置文件中,相当于缓存下来,之后直接get相关数据即可,不用用一次读一次
                pro.load(stream);
                
                //getConnection方法会遍历之前加载进来的多有driver,尝试建立数据库连接,如果成功则返回
                conn = DriverManager.getConnection(pro.getProperty("url"), pro.getProperty("userName"), pro.getProperty("password"));
            
                 
            
            } catch (FileNotFoundException e) {
                System.out.println("配置文件未找到!");
                e.printStackTrace();
            } catch (IOException e) {
                System.out.println("配置文件读取失败!");
                e.printStackTrace();
            } catch (SQLException e) {
                System.out.println("数据库连接创建失败");
                e.printStackTrace();
            }
            
            return conn;
        }
         
         
         
        /*
         * 提供一个关闭之前打开的链接的方法
         * PreparedStatement pStatement----预编译对象
         * ResultSet... set------结果集,对于增删改操作,没有返回结果集,所以这里设计成可变参数列表的形式
         */
        static void close(PreparedStatement pStatement,ResultSet... set) {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    System.out.println("数据库连接关闭失败!");
                    e.printStackTrace();
                }
            }
            if (pStatement != null) {
                try {
                    pStatement.close();
                } catch (SQLException e) {
                    System.out.println("预编译声明对象关闭失败!");
                    e.printStackTrace();
                }
            }
            
        
            for (int i = 0; i < set.length; i++) {
                if (set[i] != null) {
                    try {
                        set[i].close();
                    } catch (SQLException e) {
                        System.out.println("结果集关闭失败!");
                        e.printStackTrace();
                    }
                }
            }
            
            
        }
        
    
    }

    2.封装了查询方法和增删改方法的类

    package jdbc;
    
    import java.io.File;
    import java.lang.reflect.Field;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.ResultSetMetaData;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.TreeMap;
    
    import org.omg.PortableServer.IdAssignmentPolicy;
    
    public class jdbcExecUnit {
    
        /*
         * 该方法是执行select语句,并将查询到的数据返回到调用者提供的类对象中。
         * 该方法是一个泛型方法,泛型方法的声明方式为:修饰符 <T> 返回值类型 方法名(参数列表)
         * class<T> class 要存储数据的类
         * string properties 存储调用者自己的数据库配置文件
         * String sql 存储调用者的sql语句
         * String... args 存储调用者sql语句占位符相应的参数值
         */
        public static <T> List<T> select2(Class<T> clazz, String properties, String sql, String... args)
                throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
    
            //准备一个预编译的sql语句的对象,用来将预编译的sql发送到数据库
            PreparedStatement pStatement = null;
            //准备一个结果集,用来存储查询获得的结果
            ResultSet DataSet = null;
            //调用jdbConnUnit类中getConn的方法来获得一个数据库连接
            Connection connection = jdbConnUnit.getConn(properties);
            //准备一个List,来存放我们的泛型对象
            List<T> list = new ArrayList<T>();
            
            try {
    
                // 对sql语句执行预编译,并发送给数据库。可以防止sql注入
                pStatement = connection.prepareStatement(sql);
    
                // 获取可变参数的个数(对应的是sql语句中占位符的个数)
                int length = args.length;
    
                /* 利用pStatement.setString方法将参数一次赋值给sql中的占位符
                 * select Id,RoleName from roles WHERE RoleName =? orRoleName=?,
                 * 相当于给?占位符的位置填充好参数
                 */
                for (int i = 0; i < args.length; i++) {
                    pStatement.setString(i + 1, args[i]);
                }
    
                // 执行预编译好的sql,获得一个结果集
                DataSet = pStatement.executeQuery();
                // 获得结果集对象的 列的描述(可以通过它得到表头、数据集的列数)
                ResultSetMetaData dataTitle = DataSet.getMetaData();
                //获得列数
                int length1 = dataTitle.getColumnCount();
    
                // 利用结果集的next方法,将游标指向第一行数据,以便后面将数据取出
                while (DataSet.next()) {
                    // 得到泛型T的对象
                    T t = clazz.newInstance();
                    // 获得T所有的属性
                    Field[] fields = clazz.getDeclaredFields();
                    /*
                     *  下面的for,判断了数据的类型,按照从数据库读出来的类型对应存储到对象中
                     */
                    for (int i = 0; i < fields.length; i++) {
                        //得到对应的域,并获得域名
                        Field field = fields[i];
                        String tempFieldName = field.getName();
                        //将每一个域名与列名进行对比,如果有,则赋值
                        for (int j = 0; j < length1; j++) {
                            //得到列名
                            String columnName = dataTitle.getColumnName(j + 1);
                            if (tempFieldName.equals(columnName)) {
                                // 将查询出来的resultSet中数据读取为object类型,再将其转换成和域相同的类型
                                field.set(t, (field.getType().cast(DataSet.getObject(columnName))));
                            }
                        }
                    }
    
                    // 下面的for,没有判断数据类型,直接按照String类型存入到成员变量中
                    /*
                     * for (int i = 0; i < length1; i++) {
                     * 
                     * String tempTitle=dataTitle.getColumnName(i+1); //根据表头中的列名来获得对应的属性名称 Field
                     * field=clazz.getDeclaredField(tempTitle); //给对象t的属性field赋值 field.set(t,
                     * DataSet.getString(tempTitle));
                     * 
                     * }
                     */
                    
                    //将每一个t对象加入到list中
                    list.add(t);
    
                }
    
            } catch (SQLException e) {
                System.out.println("查询语句执行失败!");
                e.printStackTrace();
            }finally{//这个关闭必须写在finally里面,如果获取的conn或者statement的时候出现了问题,这没有在finally中的话,就调用不到close 方法了,或者conn为null的时候,还会报空指针异常
    jdbConnUnit.close(pStatement, DataSet);
    } return list; } /* * 该方法是执行查询语句,并将结果保存到map中返回。 * 参数: * String properties----数据库配置文件的绝对地址; * String sql-----要执行的sql语句 * String... args----上述sql语句中如果包含占位符?,就需要传入占位符相应的参数值 */ public static List<Map<String, String>> select(String properties, String sql, String... args) { ResultSet DataSet = null; Connection connection = jdbConnUnit.getConn(properties); List<Map<String, String>> list = new ArrayList<Map<String, String>>(); PreparedStatement pStatement = null; try { // 对sql语句执行预编译,发送给数据库。防止sql注入 pStatement = connection.prepareStatement(sql); // 获取可变参数的个数(对应的是占位符的个数) int length = args.length; // 利用setString方法将参数一次赋值给sql中的占位符 // select Id,RoleName from roles WHERE RoleName =? or // RoleName=?,相当于给?占位符的位置填充好参数 for (int i = 0; i < args.length; i++) { pStatement.setString(i + 1, args[i]); } // 执行预编译好的sql,获得一个结果集 DataSet = pStatement.executeQuery(); // 获得结果集对象列的描述(包括数量、类型、属性) ResultSetMetaData dataTitle = DataSet.getMetaData(); int length1 = dataTitle.getColumnCount(); // 利用结果集的next方法,将光标移动到第一行数据,之后获得这一行的数据 while (DataSet.next()) { //这里的map用来存放一行数据,key为列名,Value为列名对应的值。 //必须在循环内部创建,否则最后的list中每个元素都会指向同一个map,而且由于map的key的不可重复性,后面的值会全部覆盖掉前面的值 Map<String, String> map = new TreeMap<String, String>(); for (int i = 0; i < length1; i++) { map.put(dataTitle.getColumnName(i + 1), DataSet.getString(dataTitle.getColumnName(i + 1))); // System.out.println(dataTitle.getColumnName(i+1));  } list.add(map); } } catch (SQLException e) { System.out.println("查询语句执行失败!"); e.printStackTrace(); } jdbConnUnit.close(pStatement, DataSet); return list; } /* * 该方法提供了增删改的语句的执行,返回受影响的行数 * String properties----数据库配置文件的绝对地址; * String sql-----要执行的sql语句 * String... args----上述sql语句中如果包含占位符?,就需要传入占位符相应的参数值 */ public static int update(String properties, String sql, String... args) { Connection connection = jdbConnUnit.getConn(properties); PreparedStatement pStatement = null; try { // 预编译sql pStatement = connection.prepareStatement(sql); } catch (SQLException e) { System.out.println("sql预编译失败!"); e.printStackTrace(); } for (int i = 0; i < args.length; i++) { try { // 给预编译之后的sql中的参数(占位符的位置)赋值 pStatement.setString(i + 1, args[i]); } catch (SQLException e) { System.out.println("更新sql占位符赋值失败"); e.printStackTrace(); } } int byUpdate = 0; try { // 执行增删改的操作 byUpdate = pStatement.executeUpdate(); } catch (SQLException e) { System.out.println("执行增删改异常"); e.printStackTrace(); } if (byUpdate > 0) { System.out.println("更新增删改操作成功,更新了" + byUpdate + "条数据"); } else { System.out.println("更新操作失败"); } jdbConnUnit.close(pStatement); return byUpdate; } }

    3.下面是与数据库表对应的一个类(为了演示,只取出了测试库一个表的映射。这种做法是就是根据ORM--对象关系映射来实现的)

    package jdbc;
    
    import java.math.BigDecimal;
    
    public class  Trafficinfos{
        //Addition  AdultPrice ChildPrice CreateTime Destination
        //From  IsDeleted StartDate TrafficNo TrafficTypeId TravelGroupId
        String Id;
        String Addition;
        BigDecimal AdultPrice;
        String ChildPrice;
        String CreateTime;
        String Destination;
        String From;
        String IsDeleted;
        String StartDate;
        String TrafficNo;
        String TrafficTypeId;
        String TravelGroupId;
        public String getId() {
            return Id;
        }
        public void setId(String id) {
            Id = id;
        }
        public String getAddition() {
            return Addition;
        }
        public void setAddition(String addition) {
            Addition = addition;
        }
        public BigDecimal getAdultPrice() {
            return AdultPrice;
        }
        public void setAdultPrice(BigDecimal adultPrice) {
            AdultPrice = adultPrice;
        }
        public String getChildPrice() {
            return ChildPrice;
        }
        public void setChildPrice(String childPrice) {
            ChildPrice = childPrice;
        }
        public String getCreateTime() {
            return CreateTime;
        }
        public void setCreateTime(String createTime) {
            CreateTime = createTime;
        }
        public String getDestination() {
            return Destination;
        }
        public void setDestination(String destination) {
            Destination = destination;
        }
        public String getFrom() {
            return From;
        }
        public void setFrom(String from) {
            From = from;
        }
        public String getIsDeleted() {
            return IsDeleted;
        }
        public void setIsDeleted(String isDeleted) {
            IsDeleted = isDeleted;
        }
        public String getStartDate() {
            return StartDate;
        }
        public void setStartDate(String startDate) {
            StartDate = startDate;
        }
        public String getTrafficNo() {
            return TrafficNo;
        }
        public void setTrafficNo(String trafficNo) {
            TrafficNo = trafficNo;
        }
        public String getTrafficTypeId() {
            return TrafficTypeId;
        }
        public void setTrafficTypeId(String trafficTypeId) {
            TrafficTypeId = trafficTypeId;
        }
        public String getTravelGroupId() {
            return TravelGroupId;
        }
        public void setTravelGroupId(String travelGroupId) {
            TravelGroupId = travelGroupId;
        }
        
        //toString
        @Override
        public String toString() {
            return "Trafficinfos [Id=" + Id + ", Addition=" + Addition + ", AdultPrice=" + AdultPrice + ", ChildPrice="
                    + ChildPrice + ", CreateTime=" + CreateTime + ", Destination=" + Destination + ", From=" + From
                    + ", IsDeleted=" + IsDeleted + ", StartDate=" + StartDate + ", TrafficNo=" + TrafficNo
                    + ", TrafficTypeId=" + TrafficTypeId + ", TravelGroupId=" + TravelGroupId + "]";
        }
        
        
    }

    上面就完成了一个对数据库操作的简单封装。提供一个测试类,进行简单的测试:

    package jdbc;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    public class TestJdbc {
    
        public static void main(String[] args)
                throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
            // update执行
            String updateSql = "UPDATE trafficinfos set AdultPrice=?,`From`=?;";
            jdbcExecUnit.update("D:\myTest\jdbc\src\main\resources\jdbc.Properties", updateSql, "3333",
                    "yuhangnanyuan");
    
            // select执行,数据存储到map里
            String selectSql = "SELECT Id,AdultPrice FROM  trafficinfos ;";
            List<Map<String, String>> data = jdbcExecUnit.select("D:\myTest\jdbc\src\main\resources\jdbc.Properties",selectSql);
    
            for (Map<String, String> map : data) {
                Set<String> keySets = map.keySet();
                for (String keyset : keySets) {
                    System.out.println(keyset + "--------" + map.get(keyset));
                }
            }
    
            // select执行,数据存储到类的对象里
            String selectSql2 = "SELECT Id,AdultPrice FROM  trafficinfos ;";
            List<Trafficinfos> list = jdbcExecUnit.<Trafficinfos>select2(Trafficinfos.class,
                    "D:\myTest\jdbc\src\main\resources\jdbc.Properties", selectSql2);
            for (Trafficinfos trafficinfos : list) {
                System.out.println(trafficinfos);
            }
    
        }
    
    }

    完成上述封装之后,可以打成jar包,提交到maven库上,供其他人使用。

  • 相关阅读:
    网络摄像头监控中什么情况下需要使用流媒体转发服务器?
    流媒体播放器播放h264编码视频与h265编码视频哪个更清晰?
    RTMP推流视频流媒体服务如何实现网络传输?
    web视频直播服务器更改端口号后录像功能失效问题解决
    互联网视频直播/点播流媒体服务器使用http和rtmp做点播时有什么区别?
    视频互联网直播/点播服务器中关于内容分发CDN的相关问题汇总
    网页直播系统推流端和拉流端都有哪些环节?
    自建视频流媒体服务器需要满足哪些条件?
    线上课堂教育行业选择互动直播时有哪些直播方案?
    互联网直播点播平台在进行iframe直播分享时如何禁止本地视频自动播放?
  • 原文地址:https://www.cnblogs.com/clairejing/p/9385878.html
Copyright © 2020-2023  润新知