Java JDBC编程基础
JDBC简介
JDBC (Java Database Connectivity) 是Java语言中用于执行SQL语句的标准API,它提供了一套与数据库交互的类和接口,使开发者能够用统一的方式访问各种关系型数据库。JDBC位于java.sql
和javax.sql
包中,是Java企业级应用的基础组件之一。
JDBC的主要优势在于它的”编写一次,到处运行”的特性,通过不同的驱动程序,同一套JDBC代码可以连接不同的数据库系统,如MySQL、Oracle、SQL Server等。
JDBC架构
JDBC架构主要包括四个组件:
- JDBC API:提供了程序对数据库的访问能力
- JDBC驱动管理器:用于管理和选择合适的驱动程序
- JDBC驱动程序接口:连接数据库的标准接口
- JDBC驱动程序:特定数据库的实现
JDBC驱动类型
JDBC驱动程序分为四种类型:
- JDBC-ODBC桥驱动:通过ODBC连接数据库(已过时)
- 本地API驱动:部分Java代码,部分本地代码
- 网络协议驱动:纯Java驱动,通过中间服务器
- 本地协议驱动:纯Java驱动,直接与数据库交互
现在大多数数据库厂商提供的都是第四种类型的驱动程序。
JDBC编程步骤
使用JDBC访问数据库通常包括以下步骤:
- 导入JDBC包
- 注册JDBC驱动
- 建立数据库连接
- 创建Statement对象
- 执行SQL查询
- 处理结果集
- 关闭连接和释放资源
导入JDBC包
1 2 3 4 5
| import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement;
|
MySQL数据库示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| public class JDBCExample { static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver"; static final String DB_URL = "jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC";
static final String USER = "root"; static final String PASS = "password";
public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { Class.forName(JDBC_DRIVER);
System.out.println("连接数据库..."); conn = DriverManager.getConnection(DB_URL, USER, PASS);
System.out.println("实例化Statement对象..."); stmt = conn.createStatement(); String sql = "SELECT id, name, age FROM employees"; ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age");
System.out.print("ID: " + id); System.out.print(", 姓名: " + name); System.out.println(", 年龄: " + age); } rs.close(); stmt.close(); conn.close(); } catch (SQLException se) { se.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (stmt != null) stmt.close(); } catch (SQLException se2) { } try { if (conn != null) conn.close(); } catch (SQLException se) { se.printStackTrace(); } } System.out.println("再见!"); } }
|
PreparedStatement的使用
PreparedStatement
是Statement
的子接口,它表示预编译的SQL语句。使用PreparedStatement
有以下优势:
- 提高安全性,防止SQL注入攻击
- 提高性能,因为SQL语句只编译一次
- 处理特殊字符更方便
- 可以使用参数化查询
1 2 3 4 5 6 7 8 9 10 11
| String sql = "INSERT INTO employees(name, age) VALUES(?, ?)"; PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "张三"); pstmt.setInt(2, 30);
int rowsAffected = pstmt.executeUpdate(); System.out.println("插入了 " + rowsAffected + " 条记录");
|
事务管理
在JDBC中,默认情况下事务是自动提交的。如果需要手动控制事务,可以按照以下方式进行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, USER, PASS); conn.setAutoCommit(false); Statement stmt = conn.createStatement(); String sql1 = "UPDATE accounts SET balance = balance - 100 WHERE id = 1"; stmt.executeUpdate(sql1); String sql2 = "UPDATE accounts SET balance = balance + 100 WHERE id = 2"; stmt.executeUpdate(sql2); conn.commit(); System.out.println("事务执行成功"); } catch (SQLException e) { if (conn != null) { try { conn.rollback(); System.out.println("事务已回滚"); } catch (SQLException se) { se.printStackTrace(); } } e.printStackTrace(); } finally { if (conn != null) { try { conn.setAutoCommit(true); } catch (SQLException se) { se.printStackTrace(); } } }
|
批处理操作
当需要执行多条SQL语句时,可以使用批处理来提高性能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| conn.setAutoCommit(false);
PreparedStatement pstmt = conn.prepareStatement( "INSERT INTO employees(name, age) VALUES(?, ?)");
for (int i = 0; i < 10; i++) { pstmt.setString(1, "员工" + i); pstmt.setInt(2, 20 + i); pstmt.addBatch(); if (i % 500 == 0) { pstmt.executeBatch(); pstmt.clearBatch(); } }
pstmt.executeBatch();
conn.commit();
|
使用连接池
在实际应用中,为了提高性能并有效管理数据库连接,通常会使用连接池。以下是使用Apache DBCP连接池的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import org.apache.commons.dbcp2.BasicDataSource;
public class DbcpExample { private static BasicDataSource dataSource; static { dataSource = new BasicDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/testdb"); dataSource.setUsername("root"); dataSource.setPassword("password"); dataSource.setInitialSize(5); dataSource.setMaxTotal(20); dataSource.setMaxIdle(10); dataSource.setMinIdle(5); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } public static void main(String[] args) { try (Connection conn = getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM employees")) { while (rs.next()) { System.out.println(rs.getString("name")); } } catch (SQLException e) { e.printStackTrace(); } } }
|
使用Java 7的try-with-resources
Java 7引入的try-with-resources语句可以自动关闭实现了AutoCloseable
接口的资源,包括JDBC中的Connection
、Statement
和ResultSet
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static void printEmployees() { String sql = "SELECT id, name, age FROM employees"; try ( Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); PreparedStatement pstmt = conn.prepareStatement(sql); ResultSet rs = pstmt.executeQuery() ) { while (rs.next()) { System.out.println("ID: " + rs.getInt("id") + ", 姓名: " + rs.getString("name") + ", 年龄: " + rs.getInt("age")); } } catch (SQLException e) { e.printStackTrace(); } }
|
处理大型对象(LOB)
JDBC支持处理大型对象,如BLOB(二进制大型对象)和CLOB(字符大型对象):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| String sql = "SELECT id, name, photo FROM employees WHERE id=?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setInt(1, 1001); ResultSet rs = pstmt.executeQuery();
if (rs.next()) { Blob blob = rs.getBlob("photo"); InputStream in = blob.getBinaryStream(); }
sql = "INSERT INTO employees(name, photo) VALUES(?, ?)"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, "李四");
File file = new File("photo.jpg"); FileInputStream fis = new FileInputStream(file); pstmt.setBinaryStream(2, fis, (int)file.length()); pstmt.executeUpdate();
|
总结
JDBC是Java连接数据库的标准API,它提供了一套完整的解决方案,使Java应用能够与各种关系型数据库交互。虽然现在有许多更高级的ORM框架(如Hibernate和MyBatis),但JDBC仍然是这些框架的基础,了解JDBC对于深入理解Java数据库编程非常重要。
在实际应用中,建议使用连接池管理数据库连接,使用PreparedStatement
而不是Statement
来防止SQL注入,并利用事务确保数据的一致性。同时,合理使用批处理和处理大型对象的技术能够提高应用程序的性能。