这篇我们说说怎样用java操作数据库。用selenium做测试时有时候不光要测试页面上的值是否正确,还需要查验数据库,这就需要我们连接数据库并执行一些简单的sql语句。所以,在读这篇之前,确保你懂一些基本的sql知识,比如数据的增删改查(create, insert, update, select),以及连接(join)等等。
首先你要在电脑上安装一个数据库软件,我选的是mysql,以下也是用mysql做实验。你既可以去mysql官网下载,也可以下载一个叫appserv的软件,开源免费,是一个泰国人写的,里面自带mysql。我推荐后者,因为以后写自动化测试代码时也会用到appserv,它可以帮你自动搭建一个web服务器环境并加载测试网页,关键是它还是一键傻瓜式安装,省去了繁琐的配置问题。把appserv安装好,装好后你应该能在程序列表里看到MySQL Command Line Client:
点击它然后输入安装时设置的密码密码,出现下面的内容证明安装正确:
然后执行show databases显示当前的数据库。由于我们还没创建自己的数据库,只能显示系统自带的:
在命令行下看结果或是操作数据库很不方便,于是mysql还给我们提供了好几个图形界面,其中一个有名的是MySQL Workbench。这个工具在Oracle官网上有,免费倒是免费,但下载时需要你注册登录,挺事儿b的。不过还有几个界面工具,比如Navicat也不错,大家可以去试试,不嫌麻烦还是Workbench最好用。刚装好mysql的时候只有它本身自带的系统数据库,没有我们自己创建的,不过还是没关系,我给大家准备好了一个数据库。找到HumanResources.sql,把它复制到刚装好的Appserv的MySQL的bin目录下:
在MySQL Command Line Client输入create database humanresources;先创建一个空的数据库,接着执行use humanresources go;转到该数据库下,然后执行source humanresources.sql倒入所有的表:
在MySQL Workbench连接数据库就可以看到我们新建的humanresources了,不太会用MySQL Workbench的朋友可以去网上多搜搜,资料多的是,我这儿就不多说了。接着我们查询一下employee表的内容,发现有三个员工的信息:
到此为止我们就有了一个可以操作的数据库了。现在我就开始用自己写的程序控制数据库里的数据。Java提供了一个叫jdbc的类库,又叫jdbc driver(驱动),它可以像一座桥一样连接你的程序和数据库,让它俩之间对话:
java为不同种类的数据库提供对应的jdbc,因为我们用mysql,所以必须用mysql对应的jdbc,用sql server的就不行。jdbc也不是java jdk里自带的,我们需要去网上单独下载。不过我还是已经为大家准备好了,就在tools文件夹里,名字叫mysql-connector-java-3.1.14-bin.jar。这些浪费时间的事情就让我为大家服务吧,但平时练习要自己多练多查资料。
打开eclipse,新建一个叫SeleniumDatabase的项目和一个叫Test.java的类,带上main()函数。接着把jar包放进去:
打开Test.java开始写程序。想让程序对数据库进行操作之前需要完成三个小步骤:1.声明jdbc driver;2.连接数据库;3.准备数据库指令发送器。
这三步对应的代码如下:
第一步声明jdbc driver,就是提示该类即将与数据库进行交互。相当于打个招呼“哎,数据库姐们儿,我要撩你了。”用之前先声明。
提示完成后就该撩了,第二步连接数据库。getConnection()是属于DriverManager类的一个静态方法,翻译过来其实就是“得到连接”的意思。它接受三个参数,分别是数据库名,数据库用户名,密码。数据库名参数里面包含的信息不少,”mysql”指定了我们的jdbc类型;”localhost”指明了数据库的地址是在本机上,你也可以用127.0.0.1代替。当然,如果你的数据库不在你电脑上的话就需要指明其它地址;”3306”是mysql数据库的默认端口号,如果是别的类型数据库就可能需要更改端口号;”humanresources”就是我们的数据库名称。第一个参数虽然长,但不难理解。第二、三个参数就是你用于登录数据库的用户名密码。
第三步是准备指令发送器。数据库连接完毕就该发指令操作了,操作时需要一个类型为Statement的对象来发送指令。其实发送器这个名字是我自己起的,现在不理解没关系,一会儿就体会到了。这三步牢牢记住,只要用到数据库没跑。
刚才说了,对数据库的基本操作无外乎有四种– 增加表中记录,删除表中记录,修改表中记录,查询表中记录,简称增删改查。比如我现在根据已有数据库对Employee表进行如下操作:
- 查询当前所有记录;
- 查询员工号码为1001的记录;
- 增加一个员工,号码为1004,名字是Tester Four,密码是321,最后登录日期是2017-10-07;
- 删除号码为1004的员工;
- 修改号码为1004的员工信息。
查询当前所有记录的语句是select * from Employee,所以我们的程序就是:
还是一句一句讲。刚才咱们准备好的指令发送器对象叫stmt,第22行它调用executeQuery()方法把语句直接发送到数据库端并执行。你看,stmt就好像一门大炮一样直接把炸弹通过程序打到指定数据库,叫它发送器是不是很形象?executeQuery()中文是执行查询语句的意思,容易记住。执行完总要返回结果吧?你在mysql workbench或是mysql命令行下输入“select * from Employee”会返回这三条数据:
我们管它叫作结果集。在程序里用executeQuery()执行查询语句会把这个结果集作为对象的形式返回,对象类型是ResultSet。这就是ResultSet rs = stmt.executeQuery(“select * from Employee”)这句话的意思。跟数据库里的表一样,结果集也都是一行一行的,我们可以把它看成一个包含有三个元素的大数组,数组里每一个成员都是一整行:
讲java时说数组对象会默认指向第一行,存储第一个元素的地址。既然结果集像数组,ResultSet对象rs也就默认指向第一行,存储第一行的地址:
紧接着下面有个while循环。拿到rs结果集对象后怎么访问里面的数据呢?还记得我们之前讲hashmap的时候提到过的浮标吗?当时我说遍历的时候要用浮标逐行扫描,每次循环执行浮标指向下一行。结果集与它很类似,每次执行rs.next()的时候小浮标就会指向新的一行,存新的一行地址。while循环体里是针对当前某一行读取数据的过程。如果刚才rs是针对行的访问,那么读取过程就是针对每一行中列的访问。被访问的值是整型就用rs.getInt(),字符串就是rs.getString()。以employeeName = rs.getString(3)为例,结果集里员工姓名是字符串类型的,这是在我们创建Employee表的时候就已经自定义好的了,改不了,所以要用getString。它里面接受一个参数,可以是数据所在结果集里的列标,也就是列的位置,3代表第3列,你看第三列是不是员工姓名?
除了员工姓名,我们这个例子里还访问了员工代号(列标为2)和员工最后一次登录日期(列标为4)。值得注意的是,行标一定是在当前结果集里行的位置。比如你的sql语句是selectEmployeeCode, Name from Employee,那返回的结果集就是
员工代号是第1列,员工姓名是第2列,而且压根没有员工最后登录日期。你要是硬取就会报错。同样,你的结果集要是只包含Employee表中的第3列姓名和第6列是否被删除,那在结果集中姓名就是变成了第1列,是否被删除就变成了第2列。所以,列标是根据结果集而言的,要时刻根据结果集的改变进行相应调整。
我们的例子里有三行,所以while循环执行三遍,每一遍访问一行员工代号、员工姓名和员工最后登陆日期的值,并且打印出来。结果如下:
这就是查询。把结果集想成一个大数组,只不过数组中每一个位置装的不是一个值,而是一整行数据。
第二个操作是查询员工号码为1001的员工。那你的sql语句就要变成select * from Employee where EmployeeCode = '1001'。因为这次的结果集只有一行,你可以把while改成if,不多说,自己去练习。
第三个操作是增加一个员工,号码为1004,名字是Tester Four,密码是321,最后登陆日期是2017-10-07。Sql语句是insert into Employee values(EmployeeId, '1004', 'Tester Four', '321', '2017-10-07', 0)。先放上程序:
刚才查询的时候发送器对象stmt调用方法是executeQuery(),但新增不是查询,并不是要把结果读出来,而是修改数据库里的值,所以调用的方法是executeUpdate()。既然不是读值,当然也就没必要返回什么结果集,所以它返回的是一个整型数值,而且这个值就两种可能,要么是1,要么是0。1代表添加成功,0代表添加失败。我们执行程序然后查询下数据库,看是不是新添了一行:
没有任何问题。第四个操作是删除号码为1004的员工,sql语句是delete from Employee where EmployeeCode = '1004'。同样,删除也是修改,用executeUpdate()。删除成功返回1,删除失败返回0:
但是,实际项目中往往不推荐这么做。万一删错了呢?或是你刚删完老板改主意说不删了呢?你还得再加回去。有时候往往一个表有很多列,加的时候挺麻烦的,如果原始数据丢失了加都加不回来。这就是我在设计表时加入了IsDeleted这一列的原因。如果被删了就是1,没删的时候显示0。这样,只要我每次取数据时用select * from Employee where IsDeleted = 0就行了,每次出来的都是没被删的。这就是第五个操作,修改号码为1004的IsDeleted值。还是修改,所以继续用executeUpdate():
再查查数据库:
1004号员工更新成功。以上就是让通过jdbc驱动来操作数据库的过程,增删改查,尤其是查询,在自动化测试里很关键。这篇源代码在DatabaseOperations这个项目里。