JDBC API主要做3件事:与数据库建立连接、发送SQL语句、处理结果。
DriverManager类:依据数据库的不同,管理相应的JDBC驱动。
Connection接口:负责连接数据库并担任传送数据的任务。
Statement接口:由Connection产生,负责执行SQL语句。
ResultSet接口:负责保存和处理Statement执行后所产生的查询结果。
PreparedStatement接口:Statement的子接口,也有Connection产生,同样负责执行SQL语句。与Statement接口相比,具有高安全性、高性能、高可读性和高可维护性的优点。
JDBC访问数据库的步骤。
(1)加载JDBC驱动。
使用Class.forName()方法将给定的JDBC驱动类加载到Java虚拟机中。若系统中不存在给定的类,则会引发异常,异常类型为ClassNotFoundException。代码示例:
Class.forName("JDBC驱动类的名称");
(2)与数据库建立连接
DriverManager类是JDBC的管理层,作用于用户和驱动程序之间。DriverManager类跟踪可用的驱动程序,并在数据库和相应的驱动程序之间建立连接。当调用getConnection()方法时,DriverManager类首先从已加载的驱动程序列表中找到一个可以接收该数据库URL的驱动程序,然后请求该驱动程序使用相关的URL、用户名和密码连接到数据库中,于是就建立了与数据库的连接,创建连接对象并返回引用。代码示例:
Connection con=DriverManager.getConnection(数据连接字符串,数据库用户名,密码);
(3)发送SQL语句,并得到返回结果。
一旦建立连接,就使用该连接创建Statement接口的对象,并将SQL语句传递给他所连接的数据库。如果是查询操作,将返回类型为ResultSet的结果集,它包含执行SQL查询的结果。如果是其他操作,将根据调用方法的不同返回布尔值或操作影响的记录数目。代码示例:
Statement stmt=con.createStatement();
ResultSet rs=stmt.executeQuery("SELECT id,name FROM master");
(4)处理返回结果
主要是针对查询操作的结果集,通过循环取出结果集中每条记录并做出相应处理。处理结果的代码示例:
While(re.next()){
int id=rs.getInt("id");
String name=rs.getString("name");
System.out.println(id+" "+name);
}
两种常用的驱动方式:第一种是JDBC-ODBC桥连方式,适用与个人开发与测试,他通过ODBC与数据库进行连接。第二种是纯Java驱动方式,他直接通数据库进行连接,在生产型开发中,推荐使用纯Java驱动方式。
使用JDBC-ODBC桥连方式连接数据库,JDBC驱动类是“sun.jdbc.odbc.JdbcOdbcDriver”,数据库连接字符串将以“jdbc:odbc”开始,后面跟随数据源名称。因此,假设我们已经配置了一个叫“conn_epet”的ODBC数据源,数据库连接字符串就是“jdbc:odbc:conn_epet”,假定登录数据库系统的用户名为“sa”,口令为“sa”。具体实现代码如示例1所示。
我们使用纯Java驱动方式进行数据库连接,首先需要下载数据库厂商提供的驱动程序jar包,并将jar包引入工程中。。接下来就可以进行编程,与数据库建立连接。此处假定在SQL Server2008中已经建立了名称为“epet”的数据库,数据库用户名为“sa”,密码为“sa”,驱动程序包为sqljdbc2008.jar。
获取Connection对象后就可以进行各种数据库操作了,此时需要使用Connection对象创建Statement对象。Statement对象用于将SQL语句发送到数据库中,或者理解为执行SQL语句。Statement接口中包含很多基本数据库操作方法,以下为执行SQL命令的3个方法。
ResultSet executeQuery(String sql):可以执行SQL查询并获取ResultSet对象。
int executeUpdate(String sql):可以执行插入、删除、更新的操作。返回值是执行该操作所影响的行数。
boolean execute(String sql):可以执行任意SQL语句,若结果为ResultSet对象,则返回true;若其为更新计数或者不存在任何结果,则返回false。
使用Statement添加狗狗信息到数据库,只要创建Statement对象然后调用execute(String sql)方法或者executeUpdate(String sql)方法即可。这里关键是SQL语句的拼接,可以直接利用“+”运算符进行拼接,也可以利用StringBuffer类的append()方法进行拼接。拼接时要非常小心,尤其是引号、逗号和括号的拼接,避免出错。
如:insert into dog (name,health,love,strain) values ('name',health,love,'strain') 这里name,strain是字符串,health、love是int型。
具体代码如下:
//2、建立连接
conn=DriverManager.getConnection("jdbc:sqlserver://localhost:1433;DatabaseName=epet","sa","bdqn");
//3、插入狗狗信息到数据库
stmt=conn.createStatement();
StringBuffer sbSql=new StringBuffer("insert into dog (name,health,love,strain) values ('");
sbSql.append(name+"',");
sbSql.append(health+",");
sbSql.append(love+",'");
sbSql.append(strain+"')");
stmt.execute(sbSql.toString());
StringBuffer的使用。
Java定义了String和StringBuffer两个类来封装对字符串的各种操作。String类的字符串是常量,它们的值在创建之后不能更改。而StringBuffer类似于String的字符串缓冲区,通过某些方法调用可以改变该字符串的长度和内容,用于存放内容可以改变的字符串。如果StringBuffer生成了最终想要的字符串,可以用过toString()方法转换为一个String对象。
Java为字符串提供了字符串连接运算符“+”,可以把非字符串数据转换为字符串并连接成新的字符串。类似“+”运算符的功能也可以通过Stringbuffer类的append()方法实现。
例如:
int health=90,love=20;
String sql="select * from epet where health>"+health+" and love>"+love;
等效于
int health=90,love=20;
String sql=new StringBuffer()
.append("select * from epet where health>")
.append(health)
.append(" and love>")
.append(love)
.toString();
Result可以理解为有查询结果组成的一个二维表,每行代表一条记录,每列代表一个字段;并且存在一个光标,光标所指行为当前行,只能对结果集的当前行数据进行操作;光标初始位置是第一行之前(而不是指向第一行)。通过ResultSet的next()方法可以是光标向下移动一行,然后通过一系列getXxx()方法实现对当前行各列数据的操作。
若执行next()后光标指向结果集的某一行,则返回true;否则返回false。若光标已指向结果集最后一行,再次调用next()方法,会指向最后一行的后面,此时返回false。
getXxx()方法提供了获取当前行中某列值的途径,列号或列名可用于标识要从中获取数据的列(Xxx代表基本数据类型名,如Int、Float等,也可以是String)。例如,如果结果集中第一列的列名为id,存储类型为整型,那么可以使用两种方法获取存储在该列中的值,如“int id=rs.getInt(1);”或者“int id=rs.getInt("id");”。采用列名来标识列可读性强,建议多采用这种方式。
ResultSet接口常用方法及作用
方法名称 | 作用 |
boolean next() | 将光标从当前位置向下移动一行 |
boolean previous() | 将光标从当前位置向上移动一行 |
void close() | 关闭Result对象 |
int getInt(int columnIndex) | 以int的形式获取结果集当前行指定列号的值 |
Int getInt(String co) getInt(String columnLabel) | 以int的形式获取结果集当前行指定列名的值 |
float getFloat(int columnIndex) | 以float的形式获取结果集当前行指定列号的值 |
float getFloat(String columnLabel) | 以float的形式获取结果集当前行指定列名的值 |
String getString(int columnIndex) | 以String的形式获取结果集当前行指定列号的值 |
String getString(String columnLabel) | 以String的形式获取结果集当前行指定列名的值 |
int getRow() | 得到光标当前所指行的行号 |
boolean absolute(int row) | 光标移动到row指定的行 |
注意:
作为一种好的编程风格,应该在不需要ResultSet对象、Statement对象和Connection对象显式的关闭它们。语法形式为:public void close() throws SQLException
要按先ResultSet结果集,后Statement,最后Connection的顺序关闭资源,因为ResultSet是通过Statement执行SQL命令得到的,而Statement是需要在创建连接后才可以使用的,所以三者之间存在相互依存的关系,关闭时也必须按照依存关系进行。
用户如果不关闭ResultSet,当Statement关闭、重新执行或用于从多结果序列中获取下一个结果时,该ResultSet将被自动关闭。
PerparedStatement接口继承自Statement接口,PerparedStatement比普通的Statement对象使用起来更加灵活,更有效率。
使用PreparedStatement操作数据库的基本步骤有3步。
1、创建PreparedStatement对象。
通过Connection接口的preparedStatement(String sql)方法来创建PreparedStatement对象,SQL语句可具有一个或多个输入参数。这些输入参数的值在SQL语句创建时未被指定,而是为每个输入参数保留一个问号(“?”)作为占位符。
以下的代码段(其中con是Connection对象)将创建包含带有3个输入参数的SQL语句的PreparedStatement对象。
PreparedStatement pstmt=con.preparedStatement("update dog set health=?,love=? where id=?");
2、设置每个输入参数的值。
通过调用setXxx()方法来完成,其中,Xxx是与该参数相应的类型。例如,若参数是String类型,则使用的方法就是setString()。setXxx()方法的第一个参数是要设置的序数位置(从1开始计数),第二个参数是设置给该参数的值。例如,以下代码将第一个参数设为整型值80,第二个参数设为整型值15,第三个参数设为整形1。
pstmt.setInt(1,80);
pstmt.setInt(2,15);
pstmt.setInt(3,1);
3、执行SQL语句。
在设置了各个输入参数的值后,就可以调用PreparedStatement接口的3个执行方法之一来执行SQL语句。
ResultSet executeQuery():可以执行SQL查询并获取到ResultSet对象。
int executeUpdate():可以执行插入、删除和更新的操作,返回值是执行该操作所影响的行数。
boolean execute():可以执行任意SQL语句,若结果为ResultSet对象,则返回true;若其为更新计数或者不存在任何结果,则返回false。
注意这3个方法和Statement接口中3个方法名字相同、作用相同,但是不需要SQL语句做参数,SQL语句已经在创建对象PreparedStatement时指定了。例如:pstmt.executeUpdate();
创建PreparedStatement对象时会对SQL语句进行预编译,所以执行速度要快于Statement对象。因此,如果在程序中存在需要多次执行SQL语句时,应使用PreparedStatement对象来执行数据库操作,以提高效率。
PreparedStatement比Statement好在哪里?
1、提高了代码的可读性和可维护性。
虽然使用PreparedStatement来代替Statement会多几行代码,但避免了繁琐麻烦又容易出错的SQL语句拼接,提高了代码的可读性和可维护性。
2、提高了SQL语句执行的性能。
创建Statement对象时不使用SQL语句做参数,不会解析和编译SQL语句,每次调用方法执行SQL语句时都要进行SQL语句解析和编译操作,即操作相同仅仅是数据不同。
创建PreparedStatement对象时使用SQL语句做参数,会解析和编译该SQL语句。也可以使用带占位符的SQL语句做参数,在通过setXxx()方法给占位符赋值后执行SQL语句时无需再解析和编译SQL语句,直接执行即可。多次执行相同操作可以大大提高性能。
3、提高了安全性。
PreparedStatement使用预编译语句,传入的任何数据都不会和已经预编译的SQL语句进行拼接,避免了SQL注入攻击。