JDBC是一种可用于执行SQL语句的Java API,是连接数据库和Java应用程序的纽带。
JDBC-ODBC桥是一个JDBC驱动程序,完成了从JDBC操作到ODBC操作之间的转换工作,允许JDBC驱动程序被用作ODBC的驱动程序。JDBC-ODBC桥作连接数据库的过渡性技术,现在已经不被Java广泛应用了,现在被广泛应用的是JDBC技术。但这并不表示JDBC-ODBC桥技术已经被淘汰,由于ODBC技术被广泛地使用,使得Java可以利用JDBC-ODBC桥访问几乎所有的数据库。JDBC-ODBC桥作为sun.jdbc.odbc包与JDK一起自动安装,不需要特殊配置。
JDBC的全称是Java DataBase Connectiviry,是一套面向对象的应用程序接口,指定了统一的访问各种关系型数据库的标准接口。JDBC是一种底层的API,因此访问数据库时需要在业务逻辑层中嵌入SQL语句。SQL语句是面向关系的,依赖于关系模型,所以通过JDBC技术访问数据库也是面向关系的。JDBC技术主要完成以下几个任务:
(1)与数据库建立一个连接。
(2)向数据库发送SQL语句。
(3)处理从数据库返回的结果。
需要注意的是,JDBC并不能直接访问数据库,必须依赖于数据库厂商提供的JDBC驱动程序。
在Java语言中提供了丰富的类和接口用于数据库编程,利用这些类和接口可以方便地进行数据访问和处理。这些类或接口都在java.sql包中。
Connection接口代表与特定的数据库的连接。要对数据表中数据进行操作,首先要获取数据库连接。Connection实例就像在应用程序与数据库之间开通了一条渠道。Connection接口常用方法如下:
Statement接口用于在已经建立连接的基础上向数据库发送SQL语句。在JDBC中有3中Statement对象,分别是Statement、PreparedStatement和CallableStatement。Statement对象用于执行不带参数的简单的SQL语句;PreparedStatement继承了Statement,用来执行动态的SQL语句;CallableStatement继承了PreparedStatement,用于执行对数据库的存储过程的调用。Statement接口中常用的方法如下:
PreparedStatement接口继承Statement,用于执行动态的SQL语句,通过PreparedStatement实例执行的SQL语句,将被预编译并保存到PreparedStatement实例中。从而可以反复地执行该SQL语句。PreparedStatement接口的常用方法如下:
DriverManager类用来管理数据库中的所有驱动程序。是JDBC的管理层,作用于用户和驱动程序之间,跟踪可用的驱动程序,并在数据库的驱动程序之间建立连接。此外,DriverManager类也处理诸如驱动程序登录时间限制及登录和跟踪信息的显示等事务。DriverManager类中的方法都是静态方法,所以在程序中无须对它进行实例化,直接通过类名就可以调用。DriverManager类的常用方法如下:
方法 | 说明 |
---|---|
getConnection(String url, String user, String password) | 尝试建立与给定数据库URL的连接。 |
setLoginTimeout(int seconds) | 设置驱动程序在识别驱动程序后尝试连接到数据库时等待的最长时间(秒)。 |
println(String message) | 将一条消息打印到当前JDBC日志流的消息。 |
ResultSet接口类似于一个临时表,用来暂时存放数据库查询操作所获得的结果集。ResultSet实例具有指向当前数据行的指针,指针开始的位置在第一条记录的前面,通过next()方法可以将指针向下移。
方法 | 说明 |
---|---|
getInt() | 以 int 形式获取此 ResultSet 对象的当前行的指定列值。如果列值为NULL,则返回值是0。 |
getFloat() | 以 float 形式获取此 ResultSet 对象的当前行的指定列值。如果列值为NULL,则返回值是0。 |
getDate() | 以 date 形式获取此 ResultSet 对象的当前行的指定列值。如果列值为NULL,则返回值是null。 |
getBoolean() | 以 boolean 形式获取此 ResultSet 对象的当前行的指定列值。如果列值为NULL,则返回值是null。 |
getString() | 以 String 形式获取此 ResultSet 对象的当前行的指定列值。如果列值为NULL,则返回值是null。 |
getObject() | 以 Object 形式获取此 ResultSet 对象的当前行的指定列值。如果列值为NULL,则返回值是null。 |
first() | 将光标移动到此 ResultSet对象中的第一行。 |
last() | 将光标移动到此 ResultSet对象中的最后一行。 |
next() | 将指针向下移一行。 |
beforeFirst() | 将指针移到集合的开头(第一行位置)。 |
afterLast() | 将指针移到集合的尾部(最后一行文章)。 |
absolute(int row) | 将指针移到 ResultSet对象给定编号的行。 |
isFirst() | 判断指针是否在此 ResultSet对象的第一行。 |
isLast() | 判断指针是否位于此 ResultSet对象的最后一行。 |
updateInt() | 使用 int值更新指定的列。 |
updateFloat() | 使用 float值更新指定的列。 |
updateLong() | 使用 long值更新指定的列。 |
updateString() | 使用 String值更新指定的列。 |
updateNString() | 使用 String值更新指定的列。 |
updateObject() | 使用 Object值更新指定的列。 |
updateNull() | 使用 null值更新指定的列。 |
updateDate() | 使用 java.sql.Date值更新指定的列。 |
updateDouble() | 使用 double值更新指定的列。 |
getRow() | 查看当前行的索引号。 |
insertRow() | 将插入行的内容插入到数据表。 |
updateRow() | 将当前行的内容同步到数据表。 |
deleteRow() | 删除当前行,但并不同步到数据表中,而是在执行close()方法后同步到数据表。 |
要对数据库表中的数据进行操作,应该首先建立与数据库的连接。通过JDBC的API中提供的各种类可实现对数据表中的数据进行查找、添加、修改、删除等操作。下面以操作MySQL数据库为例。
要访问数据库,首先要加载数据库的驱动程序(只需要在第一次访问数据库时加载一次),然后每次访问数据时创建一个Connection对象,之后执行操作数据库的SQL语句,最后在完成数据库操作后销毁前面创建的Connection对象,释放与数据库的连接。
示例:创建getConnection()方法,获取MySQL数据库的连接。
import java.sql.*; //导入java.sql包
// 数据库驱动
public static final String DRIVER_CLASS = "com.mysql.cj.jdbc.Driver";
// 数据库连接地址
public static final String DB_URL = "jdbc:mysql://localhost:3306/db_admin?serverTimezone=Hongkong&useUnicode=true&characterEncoding=utf8&useSSL=false";
// 数据库用户名称
public static final String DB_USER = "root";
// 数据库用户密码
public static final String DB_PASSWORD = "123456";
/**
* 获取数据库连接
*
* @author pan_junbiao
* @return 数据库连接对象
*/
public static Connection getConnection()
{
Connection conn = null;
try
{
// 加载数据库驱动类
Class.forName(DRIVER_CLASS);
System.out.println("数据库驱动加载成功");
// 获取数据库连接对象
conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
System.out.println("数据库连接成功");
} catch (ClassNotFoundException cnfe)
{
cnfe.printStackTrace();
} catch (SQLException sqle)
{
sqle.printStackTrace();
} catch (Exception ex)
{
ex.printStackTrace();
}
return conn;
}
执行结果:
示例:创建closeConnection()方法,关闭数据库连接。
/**
* 关闭数据库连接
*
* @author pan_junbiao
* @param conn 数据库连接对象
*/
public static void closeConnection(Connection conn)
{
try
{
if (conn != null)
{
conn.close();
System.out.println("数据库连接关闭成功");
}
} catch (SQLException sqle)
{
sqle.printStackTrace();
}
}
示例:创建closeOperate()方法,关闭相关数据操作对象。
/**
* 关闭数据库操作对象
*
* @author pan_junbiao
* @param res ResultSet对象
* @param stmt Statement对象
* @param conn Connection对象
*/
public static void closeOperate(ResultSet res, Statement stmt, Connection conn)
{
try
{
// 关闭ResultSet对象
if (res != null)
{
res.close();
}
// 关闭Statement对象
if (stmt != null)
{
stmt.close();
}
// 关闭Connection对象
if (conn != null)
{
conn.close();
}
System.out.println("关闭数据库操作对象完成");
} catch (SQLException sqle)
{
sqle.printStackTrace();
}
}
通过Statement对象,可以调用相应的方法实现对数据库的查询和修改,并将查询的结果集存放在ResultSet类的对象中。
示例:查询数据表,并遍历查询结果集。
/**
* 查询结果集
*
* @author pan_junbiao
*/
public static void searchData()
{
Connection conn = null; // 数据库连接对象
Statement stmt = null; // Statement对象
ResultSet res = null; // 结果集对象
try
{
// 获取数据库连接
conn = getConnection();
// 创建Statement对象
stmt = conn.createStatement();
// 查询SQL语句
String sql = "SELECT * FROM tb_user";
// 执行SQL语句,获取结果集
res = stmt.executeQuery(sql);
// 循环遍历结果集
while (res.next())
{
// 获取列值
int id = res.getInt("id");
String userName = res.getString("user_name");
String blogUrl = res.getString("blog_url");
String remark = res.getString("remark");
Timestamp createTime = res.getTimestamp("create_time");
// 输出列值
System.out.println("编号:" + id);
System.out.println("用户名称:" + userName);
System.out.println("博客地址:" + blogUrl);
System.out.println("备注信息:" + remark);
System.out.println("创建时间:" + createTime);
}
} catch (SQLException sqle)
{
sqle.printStackTrace();
} finally
{
// 关闭数据库操作对象
closeOperate(res, stmt, conn);
}
}
执行结果:
向数据库发送一个SQL语句,数据库中的SQL解释器负责把SQL语句生成底层的内部命令,然后执行该命令,完成相关的数据操作。如果不断地向数据库提交SQL语句,肯定会增加数据库中SQL解释器的负担,影响执行的速度。
对于JDBC,可以通过Connection对象的preparedStatement(String sql)方法对SQL语句进行预处理,生成数据库底层的内部命令,并将该命令封装在PreparedStatement对象中,通过调用该对象的相应方法执行底层数据库命令。这样应用程序能针对连接的数据库,实现将SQL语句解释为数据库底层的内部命令,然后让数据库执行这个命令,这样可以减轻数据库的负担,提高访问数据库的速度。
对SQL进行预处理时可以使用通配符“?”来代替任何的字段值。
示例:使用预处理语句查询编号为10的用户信息,并遍历结果集。
/**
* 使用预处理语句查询结果集
*
* @author pan_junbiao
*/
public static void preSearchData()
{
Connection conn = null; // 数据库连接对象
PreparedStatement preStmt = null; // PreparedStatement对象
ResultSet res = null; // 结果集对象
try
{
// 获取数据库连接
conn = getConnection();
// 查询SQL语句
String sql = "SELECT * FROM tb_user WHERE id = ?";
// 创建PreparedStatement对象
preStmt = conn.prepareStatement(sql);
// 设置参数
preStmt.setInt(1, 10);
// 执行SQL语句,获取结果集
res = preStmt.executeQuery();
// 循环遍历结果集
while (res.next())
{
// 获取列值
int id = res.getInt("id");
String userName = res.getString("user_name");
String blogUrl = res.getString("blog_url");
String remark = res.getString("remark");
Timestamp createTime = res.getTimestamp("create_time");
// 输出列值
System.out.println("编号:" + id);
System.out.println("用户名称:" + userName);
System.out.println("博客地址:" + blogUrl);
System.out.println("备注信息:" + remark);
System.out.println("创建时间:" + createTime);
}
}
catch (SQLException sqle)
{
sqle.printStackTrace();
}
finally
{
// 关闭数据库操作对象
closeOperate(res, preStmt, conn);
}
}
执行结果:
可以通过SQL语句对数据执行添加、修改和删除操作。可通过PreparedStatement类的指定参数动态地对数据表中原有数据进行修改操作,并通过executeUpdate()方法执行更新语句。
示例:使用预处理语句执行新增、修改、删除操作。
/**
* 数据操作
*
* @author pan_junbiao
*/
public static void operData()
{
int result = 0; // 执行条数
Connection conn = null; // 数据库连接对象
PreparedStatement preStmt = null; // 创建PreparedStatement对象
try
{
// 获取数据库连接
conn = getConnection();
// 新增操作
preStmt = conn.prepareStatement("INSERT INTO tb_user(id,user_name,blog_url,remark,create_time) VALUES(?,?,?,?,?)");
preStmt.setInt(1, 20); // 设置参数
preStmt.setString(2, "pan_junbiao的博客");
preStmt.setString(3, "https://blog.csdn.net/pan_junbiao");
preStmt.setString(4, "您好,欢迎访问 pan_junbiao的博客");
preStmt.setTimestamp(5, new java.sql.Timestamp(new java.util.Date().getTime()));
result = preStmt.executeUpdate(); // 执行操作
System.out.println("新增条数:" + result);
// 修改操作
preStmt = conn.prepareStatement("UPDATE tb_user SET user_name = ? WHERE id = ?");
preStmt.setString(1, "pan_junbiao的博客2"); // 设置参数
preStmt.setInt(2, 20);
result = preStmt.executeUpdate(); // 执行操作
System.out.println("修改条数:" + result);
// 删除操作
preStmt = conn.prepareStatement("DELETE FROM tb_user WHERE id = ?");
preStmt.setInt(1, 20); // 设置参数
result = preStmt.executeUpdate(); // 执行操作
System.out.println("删除条数:" + result);
}
catch (SQLException sqle)
{
sqle.printStackTrace();
}
finally
{
// 关闭数据库操作对象
closeOperate(null, preStmt, conn);
}
}
执行结果:
使用Statement接口提供的executeBatch()方法,将一批命令提交到数据库以执行,并且所有命令都执行成功,返回一个更新计数的数组。数组元素的排序与SQL语句的添加顺序对应。
示例:使用Statement接口提供的executeBatch()方法,执行批量新增数据操作。
/**
* 批量新增数据
*
* @author pan_junbiao
*/
public static void addBatchData()
{
int result = 0; // 执行条数
Connection conn = null; // 数据库连接对象
PreparedStatement preStmt = null; // PreparedStatement对象
try
{
// 获取数据库连接
conn = getConnection();
// SQL语句
String sql = "INSERT INTO tb_user(id,user_name,blog_url,remark,create_time) VALUES(?,?,?,?,?)";
// 创建PreparedStatement对象
preStmt = conn.prepareStatement(sql);
// 循环创建添加数据
for (int i = 1; i <= 10; i++)
{
preStmt.setInt(1, i);
preStmt.setString(2, "pan_junbiao的博客_" + i);
preStmt.setString(3, "https://blog.csdn.net/pan_junbiao");
preStmt.setString(4, "您好,欢迎访问 pan_junbiao的博客");
preStmt.setTimestamp(5, new java.sql.Timestamp(new java.util.Date().getTime()));
preStmt.addBatch(); // 添加批量处理命令
}
int[] rows = preStmt.executeBatch();
result = rows.length;
System.out.println("批量执行条数:" + result);
} catch (SQLException sqle)
{
sqle.printStackTrace();
} finally
{
// 关闭数据库操作对象
closeOperate(null, preStmt, conn);
}
}
执行结果:
(1)程序执行结果:
(2)数据库查询结果: