聊聊JDBC

来源:互联网 发布:马哥的linux视频教程 编辑:程序博客网 时间:2024/06/08 07:01
JDBC英文全称为Java DataBase Connectivity,即Java数据库连接,也就是Java程序连接数据库的一种技术。说白了,就是当程序代码和数据库需要进行数据传输(交互)的时候使用的一门技术。
1.确定的和不确定的
介绍了JDBC的作用以后,我们知道目标是两个,一个是编程语言,一个是数据库。那么现在确定的是编程语言Java,数据库是什么呢?SQL Server,Oracle,MySQL?还是?都有可能,也就是不确定的是数据库。不管大家是否了解这些数据库,至少我们知道,不同的数据库有可能属于不同的厂商,而且不同的数据库之间是有一些差异性的。这样会带来一个问题,就是Java通过JDBC技术与不同的数据库进行交互,具体的细节都一样吗?显然会有一些差异。

2.不确定因素的解决方案
上面和大家唠叨了关于不确定的一面,即数据库不确定,那么这个JDBC技术就要能适应不同的数据库,这句话应该可以理解,也就是JDBC技术要足够强大,对于不同的数据库有不同的技术支持,这样才能满足不同数据库之间的差异性问题。

那怎样让JDBC技术足够强大呢?用大家能够接受的逻辑思维就是,在使用JDBC技术的时候,对数据库进行一下判断,if(数据库=SQL Server)...,if(数据库=MySQL)...,这样的做法原则上可以达到目的。问题是,一方面这样写代码是不是感觉有点low?另外一方面,目前市面上有的数据库咱们可以用if来写,那以后出了什么新的数据库,是不是还要大动干戈去修改JDBC的源代码?所以这样做不可取。

这时候大家会想,我们连JDBC怎么用都不知道,说这么多为了什么。别急,马上就开始介绍JDBC的使用。之所以啰嗦这么长时间,是为了给大家传输并强化一个概念,就是接口与实现。一说到接口,肯定每个人都能想到interface A,这表示定义了一个接口,class Student implements A,表示定义了一个类Student实现了接口A。这种写法大家很熟悉,问题是怎么用?我们知道,接口是为了抽取出相同的功能,方便以后扩展,这句话不太接地气,用接地气的理解就是。要想使用JDBC技术,要脚趾头想都知道,肯定是找类,找方法,找属性等等,因为Java是面向对象的开发语言,做任何事情都要想到对象,new对象对吧?好了,有了这样一个共识,上面说的不确定因素就可以使用接口的方式来解决。如何解决呢?请看下面一个图解。

上图解释的还是比较清楚的,也就是JDBC只提供接口,对于每个不同的数据库,它们自己去实现这些接口即可,因为对于不同的数据库即使有差异性,但是大体流程是一样的,这些流程由不同的对象去完成,后面我们会介绍。假如以后有了一个新的数据库产生,那么该数据库只要去实现JDBC提供的接口即可,非常方便,那具体怎么做呢?我们看下一个知识点。

3.接口和实现
上面的流程咱们清楚了,也就是谁谁来提供接口,具体的数据库来提供实现,那具体怎么操作?
提供这套JDBC接口的任务当之无愧是jdk,在jdk中有一个包,名称为java.sql,在这个包中提供了很多接口,如下图所示。

好了,接口有了,还差具体的实现。我们知道,不同的数据库有不同的实现方式,这取决于我们用哪个数据库,比如我们这次案例中使用MySQL数据库,那么MySQL数据库就要对这些接口进行实现,如何实现?MySQL厂商把这些实现类已经写好了,以jar包的形式提供给开发者,这时候大家就会想到没错,需要导包,导包之后才可以进行开发,这也是我们java开发中必要的一个环节,经常在使用某个技术的时候,需要导入相关的jar包,这里之所以需要导入MySQL的jar,是因为需要提供实现类,因为不同的数据库之间有一些差异性,上面已经分析过了。

4.JDBC技术的基础使用
从上面的描述我们知道,除了搭建开发环境之外,搭建开发环境也就是需要使用哪些软件,因为本文是阐述JDBC的,这里就不作过多介绍咯,不会搭建java开发环境的可以问度娘,比较简单。还需要的就是把之前说的接口和实现准备好,因为接口是在jdk中,无需准备,实现的话,我们本次使用的是MySQL数据库,所以需要导入MySQL数据库的jar包,在百度中可以随意下载一个,比如我下载了一个5.1的版本,名称为"mysql-connector-java-5.1.17-bin.jar",然后导入到MyEclipse开发环境中。这里先作一个说明,数据库dbjdbc和数据表users我都已经建好了,Java项目JDBCDemo也建好了,就不作过程的阐述咯。



