JDBC代码实现之第三版:创建语句对象与执行SQL语句
前言
由代码实现之第二版:代码封装与参数提取 我们通过DBTool工具封装了注册驱动,建立、关闭连接的代码,那我们得到连接以后,想执行一个SQL。还需要通过连接创建一个对象叫Statement。
然后呢,调用它的方法执行sql语句。那么,执行DML的方法调这个executeUpdate()。DQL调这个方法executeQuery(),其他的,调这个execute()。
1.准备工作
- 想执行SQL语句,首先数据库里需要有相应的表存在。新建一个emps表,然后再表中插入几条默认数据。往Oracle里插入数据时,ID使用序列生成,还得需要创建一个序列sequence,在执行DML语句之后需要commit提交,保存更改。
- 创建员工表:
create table emps (
empno number(8) primary key,
ename varchar(20),
job varchar(20),
mgr number(8),
hiredate date,
sal number(11,2),
comm number(11,2),
deptno number(8)
);
- 创建序列:
create sequence emps_seq;
- 插入默认数据:
insert into emps values(emps_seq.nextval,‘张三’,‘领导’,0,sysdate,18000.0,3000.0,1);
insert into emps values(emps_seq.nextval,‘李四’,‘销售’,1,sysdate,7000.0,5000.0,1);
insert into emps values(emps_seq.nextval,‘王五’,‘销售’,1,sysdate,8000.0,2000.0,1);
insert into emps values(emps_seq.nextval,‘马六’,‘市场’,1,sysdate,6000.0,0,1);
insert into emps values(emps_seq.nextval,‘周七’,‘市场’,1,sysdate,5000.0,0,1);
insert into emps values(emps_seq.nextval,‘冯八’,‘市场’,1,sysdate,4000.0,0,1);
commit;
- 检查数据:
select * from emps;
2.创建Statement对象
创建连接以后,下一步还要创建另外一个对象Statement,这个对象能帮我们执行SQL,在利用Statement对象执行SQL之前,要写好相应的SQL语句。这个Statement在java.sql包下,java.sql.Statement。代码如下:
Statement smt = conn.createStatement();
3.执行DML的SQL语句
基于emps表,以insert为DML语句的代表,演示Statement对象如何执行DML的SQL语句。
- SQL语句:在emps表中一共有8个字段,所以要写出8列来,第一列用序列生成,其他列的值,按照顺序直接写上去,代码如下(JDBC要求在Java中,sql语句无需写分号结尾):
String sql = "insert into emps values(emps_seq.nextval, '唐僧', '经理', 0, sysdate, 8000.0, 2000.0, 3)";
- 执行SQL:执行DML的SQL语句,需要调用Statement对象的executeUpdate()方法。这个方法是有返回值的,它的返回值是个整数,返回的是行数,就是你插入一行数据,则返回1,插入两行,返回2,当然我们插入,肯定就是每个sql就1行,那如果执行的是一个delete语句,它返回的是删除几行,执行update语句,返回的是修改了几行,所以它返回的是增加,或修改,或删除了几行,是一个行数。代码如下:
int rows = smt.executeUpdate(sql);
System.out.println(rows);
- 完整代码示例:
/**
* 1.测试DBTool.getConnection()
* 2.执行insert语句
*/
@Test
public void test() {
Connection conn = null;
try {
//创建连接
conn = DBTool.getConnection();
System.out.println(conn);
//创建Statement
Statement smt = conn.createStatement();
//SQL(不要写分号结束)
String sql = “insert into emps values(emps_seq.nextval, ‘唐僧’, ‘经理’, 0, sysdate, 8000.0, 2000.0, 3)”;
//执行SQL
int rows = smt.executeUpdate(sql);
//返回增加/修改/删除了几行
System.out.println(rows);
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBTool.close(conn);
}
}
4.执行DQL的SQL语句
如何执行查询语句,执行DQL语句比执行DML语句要复杂一些,因为,执行DML语句,它直接返回个行数就完了,一般还用不上这个行数,只要执行成功了,它该几行就几行,但执行查询的时候,它查什么就返回什么,我要查10个员工,你把10个员工返回给我,所以,这个返回的数据就多了,那具体要怎么处理返回的数据,就麻烦在此。
- SQL语句:查询emps这张表,条件部门ID等于1,排个序,结尾不写分号。另外,在正式项目中写SQL语句时,建议不要写
select *
,因为一个表中可能有很多字段,而业务中一般只需要几个字段,写select *
是比较耗资源的。比如开发的时候,做一个合同的模板。它不是一张表,是关联的表有十几个,而合同的主表有200多个字段,上来就select *
的话,就是不负责任的,具体要看业务需求,要什么,查什么。此处表中字段少,为了省事,写*
问题也不大。String sql = "select * from emps_lhh where deptno=1 order by empno";
Ctrl+Shift+X 变大
Ctrl+Shift+Y 变小 - 执行SQL:执行DQL的SQL语句,需要调用Statement对象的executeQuery(sql)方法。传入sql,它给我们返回一个特殊的东西,叫做ResultSet结果集对象,查询结果的集合,这个对象里存的是查询到的结果,是多行多列的数据。即查询到的数据以多行多列的形式封装到这个结果集ResultSet对象中。代码如下:
ResultSet rs = smt.executeQuery(sql);
- ResultSet:结果集ResultSet对象,它的底层采用的是迭代器模式进行的设计,迭代器模式通常使用while遍历访问(比如遍历Iterator对象也是如此)。通过调用ResultSet的next()方法读取到下一行数据,所以说我们遍历的是行,在获取到某一行数据之后,这一行会有很多列,可以通过调用ResultSet的getXxx()方法分别获取该行的每一列的值(比如
rs.getInt("empno");
或者rs.getString("ename")
),在rs.getXxx()的语法是rs.get类型()的一系列方法,这些方法中需要传入一个参数,这个参数可以写字段的名称,也可以写字段的序号都行,但一般,建议写字段的名,不建议写序号,因为在我们开发的过程中,有可能,这个表将来会发生变化。比如说今年,这个表里20个字段,明年没准多几个字段,或者少了几个字段,这个顺序会有所变化。而字段名,只要字段不删的话,一般不会变。所以一般建议用字段名作参数。 - 完整代码示例:
@Test
public void test() {
Connection conn = null;
try {
conn = DBTool.getConnection();
Statement smt = conn.createStatement();
//ctrl+shift+x/y
String sql = “select * from emps where deptno=1 order by empno”;
//返回的ResultSet是结果集,封装了查询到的结果(多行多列).
//该对象采用迭代器模式设计而来的. 迭代器通常使用while遍历.
ResultSet rs = smt.executeQuery(sql);
//每次next()就可以获取下一行数据
while(rs.next()) {
//获取该行的每一列的值
//rs.get类型(字段的序号)
//rs.get类型(字段的名称)
System.out.println(rs.getInt(“empno”));
System.out.println(rs.getString(“ename”));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBTool.close(conn);
}
}
5.总结
-
JDBC(Java Database Connectivity): Java访问数据库的解决方案,希望用相同的方式访问不同的数据库,以实现与具体数据库无关的Java操作界面,因此JDBC定义了一套标准接口,即访问数据库的通用API,不同的数据库厂商根据各自数据库的特点去实现这些接口:
APP——JDBC——JDBC Driver(由数据库厂商实现)——数据库
-
JDBC是属于Java的一部分。这部分技术,Sun只是规定了接口,而没有提供实现。主要因为Sun实现不起,市场上的数据库太多,所以这个接口的实现由数据库的厂商来实现,而数据库厂商提供的实现,我们不叫实现类,我们给它取个学名叫Driver,英文的驱动,那么每一个数据库厂商都有它自己的驱动。那么在开发的时候,选择哪一个驱动需要由我们来指定,并且不用自己去实例化这个驱动类,Sun给我提供了一个类叫DriverManager,它能够帮我们去实例化这个实现类,并帮我们管理驱动,但是,DriverManager给我返回的是接口类型,而它底层的实现new的是具体的实现类。因此,对我们来说,重点是关注这个接口中有什么方法,怎么去调用它们,而不需要关心它的实例化的过程。
-
JDBC的使用步骤:首先,注册驱动,告诉DriverManager驱动管理器,要用哪一套驱动,DriverManager知道信息以后,就能够调用那个驱动里面的实现类,帮我们创建连接,我们得到连接以后,又可以通过连接创建Statement,而这个Statement,它有若干方法,能够执行不同的SQL,其中executeUpdate()执行DML语句,增删改。executeQuery()这个方法执行查询,其他的SQL执行用execute()方法。最后,在使用连接执行完SQL以后,一定要把连接关闭。因为如果不关闭,会长期占用这个数据库的连接,无论是什么数据库,它的连接上限基本上就是五六百个,消耗没了,就很有可能导致数据库的崩溃,所以一定要关闭连接。另外,在JDBC当中,我们并不用在写代码时进行commit操作,因为JDBC默认情况下,在执行完SQL以后,它会自动commit,无需手动commit。当然想改为手动commit也可以。
-
在jdbc技术中执行executeQuery()方法,会得到一个结果集ResultSet,这个结果集是由迭代器模式设计而来。迭代器模式的特点是,对于一个里面封装了多条数据的集合,通常遍历这个集合的方式,是通过写while循环语句进行遍历,在while遍历集合时,再通过集合中的next()方法进行顺序的获取下一行数据记录,从而将记录中所需要的信息,也就是记录中的列值通过get取出,再对取出的数据进行一系列后续的相关操作。
-
通过具体的代码演示可以发现,整个创建连接的过程是很麻烦的,一个是创建连接时,需要写一大堆参数,如果每次创建连接都要写这些参数,易错而枯燥,对编程而言会消耗很多时间,很不方便;再一个是在使用JDBC时,几乎每一句话,它都声明抛异常,到处得catch,让代码冗余杂乱,所以为了减少这些麻烦,通过配置文件对参数提取,并不冗余代码封装成一个工具,使其在每次创建连接时得以代码复用,以后创建连接就方便了。那么封装的思路就是管理连接的过程,是根据最终的需求反推过来,而不是一开始就想到的。首先,想法比较简单,就是写一个DBTool工具类,写一个静态方法getConnection(),这个方法能够被调用者调用,给调用者创建连接,然后把创建连接的代码放到这里,复用即可。后来又想到,在创建连接时需要在这个方法中写4个参数,如果在方法中直接写死的话,很不合适,因为软件上线时这个参数是要改的,比如,如果我们做的是一个OA,或者财务的软件产品,卖给不同的公司,卖给一家公司,就要改一遍参数,那么,为了降低修改这个参数的难度,使其改完之后不用开发工具编译,那我们就把这个参数写到一个配置文件里,这个配置文件,如果参数的结构很复杂,通常使用XML,如果参数只是比较简单的结构,就用properties文件,而使用properties文件来存储简单的参数,是Java中非常经典的用法。把参数存到properties里以后,利用Java自带的Properties类进行读取,因为读取操作是比较耗效率的,为了减少读取次数,保证只读一遍,又想到把读取操作写到static静态块中,而读取到的参数是要给getConnection()服务的,最后把参数存到变量之中,以供方法调用获取这些参数。
-
最终,按照程序的调用过程分析发现,一共是5步:
- 写配置文件,要先有配置文件
- 写静态块,保证只读取一次配置文件
- 声明参数的变量,将读取到的参数赋值给变量
- 写getConnection()方法,利用参数变量创建连接
- 调用者应用,通过getConnection()方法创建连接
- 完成对创建连接的代码封装,调用者通过连接执行SQL语句:
- 利用过DBTool工具创建一个连接Connection
- 调用Connection的createStatement()方法,得到一个Statement对象
- 调用Statement对象的方法执行SQL语句:
(1) 利用executeUpdate执行DML语句的增删改操作,并返回一个被语句影响的行数;
(2) 利用executeQuery执行DQL语句的查询操作,并返回一个ResultSet结果集对象,ResultSet的底层采用迭代器模式进行的设计,内部存储有多行多列的数据,通常通过wihle语句进行遍历,获取行数据,在通过ResultSet对象中相应的getXxx()方法获取所得行数据的某一列的值。
参考文献(References)
文中如有侵权行为,请联系me。。。。。。。。。。。。。
文中的错误,理解不到位的地方在所难免,也请指教!在成长过程中,也将继续不断完善,不作为专业文章。不喜勿喷。