JDBC 基本概念
什么是 JDBC
Java DataBase Connectivity,在 Java 语言中编写 SQL 语句,对数据库中数据进行 CRUD 操作
JDBC 相关类库在哪里
java.sql.*
JDBC 本质
本质上是 java.sql 包下的一堆接口,由 SUN 公司制定
JDBC 是体现接口作用的典型案例,降低了耦合度,提高了扩展力
无需关注哪个数据库,直接面向 JDBC 接口编程
JDBC 编程步骤
前置工作:在项目根目录下创建一个 lib 文件夹,将 mysql.jar 拷贝到该目录下,右键 add as library(等同于为 jar 包配置环境变量)
注册驱动 - 加载 Driver 类
通知 java 程序要连接哪个数据库
1 2 3 4 5
| DriverManager.deregisterDriver(new com.mysql.cj.jdbc.Driver());
Class.forName(com.mysql.cj.jdbc.Driver);
|
获取数据库连接 - 得到 Connection
开启 java 进程和 mysql 进程间的通道
1
| conn = DriverManager.getConnection(String url, String user, String password);
|
获取数据库操作对象
这个对象用来执行 SQL语句
1
| stmt = conn.createStatement();
|
执行 SQL 语句
执行 CRUD 操作
1 2 3 4 5 6 7
| String sql = "DML 语句"; stmt.execute(sql);
String sql = "select ..."; rs = stmt.executeQuery(sql);
|
处理查询结果集
如果第 4 步是 select 语句,才有这一步
1 2 3 4
| while (rs.next()) { String name = rs.getString("字段名"); System.out.println(name); }
|
释放资源
1 2 3 4 5 6 7 8
| if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } }
|
JDBC 编程完整代码
DML
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
| public class JDBC01 { public static void main(String[] args) throws SQLException { Connection conn = null; Statement stmt = null; try { Class.forName("com.mysql.cj.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "123456"); stmt = conn.createStatement(); String sql = "insert into student(id, name, sex) values(01, '张三', '男')"; stmt.execute(sql); } } catch (SQLException | ClassNotFoundException e) { e.printStackTrace(); } finally { if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
|
DQL
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
| public class JDBC01 { public static void main(String[] args) throws SQLException { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { Class.forName("com.mysql.cj.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "123456"); stmt = conn.createStatement(); String sql = "select name as '学生姓名' from student where id = 01"; rs = stmt.executeQuery(sql); while (rs.next()) { String name = rs.getString("学生姓名"); System.out.println(name); } } catch (SQLException | ClassNotFoundException e) { e.printStackTrace(); } finally { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
|
读取配置文件完成 JDBC
src 包下新建 resources 包,新建 db.properties 配置文件
在原来的程序下创建资源绑定器对象,得到配置文件中的信息
使用其他数据库时,直接修改配置文件即可
配置文件
1 2 3 4 5
| driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/jdbc user=root password=123456
|
程序代码
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
| public class JDBC_Properties_Test { public static void main(String[] args) throws SQLException { ResourceBundle bundle = ResourceBundle.getBundle("resources/db"); String driver = bundle.getString("driver"); String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password");
Connection conn = null; Statement stmt = null; ResultSet rs = null; try { Class.forName(driver); conn = DriverManager.getConnection(url, user, password); stmt = conn.createStatement(); String sql = "select name as '学生姓名' from student where id = 01"; rs = stmt.executeQuery(sql); while (rs.next()) { String name = rs.getString("学生姓名"); System.out.println(name); } } catch (SQLException | ClassNotFoundException e) { e.printStackTrace(); } finally { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
|
模拟用户登录(SQL 注入问题)
- 提供一个输入界面,可以让用户输入用户名和密码
- 底层数据库中需要一张用户表,表中存储用户信息
- 当 java 程序接收到用户名和密码时,连接数据库验证用户名和密码
程序代码
存在问题:SQL 注入
问题主要原因:用户提供的信息参与了 SQL 语句的编译
例如:
用户名 abc,密码 aaaaa ‘ or ‘ 1 = 1 也会显示登录成功
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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| public class SimulateLogin { public static void main(String[] args) { Map<String, String> userLoginInfo = initUI();
boolean ok = checkNameAndPwd(userLoginInfo.get("loginName"), userLoginInfo.get("loginPwd"));
System.out.println(ok ? "登录成功" : "登录失败"); }
private static boolean checkNameAndPwd(String loginName, String loginPwd) { boolean ok = false; Connection conn = null; Statement stmt = null; ResultSet rs = null;
ResourceBundle bundle = ResourceBundle.getBundle("resources/db"); String driver = bundle.getString("driver"); String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password");
try { Class.forName(driver); conn = DriverManager.getConnection(url, user, password); stmt = conn.createStatement(); String sql = "select * from t_user where login_name = '" + loginName + "' and login_pwd = '" + loginPwd + "'"; rs = stmt.executeQuery(sql); if (rs.next()) { ok = true; } } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } finally { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } return ok; }
private static Map<String, String> initUI() { System.out.println("--- 欢迎使用模拟系统!请输入用户名和密码 ---"); Scanner sc = new Scanner(System.in); System.out.print("用户名:"); String loginName = sc.nextLine(); System.out.print("密码:"); String loginPwd = sc.nextLine();
Map<String, String> userLoginInfo = new HashMap<>(); userLoginInfo.put("loginName", loginName); userLoginInfo.put("loginPwd", loginPwd);
return userLoginInfo; } }
|
sql 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| # 用户信息表,模拟用户登录使用 drop table if exists t_user;
create table t_user ( id int primary key auto_increment, login_name varchar(255) unique, login_pwd varchar(255) not null, real_name varchar(255) );
insert into t_user(login_name, login_pwd, real_name) values ('admin', '123', '管理员'), ('ZhangSan', '123', '张三');
select * from t_user;
|
解决 SQL 注入问题
SQL 注入的根本原因:先进行了字符串的拼接,再进行的 SQL 编译
步骤
完整代码
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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| public class SimulateLogin { public static void main(String[] args) { Map<String, String> userLoginInfo = initUI();
boolean ok = checkNameAndPwd(userLoginInfo.get("loginName"), userLoginInfo.get("loginPwd"));
System.out.println(ok ? "登录成功" : "登录失败"); }
private static boolean checkNameAndPwd(String loginName, String loginPwd) { boolean ok = false; Connection conn = null; PreparedStatement ps = null; ResultSet rs = null;
ResourceBundle bundle = ResourceBundle.getBundle("resources/db"); String driver = bundle.getString("driver"); String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password");
try { Class.forName(driver); conn = DriverManager.getConnection(url, user, password); String sql = "select * from t_user where login_name = ? and login_pwd = ?"; ps = conn.prepareStatement(sql); ps.setString(1, loginName); ps.setString(2, loginPwd); rs = ps.executeQuery(); if (rs.next()) { ok = true; } } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } finally { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } return ok; }
private static Map<String, String> initUI() { System.out.println("--- 欢迎使用模拟系统!请输入用户名和密码 ---"); Scanner sc = new Scanner(System.in); System.out.print("用户名:"); String loginName = sc.nextLine(); System.out.print("密码:"); String loginPwd = sc.nextLine();
Map<String, String> userLoginInfo = new HashMap<>(); userLoginInfo.put("loginName", loginName); userLoginInfo.put("loginPwd", loginPwd);
return userLoginInfo; } }
|
JDBC 事务
实际开发中需要将自动提交机制关闭,改为手动提交
得到 Connection 连接后,关闭自动提交机制
1 2
| conn = DriverManager.getConnection(url, user, password); conn.setAutoCommit(false);
|
执行 SQL 以后,手动提交
如果出现异常,手动回滚
1 2 3 4 5 6 7 8 9 10 11
| try { ... } catch (Exception e) { try { if (conn != null) { conn.rollback(); } } catch (SQLException ex) { ex.printStackTrace(); } }
|
JDBC 工具类封装
为了方便开发,可以封装一个 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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| public class DBUtil { private DBUtil() {
}
private static final ResourceBundle bundle = ResourceBundle.getBundle("resources/db");
static { try { Class.forName(bundle.getString("driver")); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
public static Connection getConnection() throws SQLException { String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password"); Connection conn = DriverManager.getConnection(url, user, password); return conn; }
public static void close(Connection conn, Statement stmt, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
|