(1)加载MySQL的驱动类(要想使用mysql的实现类,得先注册一把)
Class.forName("com.mysql.jdbc.Driver");
为啥要这么做,加MySQL的驱动类加载到JVM中,说白了就是咱们要使用MySQL数据库的jar包了,是不是先得注册一把?


(2)根据DriverManager得到Connection对象(要java程序和数据库发生交互,得先连接上对应的数据)
Connection conn=DriverManager.getConnection(url, user, password);
a.其中url="jdbc:mysql://localhost:3306/dbjdbc",user="root",password="root"
b.url表示要连接到mysql的哪个数据库,mysql的默认端口是3306,前面的无需改,dbjdbc这里填写你自己的数据库名称
c.user填写mysql数据库的用户名
d.password填写mysql数据库的密码
e.之前我们说过,要使用JDBC技术,无非就是找类,找方法,找属性等,那么java代码要想和数据库发生联系,必定得先要有一个对象连接上对应的数据库,这个对象就是Connection对象,从名称中也可以看出,但是要注意的是Connection conn定义导包的时候,最好选择java.sql中的Connection,以接口的形式声明,这样以后的扩展性比较好,这就是多态的应用,这里不展开论述,有机会专门说说多态。


(3)根据连接对象Connection得到Statement对象(Connection对象只负责连接到哪个数据库,要想进行增删改查操作得重新找一个对象,比如Statement对象)
Statement stmt = conn.createStatement();
有了Connection连接对象之后,我们就知道具体要连接哪个数据库,接下来就是要进行具体的增删改查操作了,那怎么做呢?记住万物皆对象,Connection对象是负责连接的,而负责进行增删改查的对象又是什么呢?没错,就是Statement对象,当然这里是以Statement对象为例,还可以是其他对象,毕竟能做成一件事的途径不止一个,我们这里是为了演示使用。而Statement对象操作的是哪个数据库,这取决于由哪个Connection对象生成的Statement对象,这点很好理解吧?不过要记住,Statement对象在导包的时候也需要导入java.sql中的,原理和上面一样。


(4)增加(准备需要增加的sql语句,交给Statement对象的executeUpdate(sql)执行)
好了,说了这么多,接下来总可以进行插入操作了吧?也就是在java代码中通过jdbc技术向数据表中插入数据。这里肯定要准备一条sql语句,因为sql语句知道向哪个表中插入什么样的数据对不对?比如写上一条sql语句,String sql="insert into users(name) values('插入一条新数据!')"。那么如何让这条sql语句生效呢?这时候就想到了Statement对象,只要调用该对象的executeUpdate方法即可,并且将sql语句作为参数传入。返回值为int类型,表示受影响的行数,倘若上述的sql语句执行成功了,就会有一行受影响。

执行结果




(5)修改(修改的原理和增加的原理一样,准备一条需要修改的sql语句)
比如我们需要将id为1的name值改成"欢迎关注忆说就懂微信公众号",准备一条sql语句
String sql="update users set name='欢迎关注忆说就懂微信公众号' where id=1";
然后将该sql语句同样交给Statement对象的executeUpdate方法即可,如下图所示

执行结果


(6)删除(删除的原理和增加的原理一样,准备一条需要删除的sql语句)
比如我们需要将id为3的记录删除,准备一条sql语句
String sql="delete from users where id=3";
然后将该sql语句同样交给Statement对象的executeUpdate方法即可,如下图所示

执行结果



(7)查询
通过上面的增删改案例可以看出,增删改的原理是一样的,都是准备一条需要的sql语句,然后交给Statement对象的executeUpdate方法执行即可。那么查询呢?毋庸置疑,查询肯定也需要准备一条sql语句,因为你得告诉程序需要查询哪张表,哪条记录,查询条件是什么等等。这里我们查询users表的所有记录,然后打印输出到控制台。
String sql="select * from users";
然后将该sql语句交给Statement对象,此时调用的是executeQuery,从名称中可以看出,Query的意思是查询。一般使用一个方法,我们往往关心的是参数列表和返回值,这里的参数是sql,那么返回值是什么呢?既然是查询,那么返回值肯定是查询的结果,这个结果使用的是一个对象保存,即ResultSet,因为万物皆对象,该对象的结构和数据表的结构是一样的,要想取出其中的数据,循环读取出来,下面我们直接看代码。

