1、概述
JDBC(Java DataBase Connectivity)
是 Java
提供的用于执行 SQL
语句一套 API
,可以为多种关系型数据库提供统一访问,由一套用 Java
语言编写的类和接口组成。
有了这套接口之后,开发者就不必为每一种数据库编写不同的访问逻辑,只需要在项目中加入数据库厂商提供的 JDBC
驱动,然后面向这套 Java API
接口开发自己的程序即可。
也就是说,在没有使用某个数据库特有语法、函数的情况下,开发者只要替换数据库驱动包、修改连接配置文件即可在应用层实现数据库的替换。
这就是面向接口编程的优势所在。
JDBC最核心的几个接口
Connection | 与特定数据库的连接,SQL语句在这个连接上执行并返回结果 |
Statement | 静态SQL语句对象 |
PreparedStatement | 预编译的SQL语句对象 |
ResultSet | 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成 |
2、第一个 demo
环境:JDK 1.6 + mysql 5.5
首先创建一个 java
项目 jdbc_demo
导入 mysql-connector-java-5.1.26-bin.jar
,这个数据库驱动可以到 http://mvnrepository.com 下载,关于 eclipse 的导包可以参考 http://5ijy01.duapp.com/it/java/java01046.html#f2-7
创建 package
和测试类,如下:
编写代码连接本地 MySQL
查询 test
下 t_user
表的数据
1 String driver = "com.mysql.jdbc.Driver"; 2 String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false"; 3 String username = "root"; 4 String password = "123456"; 5 6 Class.forName(driver); 7 8 Connection conn = null; 9 Statement stmt = null; 10 ResultSet rs = null; 11 12 try { 13 conn = DriverManager.getConnection(url, username, password); 14 15 stmt = conn.createStatement(); 16 rs = stmt.executeQuery("select id, username, role_id from t_user"); 17 18 while (rs.next()) { 19 int id = rs.getInt(1); 20 String uname = rs.getString("username"); 21 int roleId = rs.getInt(3); 22 System.out.println("用户信息: id=" + id + ", username=" + uname + ", role_id=" + roleId); 23 } 24 } catch (SQLException e) { 25 e.printStackTrace(); 26 } finally { 27 try { 28 if (conn != null) 29 conn.close(); 30 if (stmt != null) 31 stmt.close(); 32 if (rs != null) 33 rs.close(); 34 } catch (SQLException e) { 35 } 36 }
代码解读
url
是数据库连接地址,它告诉数据库驱动数据库服务器的 IP地址、服务监听端口、使用的数据库以及连接配置参数,这个 url
的配置方式通常在数据库文档中可以找到
Class.forName(driver)
这行代码的作用是加载数据库驱动类
下面的代码就比较简单了:使用 DriverManager
获取一个数据库连接 Connection
对象、从连接创建 Statement
对象,然后执行语句获取 ResultSet
结果集,最后迭代结果集获取数据
需要注意的是:我们使用 finally
代码块关闭数据库连接资源,因为不论 try
代码块是否捕获到异常, finally
代码块都会执行
3、API
Connection 的核心方法
void close() | 释放Connection对象的数据库和JDBC资源 |
void commit() | 提交所有更改,并释放Connection对象当前持有的所有数据库锁 |
Statement createStatement() | 创建一个Statement对象来将SQL语句发送到数据库 |
PreparedStatement prepareStatement(String sql) | 创建一个PreparedStatement对象来将参数化的SQL语句发送到数据库 |
void rollback() | 取消当前事务中进行的所有更改,并释放Connection对象当前持有的所有数据库锁 |
void setAutoCommit(boolean autoCommit) | 设置是否自动提交 |
void setReadOnly(boolean readOnly) | 设置为只读模式,作为驱动程序启用数据库优化的提示 |
void setTransactionIsolation(int level) | 试图将此Connection对象的事务隔离级别更改为给定的级别 |
Statement 的核心方法
void addBatch(String sql) | 将给定的SQL命令添加到Statement对象的当前命令列表中 |
void close() | 释放Statement对象的数据库和JDBC资源,而不是等待该对象自动关闭时发生此操作 |
boolean execute(String sql) | 执行给定的SQL语句,该语句可能返回多个结果 |
int[] executeBatch() | 将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组 |
ResultSet executeQuery(String sql) | 执行给定的SQL语句,该语句返回单个ResultSet对象 |
int executeUpdate(String sql) | 执行给定SQL语句,该语句可能为INSERT、UPDATE或DELETE语句,或者不返回任何内容的SQL语句(如DDL语句) |
ResultSet 的核心方法
void close() | 释放ResultSet对象的数据库和JDBC资源 |
BigDecimal getBigDecimal(int columnIndex) | 以具有全精度的java.math.BigDecimal的形式获取此ResultSet对象的当前行中指定列的值 |
BigDecimal getBigDecimal(String columnLabel) | 以具有全精度的java.math.BigDecimal的形式获取此ResultSet对象的当前行中指定列的值 |
Date getDate(int columnIndex) | 以java.sql.Date对象的形式获取此ResultSet对象的当前行中指定列的值 |
Date getDate(String columnLabel) | 以java.sql.Date对象的形式获取此ResultSet对象的当前行中指定列的值 |
double getDouble(int columnIndex) | 以double的形式获取此ResultSet对象的当前行中指定列的值 |
double getDouble(String columnLabel) | 以double的形式获取此ResultSet对象的当前行中指定列的值 |
int getInt(int columnIndex) | 以int的形式获取此ResultSet对象的当前行中指定列的值 |
int getInt(String columnLabel) | 以int的形式获取此ResultSet对象的当前行中指定列的值 |
ResultSetMetaData getMetaData() | 获取此ResultSet对象的列的编号、类型和属性 |
Object getObject(int columnIndex) | 以Object的形式获取此ResultSet对象的当前行中指定列的值 |
Object getObject(String columnLabel) | 以Object的形式获取此ResultSet对象的当前行中指定列的值 |
String getString(int columnIndex) | 以String的形式获取此ResultSet对象的当前行中指定列的值 |
String getString(String columnLabel) | 以String的形式获取此ResultSet对象的当前行中指定列的值 |
Timestamp getTimestamp(int columnIndex) | 以java.sql.Timestamp对象的形式获取此ResultSet对象的当前行中指定列的值 |
Timestamp getTimestamp(String columnLabel) | 以java.sql.Timestamp对象的形式获取此ResultSet对象的当前行中指定列的值 |
boolean next() | 将光标从当前位置向前移一行 |
4、编写 DBUtil 连接工具类
在一个项目里面,会有很多的业务需要访问数据库,如果每次都使用上面的方式获取连接,代码写起来会很麻烦,而且不便于维护
所以我们可以把加载驱动、获取连接的代码提取出来,单独封装成一个工具类,对外只提供一个 getConnection()
的方法来获取连接
jdbc.properties 配置文件
首先,在 src
下添加 jdbc.properties
配置文件,主要配置 driver
、url
、username
、password
等连接参数
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false username=root password=123456
然后,编写 DBUtil
类
1 public class DBUtil { 2 3 private static String driver; 4 private static String url; 5 private static String username; 6 private static String password; 7 8 private static Properties prop = new Properties(); 9 10 static { 11 try { 12 prop.load(DBUtil.class.getClassLoader().getResourceAsStream( 13 "jdbc.properties")); 14 15 driver = prop.getProperty("driver"); 16 url = prop.getProperty("url"); 17 username = prop.getProperty("username"); 18 password = prop.getProperty("password"); 19 20 Class.forName(driver); 21 22 } catch (IOException e) { 23 throw new RuntimeException("加载JDBC配置失败", e); 24 } catch (ClassNotFoundException e) { 25 throw new RuntimeException("加载JDBC配置失败", e); 26 } 27 } 28 29 public static Connection getConnection() throws SQLException { 30 return DriverManager.getConnection(url, username, password); 31 } 32 }
最后,编写 JdbcDemo2
测试类
1 Connection conn = null; 2 Statement stmt = null; 3 ResultSet rs = null; 4 5 try { 6 // 获取连接 7 conn = DBUtil.getConnection(); 8 9 stmt = conn.createStatement(); 10 // 执行查询并获取结果集 11 rs = stmt.executeQuery("select id, username, role_id from t_user"); 12 13 // 遍历结果集 14 while (rs.next()) { 15 int id = rs.getInt(1); 16 String uname = rs.getString("username"); 17 int roleId = rs.getInt(3); 18 System.out.println("用户信息: id=" + id + ", username=" + uname + ", role_id=" + roleId); 19 } 20 21 } catch (SQLException e) { 22 e.printStackTrace(); 23 } finally { 24 // 关闭连接,释放资源 25 // 略 26 }
项目结构如下:
5、字符串拼接方式执行动态查询
创建一个 UserDao
类,用上面的方式编写一个使用id查询用户的方法
1 public class UserDao { 2 3 public Map<String, Object> getUserInfoById(int id) throws SQLException { 4 5 // 拼接sql字符串 6 String sql = "select id, username, role_id from t_user where id = "+ id; 7 8 Connection conn = null; 9 Statement stmt = null; 10 ResultSet rs = null; 11 12 try { 13 // 获取连接 14 conn = DBUtil.getConnection(); 15 16 stmt = conn.createStatement(); 17 // 执行查询并获取结果集 18 rs = stmt.executeQuery(sql); 19 20 Map<String, Object> user = new HashMap<String, Object>(); 21 22 // 遍历结果集,封装数据并返回 23 if (rs.next()) { 24 String uname = rs.getString("username"); 25 int roleId = rs.getInt(3); 26 user.put("id", id); 27 user.put("username", uname); 28 user.put("role_id", roleId); 29 } 30 return user; 31 32 } catch (SQLException e) { 33 // 可以把异常抛给业务层的调用者 34 throw e; 35 } finally { 36 // 关闭连接,释放资源 37 // 略 38 } 39 } 40 41 public static void main(String[] args) { 42 UserDao uDao = new UserDao(); 43 try { 44 Map<String, Object> user = uDao.getUserInfoById(1); 45 System.out.println(user); 46 } catch (SQLException e) { 47 e.printStackTrace(); 48 } 49 } 50 }
这个方法很简单,也符合我们正常的编码习惯,但是 PreparedStatement 相比,执行效率较低,而且也有 SQL 的风险