1. 什么是JDBC
JDBC(Java Database Connectivity,Java数据库连接),它是一套用于执行SQL语句的Java API。应用程序可通过这套API连接到关系型数据库,并使用SQL语句来完成对数据库中数据的查询、新增、更新和删除等操作。
应用程序使用JDBC访问数据库的方式如图1所示。
图1 应用程序通过JDBC访问数据库方式
2. JDBC常用API
(1)Driver接口
Driver接口是所有JDBC驱动程序必须实现的接口,该接口专门提供给数据库厂商使用。
注意:在编写JDBC程序时,必须要把所使用的数据库驱动程序或类库加载到项目的classpath中(这里指数据库的驱动JAR包)。
(2)DriverManager类
DriverManager类用于加载JDBC驱动并且创建与数据库的连接。其主要方法如表1所示。
表1 DriverManager类常用方法
方法声明 | 功能描述 |
static synchronized void registerDriver(Driver driver) | 该方法用于向DriverManager中注册给定的JDBC驱动程序 |
static Connection getConnection(String url,String user,String pwd) | 该方法用于建立和数据库的连接,并返回表示连接的Connection对象 |
说明:在实际开发中,通常不使用registerDriver(Driverdriver)注册驱动。因为JDBC驱动类Driver中有一段静态代码块,是向DriverManager注册一个Driver实例,当再次执行registerDriver(newDriver()),相当于实例化了两个Driver对象,因此在加载数据库驱动时通常使用Class类的静态方法forName()来实现。
(3) Connection接口
Connection接口代表Java程序和数据库的连接对象,只有获得该连接对象后,才能访问数据库,并操作数据表。Connection接口常用方法如表2所示。
表2 Connection接口
方法声明 | 功能描述 |
Statement createStatement() | 该方法用于返回一个向数据库发送语句的Statement对象 |
PreparedStatement prepareStatement(String sql) | 该方法用于返回一个PreparedStatement对象,该对象用于向数据库发送参数化的SQL语句 |
CallableStatement prepareCall(String sql) | 该方法用于返回一个CallableStatement对象,该对象用于调用数据库中的存储过程 |
(4) Statement接口
Statement是Java执行数据库操作的一个重要接口,它用于执行静态的SQL语句,并返回一个结果对象。
说明:Statement接口对象可以通过Connection实例的createStatement()方法获得,然后返回数据库的处理结果。Statement接口常用方法如表3所示。
表3 Statement常用方法
方法声明 | 功能描述 |
boolean execute(String sql) | 用于执行各种SQL语句,返回一个boolean类型的值,如果为true,表示所执行的SQL语句有查询结果,可通过Statement的getResultSet()方法获得查询结果 |
int executeUpdate(String sql) | 用于执行SQL中的insert、update和delete语句。该方法返回一个int类型的值,表示数据库中受该SQL语句影响的记录条数 |
ResultSet executeQuery(String sql) | 用于执行SQL中的select语句,该方法返回一个表示查询结果的ResultSet对象 |
(5)PreparedStatement接口
Statement接口封装了JDBC执行SQL语句的方法,虽然可以完成Java程序执行SQL语句的操作,但是在实际开发过程中往往需要将程序中的变量作为SQL语句的查询条件,而使用Statement接口操作这些SQL语句会过于繁琐,并且存在安全方面的问题。针对这一问题,JDBC API 中提供了扩展的PreparedStatement接口。PreparedStatement是Statement的子接口,用于执行预编译的SQL语句。
说明:PreparedStatement接口扩展了带有参数SQL语句的执行操作,应用接口中的SQL语句可以使用占位符“?”来代替其参数,然后通过setXxx()方法为SQL语句的参数赋值。
PreparedStatement接口常用方法如表4所示。
表4 PreparedStatement接口常用方法
方法声明 | 功能描述 |
int executeUpdate() | 在此PreparedStatement对象中执行 SQL 语句,该语句必须是一个DML语句或者是无返回内容的SQL 语句,如 DDL 语句 |
ResultSet executeQuery() | 在此PreparedStatement对象中执行 SQL 查询,该方法返回的是ResultSet对象 |
void setInt(int parameterIndex, int x) | 将指定参数设置为给定的int值 |
void setFloat(int parameterIndex, float x) | 将指定参数设置为给定的float值 |
void setString(int parameterIndex, String x) | 将指定参数设置为给定的String值 |
void setDate(int parameterIndex, Date x) | 将指定参数设置为给定的Date值 |
void addBatch() | 将一组参数添加到此PreparedStatement对象的批处理命令中 |
void setCharacterStream(int parameterIndex, java.io.Reader reader, int length) | 将指定的输入流写入数据库的文本字段 |
void setBinaryStream(int parameterIndex, java.io.InputStream x, int length) | 将二进制的输入流数据写入到二进制字段中 |
为SQL语句参数赋值时,可以通过输入参数与SQL类型相匹配的setXxx()方法。例如字段的数据类型为int或Integer,那么应该使用setInt()方法,也可以通过setObject()方法设置多种类型的输入参数。
// 假设users表中字段id、name、email类型分别是int、varchar、varchar
String sql = "INSERT INTO users(id,name,email) VALUES(?,?,?)";
PreparedStatement preStmt = conn.prepareStatement(sql);
preStmt.setInt(1, 1); //使用参数与SQL类型相匹配的方法
preStmt.setString(2, "zhangsan"); //使用参数与SQL类型相匹配的方法
preStmt.setObject(3, "zs@sina.com"); //使用setObject()方法设置参数
preStmt.executeUpdate();
(6)ResultSet接口
ResultSet接口用于保存JDBC执行查询时返回的结果集,该结果集封装在一个逻辑表格中。在ResultSet接口内部有一个指向表格数据行的游标(或指针),ResultSet对象初始化时,游标在表格的第一行之前,调用next()方法可将游标移动到下一行。如果下一行没有数据,则返回false。在程序中经常使用next()方法作为while循环的条件来迭代ResultSet结果集。ResultSet接口常用方法如表5所示。
表5 ResultSet接口常用方法
方法声明 | 功能描述 |
String getString(int columnIndex) | 用于获取指定字段的String类型的值,参数columnIndex代表字段的索引 |
String getString(String columnName) | 用于获取指定字段的String类型的值,参数columnName代表字段的名称 |
int getInt(int columnIndex) | 用于获取指定字段的int类型的值,参数columnIndex代表字段的索引 |
int getInt(String columnName) | 用于获取指定字段的int类型的值,参数columnName代表字段的名称 |
Date getDate(int columnIndex) | 用于获取指定字段的Date类型的值,参数columnIndex代表字段的索引 |
Date getDate(String columnName) | 用于获取指定字段的Date类型的值,参数columnName代表字段的名称 |
boolean next() | 将游标从当前位置向下移一行 |
boolean absolute(int row) | 将游标移动到此 ResultSet 对象的指定行 |
void afterLast() | 将游标移动到此 ResultSet 对象的末尾,即最后一行之后 |
void beforeFirst() | 将游标移动到此 ResultSet 对象的开头,即第一行之前 |
boolean previous() | 将游标移动到此 ResultSet 对象的上一行 |
boolean last() | 将游标移动到此 ResultSet 对象的最后一行 |
ResultSet接口中定义了大量的getXxx()方法,而采用哪种getXxx()方法取决于字段的数据类型。
程序既可以通过字段的名称来获取指定数据,也可以通过字段的索引来获取指定的数据,字段的索引是从1开始编号的。
例如,假设数据表的第1列字段名为id,字段类型为int,那么既可以使用getInt("id")获取该列的值,也可以使用getInt(1)获取该列的值。
3. JDBC编程的基本步骤
通常情况下,JDBC编程可分为如下几个基本步骤。
(1)加载数据库驱动
加载数据库驱动通常使用Class类的静态方法forName()来实现,使用格式:
Class.forName(DriverName);
上述代码中,DriverName代表的是数据库驱动类所对应的字符串。例如,要加载MySQL数据库的驱动可以采用如下代码。
Class.forName("com.mysql.jdbc.Driver");
(2)通过DriverManager获取数据库连接
DriverManager提供了一个getConnection()方法来获取数据库连接,使用格式如下。
Connection conn = DriverManager.getConnection(String url, String user, String pwd);
参数说明:
url:表示连接数据库的URL。
user:表示登录数据库的用户名。
pwd:表示登录数据库的密码。
用户名和密码通常由数据库管理员设置,而连接数据库的URL则有固定格式。如MySQL数据库的URL地址为。
jdbc:mysql://hostname:prot/databasename
上述代码中,jdbc:mysql:是固定写法,mysql指的是MySQL数据库;hostname指的是MySQL数据库所在的主机名或IP地址(例如数据库在本机上,hostname可以是localhost或127.0.0.1);port指的是连接数据库的端口号(默认为3306);databasename指的是要操作的数据库。
(3)通过Connection对象获取Statement对象
Connection创建Statement的方式有3种。
①createStatement():创建基本的Statement对象。
②prepareStatement(String sql):根据传递的SQL语句创建PreparedStatement对象。
③prepareCall(Stringsql):根据传入的SQL语句创建CallableStatement对象。
例创建基本的Statement对象,其代码如下所示。
Statement stmt = conn.createStatement();
(4)使用Statement执行SQL语句
可通过如下3种不同的方式来执行SQL语句。
①execute(String sql):用于执行任意的SQL语句。
②executeQuery(String sql):用于执行查询语句,返回ResultSet结果集对象。
③executeUpdate(String sql):主要用于执行DML(数据操作语言)和DDL(数据定义语言)语句。执行DML语句(INSERT、UPDATE或DELETE)时,会返回受SQL语句影响的行数,执行DDL(CREATE、ALTER)语句返回0。
例执行查询SQL,获取结果集:
// 执行SQL语句,获取结果集ResultSet
ResultSet rs = stmt.executeQuery(sql);
(5)操作ResultSet结果集
如果执行的SQL语句是查询语句,执行结果将返回一个ResultSet对象,该对象里保存了SQL语句查询的结果。程序可以通过操作该ResultSet对象来取出查询结果。
(6)关闭连接,释放资源
每次操作数据库结束后都要关闭数据库连接,释放资源,以重复利用资源。通常资源的关闭顺序与打开顺序相反,顺序是ResultSet、Statement(或PreparedStatement)和Connection。为了保证在异常情况下也能关闭资源,需要在try...catch的finally代码块中统一关闭资源。
4. JDBC编程示例
(1)添加mysql驱动
①在项目中新建一lib目录,将mysql的驱动程序复制到此目录中。
②选择菜单【file】-【project structure】,左侧选择“modules”,右侧中间选择“Dependencies”,然后点击右上角的“+”号,选择“1 jars or directories…”,如下图所示。
③在弹出的对话框中选择项目中lib目录下的mysql驱动,然后点击”ok”返回。
④此时在Dependencies中会多了刚才选中的mysql驱动,然后点击“ok”即可。
(2)数据库操作
假设要操作的MySQL数据库名为db_student,db_student数据库中有一个表为tb_stud,表中内容如下所示。
【例10-1】读取表中内容并显示
import java.sql.*;
public class JDBCShow {
public static void main(String[] args) throws SQLException {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/db_student?characterEncoding=utf8";
String username="root";
String password = "";
conn = DriverManager.getConnection(url,username,password);
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from tb_stud");
System.out.println("no\t\tname\tmath\tchinese\tenglish");
while(rs.next()){
String no = rs.getString("no");
String name = rs.getString("name");
int math = rs.getInt("math");
int chinese = rs.getInt("chinese");
int english = rs.getInt("english");
System.out.println(no + "\t" +name + "\t" + math + "\t\t" + chinese+"\t\t"+ english);
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(rs!=null) {
rs.close();
}
if(stmt!=null){
stmt.close();
}
if (conn!=null){
conn.close();
}
}
}
}
运行结果如下图所示。
【例10-2】先往表中插入记录,然后读取表中记录显示
import java.sql.*;
public class JDBCInsertShow {
public static void main(String[] args) throws SQLException {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/db_student?characterEncoding=utf8";
String username="root";
String password = "";
conn = DriverManager.getConnection(url,username,password);
stmt = conn.createStatement();
String sql = "insert into tb_stud(no,name,math,chinese,english) values(?,?,?,?,?)";
PreparedStatement preStmt =conn.prepareStatement(sql);
preStmt.setString(1,"1005");
preStmt.setString(2,"张小");
preStmt.setInt(3,80);
preStmt.setInt(4,99);
preStmt.setInt(5,90);
preStmt.executeUpdate();
rs = stmt.executeQuery("select * from tb_stud");
System.out.println("no\t\tname\tmath\tchinese\tenglish");
while(rs.next()){
String no = rs.getString("no");
String name = rs.getString("name");
int math = rs.getInt("math");
int chinese = rs.getInt("chinese");
int english = rs.getInt("english");
System.out.println(no + "\t" +name + "\t" + math + "\t\t" + chinese+"\t\t"+ english);
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(rs!=null) {
rs.close();
}
if(stmt!=null){
stmt.close();
}
if (conn!=null){
conn.close();
}
}
}
}
运行结果如下图所示。
此时数据库表中内容如下所示。
【例10-3】记录的增、删、改、显示操作
import java.sql.*;
public class JDBCOperator {
public static void main(String[] args) throws SQLException {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/db_student?characterEncoding=utf8";
String username = "root";
String pw = "";
conn = DriverManager.getConnection(url,username,pw);
stmt = conn.createStatement();
String sql = "update tb_stud set name='王明' where id=1"; //记录的修改
stmt.executeUpdate(sql);
sql = "insert into tb_stud values(null,'1004','张朋',70,70,70)"; //记录的添加
stmt.executeUpdate(sql);
sql = "delete from tb_stud where name='李军'"; //记录的删除
stmt.executeUpdate(sql);
rs = stmt.executeQuery("select * from tb_stud");
System.out.println("no\t\tname\tmath\tchinese\tenglish");
while(rs.next()){
String no = rs.getString("no");
String name = rs.getString("name");
int math = rs.getInt("math");
int chinese = rs.getInt("chinese");
int english = rs.getInt("english");
System.out.println(no + "\t" +name + "\t" + math + "\t\t" + chinese+"\t\t"+ english);
}
}catch(Exception e){
e.printStackTrace();
} }finally{
if (rs!=null){
rs.close();
}
if (stmt!=null) {
stmt.close();
}
if (conn!=null) {
conn.close();
}
}
}
运行结果如下图所示。
此时表中内容如下所示。