自动化测试--封装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库上,供其他人使用。

 

posted @ 2018-07-29 17:58  小屁妞  阅读(535)  评论(0编辑  收藏  举报