标签:信息 user 比较 sys 为什么 cts exec child 数据库表
在进行测试的时候,经常需要对数据库进行操作。我们知道,通过代码与数据库交互,需要以下几步:
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(); } 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库上,供其他人使用。
标签:信息 user 比较 sys 为什么 cts exec child 数据库表
原文地址:https://www.cnblogs.com/clairejing/p/9385878.html