JDBC in Java:深入理解与高效应用
简介
在Java开发中,数据库交互是非常常见的需求。Java Database Connectivity(JDBC)为Java程序员提供了一种标准的、与数据库无关的方式来访问各种关系型数据库。通过JDBC,开发者可以使用相同的Java代码来操作不同的数据库,如MySQL、Oracle、SQL Server等,极大地提高了代码的可移植性和开发效率。本文将详细介绍JDBC的基础概念、使用方法、常见实践以及最佳实践,帮助读者更好地掌握JDBC在Java中的应用。
目录
- JDBC基础概念
- 什么是JDBC
- JDBC API 组成部分
- JDBC驱动类型
- JDBC使用方法
- 加载JDBC驱动
- 建立数据库连接
- 创建SQL语句执行对象
- 执行SQL语句
- 处理结果集
- 关闭资源
- JDBC常见实践
- 插入数据
- 查询数据
- 更新数据
- 删除数据
- JDBC最佳实践
- 数据库连接池
- 使用PreparedStatement防止SQL注入
- 事务管理
- 小结
- 参考资料
JDBC基础概念
什么是JDBC
JDBC是Java语言中用于执行SQL语句的API,它提供了一系列的接口和类,用于连接数据库、执行SQL语句并处理结果。JDBC允许Java程序与各种关系型数据库进行交互,而不需要关心底层数据库的具体实现。
JDBC API 组成部分
- DriverManager:用于管理JDBC驱动程序的加载和数据库连接的建立。
- Connection:代表与数据库的连接,通过它可以创建SQL语句执行对象。
- Statement:用于执行静态SQL语句。
- PreparedStatement:用于执行预编译的SQL语句,能够有效防止SQL注入攻击。
- CallableStatement:用于执行存储过程。
- ResultSet:用于存储SQL查询语句的执行结果。
JDBC驱动类型
- Type 1(JDBC-ODBC桥接驱动):通过ODBC驱动来访问数据库,需要在客户端安装ODBC驱动,现已较少使用。
- Type 2(本地API驱动):使用本地代码来访问数据库,性能较好,但可移植性较差。
- Type 3(网络协议驱动):通过网络协议与中间件通信,中间件再与数据库通信,适合分布式应用。
- Type 4(纯Java驱动):完全用Java编写,直接与数据库通信,具有良好的可移植性和性能,是目前最常用的驱动类型。
JDBC使用方法
加载JDBC驱动
在使用JDBC之前,需要先加载相应的数据库驱动。不同的数据库有不同的驱动,例如MySQL的驱动是com.mysql.cj.jdbc.Driver
。可以通过以下两种方式加载驱动:
方式一:使用Class.forName()
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
方式二:使用DriverManager
import com.mysql.cj.jdbc.Driver;
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException e) {
e.printStackTrace();
}
建立数据库连接
加载驱动后,需要建立与数据库的连接。使用DriverManager.getConnection()
方法来获取Connection
对象,该方法需要传入数据库的URL、用户名和密码。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseConnection {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
System.out.println("数据库连接成功!");
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
创建SQL语句执行对象
建立连接后,需要创建SQL语句执行对象。根据不同的需求,可以创建Statement
、PreparedStatement
或CallableStatement
。
创建Statement
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.SQLException;
public class StatementExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
Statement statement = connection.createStatement();
// 执行SQL语句
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
创建PreparedStatement
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class PreparedStatementExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "SELECT * FROM users WHERE age >?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 30);
// 执行SQL语句
preparedStatement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
执行SQL语句
创建SQL语句执行对象后,可以执行SQL语句。Statement
和PreparedStatement
都有executeQuery()
、executeUpdate()
和execute()
方法来执行不同类型的SQL语句。
执行查询语句(executeQuery()
)
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
public class QueryExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
Statement statement = connection.createStatement();
String sql = "SELECT * FROM users";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age);
}
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
执行更新语句(executeUpdate()
)
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class UpdateExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "UPDATE users SET age =? WHERE id =?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 35);
preparedStatement.setInt(2, 1);
int rowsAffected = preparedStatement.executeUpdate();
System.out.println("更新的行数: " + rowsAffected);
preparedStatement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
处理结果集
执行查询语句后,会返回一个ResultSet
对象,用于存储查询结果。可以使用ResultSet
的next()
方法来遍历结果集,并使用相应的get
方法获取每一行的数据。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
public class ResultSetExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
Statement statement = connection.createStatement();
String sql = "SELECT * FROM users";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age);
}
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
关闭资源
使用完数据库连接、SQL语句执行对象和结果集后,需要及时关闭它们,以释放资源。关闭资源的顺序应该是:先关闭ResultSet
,再关闭Statement
或PreparedStatement
,最后关闭Connection
。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
public class CloseResourcesExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = DriverManager.getConnection(url, username, password);
statement = connection.createStatement();
String sql = "SELECT * FROM users";
resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
JDBC常见实践
插入数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class InsertExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "INSERT INTO users (name, age) VALUES (?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "John");
preparedStatement.setInt(2, 25);
int rowsAffected = preparedStatement.executeUpdate();
System.out.println("插入的行数: " + rowsAffected);
preparedStatement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
查询数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class SelectExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "SELECT * FROM users WHERE age >?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 30);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age);
}
resultSet.close();
preparedStatement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
更新数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class UpdateExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "UPDATE users SET age =? WHERE id =?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 35);
preparedStatement.setInt(2, 1);
int rowsAffected = preparedStatement.executeUpdate();
System.out.println("更新的行数: " + rowsAffected);
preparedStatement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
删除数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DeleteExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "DELETE FROM users WHERE id =?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 1);
int rowsAffected = preparedStatement.executeUpdate();
System.out.println("删除的行数: " + rowsAffected);
preparedStatement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
JDBC最佳实践
数据库连接池
频繁地创建和销毁数据库连接会消耗大量的系统资源,影响应用程序的性能。使用数据库连接池可以预先创建一定数量的数据库连接,并在需要时从连接池中获取连接,使用完后再将连接放回连接池,从而提高数据库连接的复用率,提升应用程序的性能。常见的数据库连接池有HikariCP、C3P0、DBCP等。
使用HikariCP示例
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class ConnectionPoolExample {
private static HikariDataSource dataSource;
static {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("password");
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
dataSource = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void main(String[] args) {
try {
Connection connection = getConnection();
System.out.println("从连接池获取到连接: " + connection);
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
使用PreparedStatement防止SQL注入
Statement
在执行SQL语句时,会将用户输入的内容直接拼接到SQL语句中,如果用户输入恶意的SQL语句,就可能导致SQL注入攻击。而PreparedStatement
会先预编译SQL语句,将参数部分用占位符代替,然后再将参数值传入,从而有效防止SQL注入攻击。
```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;
public class PreventSQLInjectionExample { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/mydb"; String username = "root"; String password = "password"; String input = "admin' OR '