从while循环开始理解,while(rs.next())表示循环读取行,数据表中每有一行就循环一次,这个比较容易理解,比如目前数据表中有两条记录,那么该循环就执行两次。
rs.getXxx明显是取出每行的值,比如第一次循环,读取的是第一行,我们知道对于表格,只要知道行与列就可以对应到某个具体单元格的值,那么现在行有了,列呢?rs.getXxx括号中的参数可以是列的序号,比如1,2,3表示第一列,第二列,第三列,也可以是列的名称,比如"id","name"。但是要注意,如果使用标号,该标号是从1开始的,因为程序中通常是从下标0开始,这点不同,另外如果通过列名来获取值,列名要和数据表中的名称对应,不然会报错,取不到值。至于rs.getXxx有时候是Int,有时候是String,是根据数据表中该列的类型决定的。这样一说,是不是非常容易理解。
比如在while进行第一次循环时,rs.getInt("id")表示取出第一行中列名为"id"的单元格的值,也就是第一行第一列的值,发现是"1";比如在while进行第二次循环时,rs.getString(2)表示取出第二行中第二列的单元格的值,发现是"JDBC",我们来看下运行的结果。


5.JDBC技术的进一步使用
有了上面的增删改查基础之后,我们不妨来看一个具体的案例,以"管理员登录"为例,查找admin表中是否存在该用户名和密码,从而判断是否登录成功。主要是为了了解JDBC的进一步使用。好了,咱们来开始。
首先来创建一张"admin"的数据表,如下图所示,其中有这些字段

然后我们使用之前的jdbc增加的技术对给admin表中插入一条记录,代码如下图所示

这样就可以向admin数据表中插入一条对应的记录,这里我就不截图数据表的图片了,肯定可以插入成功。

然后咱们定义一个方法叫做login,其中有两个参数username,password,模拟用户登录的情况。我们的想法是,当别人传来username和password的时候,我们用这两者作为select语句的查询条件部分,如果这样从数据表中能够查询到对应的记录,说明用户名和密码正确,否则用户名或者密码错误。sql语句如下图所示,这里进行了字符串的拼接,因为外面传来的是两个变量,直接写成"select * from admin where username=username and password=password"显然不合适,这样查询条件就变成了用户名为username,密码为password了,所以需要进行字符串的拼接。

将该sql语句交给Statement对象的executeQuery方法执行,得到一个ResultSet对象,最后我们来判断ResultSet对象中是否有数据就可以得出用户名和密码是否正确,流程比较简单,但这里想说明几个问题。

(1)拼接字符串好麻烦,能不能不拼接字符串?
大家肯定会有这样一个疑惑,万一后面的条件变多了,字段变多了,那么拼起来更加麻烦,还容易出错,那有没有解决方案呢?答案是肯定的,需要引出另外一个对象PreparedStatement,这个对象也是用于增删改查操作的,只不过和Statement对象有点不同,具体哪边不同,我们来看下面的改造。

a.得到PreparedStatement对象
之前通过Connection对象的createStatement方法可以得到Statement对象,那么PrepareStatement对象如何获得呢?如下图所示,只要调用Connection对象的prepareStatement方法即可,与createStatement对象不同的是,它需要接受一个sql语句,所以我们需要将sql语句放到创建该对象之前,这倒没问题太大问题,关键是这样有什么好处吗?再来看第二点


b.有了PreparedStatement对象之后,sql语句就可以进行改写
String sql="select * from admin where username=? and password=?";
有没有发现方便很多?那其中的"?"表示什么,它代表的是占位符,也就是它先把位置占在这,具体值是什么,接下来再指定


c.指定占位符的值
发现通过PreparedStatement对象可以对占位符的值进行指定,有了前面getXxx的基础,这里用pst.setXxx就很容易理解了吧?因为数据表中的username和password字段是varchar类型,所以这里用String来设置,第一个参数1和2,表示sql语句中占位符的顺序,比如1代表username=?中的"?",第二个参数表示用什么值来代替此占位符,发现是用别人传来的参数username和password进行代替的,这样是不是就简化了sql语句的写法,不用那样复杂的拼接字符串了。


d.执行查询操作
那接下来怎样进行查询操作呢?如下图所示,也是调用PreparedStatement对象的excuteQuery方法,与Statement对象不同的是,此时不用传sql语句了,因为在Connection对象创建PreparedStatement对象时已经把sql语句传入进行了预处理,得到的结果仍然是ResultSet对象,这点不难理解,同样是一个表格的形式。


(2)调用一下login方法,来做一个测试
好了,PreparedStatement对象咱们也会使用了,那接下来就模拟一下登录,在主函数中调用该login方法,传入username和password。为了看到效果,我们将ResultSet的结果进行一下判断,然后打印在控制台。这里之所以用if,是因为数据表中只有一条记录,如果能查询到ResultSet中也是一条记录,进行一次读取即可。

然后在主函数中进行调用,登录成功代码

运行结果

登录失败代码

运行结果


(3)好了,到了这里,应该说JDBC的基本操作已经都OK,还有一个问题需要说明一下
在根据username和password进行查询的时候,得到一个ResultSet结果集,我们肯定不能单单打印出里面的值,需要用一个对象将用户登录成功的数据保存起来,因为万物皆对象,而且在登录成功之后,比如需要显示用户的信息,像Xxx欢迎你等等,肯定不能说再查一次数据表,这样没有意义,也就是说当用户登录成功之后,我们需要用对象将其保存。

所以咱们需要设计一个对象,也就是实体类,用于保存用户登录成功之后的数据,如下图所示,于是我们设计了这样一个实体类,实体类的类名和数据表的表明对应,实体类的属性名称和数据表的字段名称对应,这是一般的设计原则,大家对为什么需要实体类有疑惑,可以关注下我们的微信公众号"忆说就懂",我们会写一份关于实体类的文章。总之现在知道一点,当用户登录成功之后,数据在ResultSet中,我们需要将ResultSet中的数据搬运到该实体类中,具体怎么做呢?

如下图所示,创建一个Admin对象,用于保存接下来的数据,然后读取ResultSet中的数据,读到每一行每一列的值分别赋值给admin对象里面的属性,通过set方法。另外,需要将login方法的返回值从void改成Admin,因为调用login方法后,需要得到一个实体类对象,这样别人才可以使用到该实体类对象中保存的数据,这点很容易理解吧?但是要注意,admin=new Admin()这句话一定要放到if语句中,不然通过判断admin是否为null的时候每次都是登录成功,细心的你一定知道是为什么。

于是我们在主函数中调用的时候,就可以根据该Admin对象是否为null,从而判断用户名和密码是否正确了,下面只演示了一下登录成功的情况,登录失败就不作截图咯。从代码和运行结果可以看出,不仅可以判断出用户名和密码是否正确,而且可以通过Admin对象拿到登录成功者的相关信息。

运行结果为


(4)但是不知道大家有没有发现,实际上这样的jdbc代码写起来还是有点麻烦的,一方面sql语句那里需要占位符,然后又要给占位符设置值,另外一方面在将ResultSet中的数据搬运到实体类中时,过程也麻烦。如果一个数据表中的字段很多,这样的过程写起来是非常痛苦的,而且感觉没什么价值。所以对于jdbc代码纵然可以实现crud操作,但不是很方便,于是有了各种对jdbc代码封装的框架,比如DBUtils,Hibernate,MyBatis等等,也就是它们把那些麻烦的操作都给封装起来了,只要我们学会使用它们即可,当然,框架的优势不仅如此,还有很多其他的优势,同时也有弊端,这里就不展开说明咯,等聊到具体的框架时咱们再分析。

6.总结
对于JDBC的基本使用,大家应该都已经没问题了,上面的所有知识都是比较基础的,咱们先入门再深入。如果文章中有哪些不足之处,还希望各位给予宝贵的意见,一起加油,谢谢!
文章中的源码和数据库下载链接:http://pan.baidu.com/s/1mhTPnaK 密码:jqj9

大家也可以关注下我们的微信公众号“忆说就懂”,扫一扫或者长按图片进行识别,平时有什么问题可以一起探讨与学习,谢谢!QQ交流群:574393683