目录
代码: 获得连接 DriverManager.getConnection()
代码: 获取SQL执行平台Statement对象, 创建表executeUpdate
代码: 处理结果集 ResultSet类 , next(), getXXX();
代码: JDBC工具类的封装, 常量参数, getConnection方法, close方法
代码: JDBC增删改, getConnection, createStatement, executeUpdate, close
代码: JDBC 查 getConnection, createStatement, executeQuery, close
代码: 使用预处理解决SQL注入, getConnection, prepareStatement, executeQuery, close
代码: 使用 Statement对象 和 PreparedStatement对象进行插入操作
5.8 Statement 与 PreparedStatement的区别
代码: JDBC 操作事务的方式, setAutoCommit(), commit(), rollback()
1. JDBC 概述
1.3 JDBC 原理
JDBC就是由sun公司定义的一套操作所有关系型数据库的规则(接口),而数据库厂商需要实现这套接口,提供数据库 驱动jar包, 我们可以使用这套接口编程,真正执行的代码是对应驱动包中的实现类。
2. JDBC 开发
2.1 数据准备
-- 数据准备
-- 创建 jdbc_user表
CREATE TABLE jdbc_user (
id INT PRIMARY KEY AUTO_INCREMENT ,
username VARCHAR(50),
PASSWORD VARCHAR(50),
birthday DATE
);
-- 添加数据
INSERT INTO jdbc_user (username, PASSWORD,birthday)
VALUES('admin1', '123','1991/12/24'),
('admin2','123','1995/12/24'),
('test1', '123','1998/12/24'),
('test2', '123','2000/12/24');
2.2 MySql驱动包
- 将MySQL驱动包添加到jar包库文件夹中,Myjar文件夹,用于存放当前项目需要的所有jar包
- 在 idea中 配置jar包库的位置
- 创建一个新的项目jdbc_task01, 配置jar包库
2.3 API使用1.注册驱动
代码: 注册驱动演示 forName()
public class JDBCDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
//1.注册驱动
// forName 方法执行将类进行初始化
Class.forName("com.mysql.jdbc.Driver");
}
}
代码: 注册驱动源码分析 Driver类
//Driver类是由Mysql驱动包提供的一个实现类, 实现了java.sql.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
//静态代码块, 随着类的加载而加载, 只加载一次
static {
try {
//DriverManager类就是驱动管理类 registerDriver方法就是用来注册驱动的
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
2.4 API使用: 2.获得连接
- Connection 接口,代表一个连接对象 ,具体的实现类由数据库的厂商实现
- 使用 DriverManager类的静态方法,getConnection可以获取数据库的连接
1) getConnection方法 3个 连接参数说明
2) 对URL的详细说明
jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8
- JDBC规定url的格式由三部分组成,每个部分中间使用冒号分隔。
第一部分是协议 jdbc,这是固定的;
第二部分是子协议,就是数据库名称,连接mysql数据库,第二部分当然是mysql了;
第三部分是由数据库厂商规定的,我们需要了解每个数据库厂商的要求,mysql的第三部分分别由数据 库服务器的IP地址(localhost)、端口号(3306),以及要使用的 数据库名称 组成。
//2.获取连接 url,用户名, 密码
String url = "jdbc:mysql://localhost:3306/db4";
Connection con = DriverManager.getConnection(url, "root", "123456");
代码: 获得连接 DriverManager.getConnection()
public class JDBCDemo02 {
public static void main(String[] args) throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接 url,用户名, 密码
String url = "jdbc:mysql://localhost:3306/db4";
Connection con = DriverManager.getConnection(url, "root", "123456");
//com.mysql.jdbc.JDBC4Connection@2e3fc542
System.out.println(con);
}
}
2.5 API 使用: 3.获取语句执行平台
- 通过Connection 的 createStatement方法 获取sql语句执行对象
- Statement : 代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象
代码: 获取SQL执行平台Statement对象, 创建表executeUpdate
public class JDBCDemo03 {
public static void main(String[] args) throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接 url,用户名, 密码
String url = "jdbc:mysql://localhost:3306/db4";
Connection con = DriverManager.getConnection(url, "root", "123456");
//3.获取 Statement对象
Statement statement = con.createStatement();
//4.执行创建表操作
String sql = "create table test01(id int, name varchar(20),age int);";
//5.增删改操作 使用executeUpdate,增加一张表
int i = statement.executeUpdate(sql);
//6.返回值是受影响的函数
System.out.println(i);
//7.关闭流
statement.close();
con.close();
}
}
2.6 API 使用: 4.处理结果集
- 只有在进行查询操作的时候, 才会处理结果集
2.6.1 ResultSet接口
- 作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。
代码: 处理结果集 ResultSet类 , next(), getXXX();
package com.lg.jdbc01;
import java.sql.*;
/**
* @author CH
* @date 2020/10/26 13:45
*/
public class JDBCDemo02 {
public static void main(String[] args) throws Exception{
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db4", "root", "123456");
//3.获取语句执行平台对象
Statement statement = con.createStatement();
//4.执行查询操作, 使用executeQuery()
String sql = "select * from jdbc_user";
//resultSet 是结果集对象
ResultSet resultSet = statement.executeQuery(sql);
/*
//处理结果集对象
boolean next = resultSet.next(); //判断是否有下一条数据
System.out.println("next = " + next);
//获取id
int id = resultSet.getInt("id"); //通过列名获取
System.out.println("通过列名获取id = " + id); //通过列名获取id = 1
int id = resultSet.getInt(1);
System.out.println("通过列号获取id = " + id); //通过列号获取id = 1
*/
//通过while循环遍历获取resultSet中的数据
while(resultSet.next()) {
//获取id
int id = resultSet.getInt("id");
//获取姓名
String username = resultSet.getString("username");
//获取密码
String password = resultSet.getString("password");
//获取生日
Date birthday = resultSet.getDate("birthday");
System.out.println(id + ": " + username + ": " + password + ": " + birthday);
}
//5.关闭流操作
resultSet.close();
statement.close();
con.close();
}
}
2.7 API 使用: 5.释放资源
1) 需要释放的对象:ResultSet 结果集(只有查询会用到),Statement 语句,Connection 连接
2) 释放原则:先开的后关,后开的先关。ResultSet ==> Statement ==> Connection
3) 放在哪个代码块中:finally 块
- 与IO流一样,使用后的东西都需要关闭!关闭的顺序是先开后关, 先得到的后关闭,后得到的先关闭
代码: 资源的关闭 close()
package com.lg.jdbc01;
import java.sql.*;
/**
* @author CH
* @date 2020/10/27 16:15
*/
public class JDBCDemo03 {
public static void main(String[] args) {
Connection con = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//1.注册驱动 省略
//2.获取连接
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db4","root","123456");
//3.获取语句执行对象
statement = con.createStatement();
//4.执行SQL
String sql = "select * from jdbc_user";
resultSet = statement.executeQuery(sql);
//5.处理结果集对象
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if(null != resultSet) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (null != statement) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (null != con) {
try {
con.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
2.8 步骤总结
1. 获取驱动(可以省略)
2. 获取连接
3. 获取Statement对象
4. 处理结果集(只在查询时处理)
5. 释放资源
3. JDBC实现增删改查
3.1 JDBC工具类
- 什么时候自己创建工具类?
1) 如果一个功能经常要用到,我们建议把这个功能做成一个工具类,可以在不同的地方重用。
2) “获得数据库连接”操作,将在以后的增删改查所有功能中都存在,可以封装工具类JDBCUtils。
提供获取连接对象的方法,从而达到代码的重复利用。
- 工具类包含的内容
1) 可以把几个字符串定义成常量:用户名,密码,URL,驱动类
2) 得到数据库的连接:getConnection()
3) 关闭所有打开的资源:close()
代码: JDBC工具类的封装, 常量参数, getConnection方法, close方法
package com.lg.utils;
import java.sql.*;
/**
* @author CH
* @date 2020/10/27 16:26
* JDBC工具类的封装
*/
public class JDBCUtils {
//1. 定义字符串常量, 记录获取连接所需要的信息
public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8";
public static final String USER = "root";
public static final String PASSWORD = "123456";
//2. 静态代码块, 随着类的加载而加载
static{
try {
//注册驱动
Class.forName(DRIVERNAME);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//3.获取连接的静态方法
public static Connection getConnection(){
try {
//获取连接对象
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
//返回连接对象
return connection;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
//4.关闭资源的方法
//不操作结果集的关闭方法
public static void close(Connection con, Statement st){
if(con != null && st != null){
try {
st.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//操作结果集的关闭方法
public static void close(Connection con, Statement st, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
close(con,st);
}
}
3.2 DML操作 (增删改)
- 解决插入中文乱码问题.
jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8
characterEncoding=UTF-8 指定字符的编码、解码格式。
代码: JDBC增删改, getConnection, createStatement, executeUpdate, close
package com.lg.jdbc02;
import com.lg.utils.JDBCUtils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author CH
* @date 2020/10/27 16:44
*/
public class TestDML {
/*
插入数据
*/
@Test
public void testInsert() throws SQLException {
//1.通过JDBCUtils工具类 获取连接
Connection con = JDBCUtils.getConnection();
//2.获取Statement对象
Statement statement = con.createStatement();
// 3.1 编写SQL
String sql = "insert into jdbc_user values(null, '张百万', '123', '2020/11/11')";
//3.2 执行 sql
int i = statement.executeUpdate(sql);
System.out.println(i);
//4. 关闭流
JDBCUtils.close(con,statement);
}
/*
更新操作 根据id修改用户名
*/
@Test
public void testUpdate() throws SQLException {
Connection con = JDBCUtils.getConnection();
Statement statement = con.createStatement();
String sql = "update jdbc_user set username = '刘能' where id = 1";
statement.executeUpdate(sql);
JDBCUtils.close(con,statement);
}
/*
删除操作
删除 id 为 1 和 2 的数据
*/
@Test
public void testDelete() throws SQLException {
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
String sql = "delete from jdbc_user where id in(1,2)";
statement.executeUpdate(sql);
JDBCUtils.close(connection,statement);
}
}
3.3 DQL操作 (查)
代码: JDBC 查 getConnection, createStatement, executeQuery, close
package com.lg.jdbc02;
import com.lg.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author CH
* @date 2020/10/27 17:39
*/
public class TestDQL {
//查询姓名为张百万的一条数据
public static void main(String[] args) throws SQLException {
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.获取语句
Statement statement = connection.createStatement();
//3.编写SQL
String sql = "select * from jdbc_user where username = '张百万'";
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()) {
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
String birthday = resultSet.getString("birthday");
System.out.println("查询到的数据是→→ " + id + ", " + username + ", " + password + ", " + birthday);
}
//4. 关闭资源
JDBCUtils.close(connection,statement,resultSet);
}
}
4. SQL注入问题
4.1 Sql注入演示
1) 向jdbc_user表中 插入两条数据
-- 插入2条数据
INSERT INTO jdbc_user VALUES(NULL,'jack','123456','2020/2/24');
INSERT INTO jdbc_user VALUES(NULL,'tom','123456','2020/2/24');
2) SQL注入演示
- 如果这是一个登陆操作,那么用户就登陆成功了.显然这不是我们想要看到的结果
-- 填写一个错误的密码
SELECT * FROM jdbc_user WHERE username = 'tom' AND PASSWORD = '123' OR '1' = '1';
4.2 sql注入案例:用户登陆
- 需求
用户在控制台上输入用户名和密码, 然后使用 Statement 字符串拼接的方式 实现用户的登录。
- 步骤
1) 得到用户从控制台上输入的用户名和密码来查询数据库
2) 写一个登录的方法
a) 通过工具类得到连接
b) 创建语句对象,使用拼接字符串的方式生成 SQL 语句
c) 查询数据库,如果有记录则表示登录成功,否则登录失败
d) 释放资源
代码: SQL注入演示, 注意代码步骤 3 的注释
package com.lg.jdbc03;
import com.lg.utils.JDBCUtils;
import javax.swing.*;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
/**
* @author CH
* @date 2020/10/27 18:52
* 用户登录案例
*/
public class TestLogin01 {
public static void main(String[] args) throws SQLException {
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.获取Statement对象
Statement statement = connection.createStatement();
//3.获取用户输入的用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名: ");
String username = sc.nextLine();
System.out.println("请输入密码: "); //这边输入→ 123' or '1'=’1 时也会登陆成功
String pw = sc.nextLine();
//4.拼接SQL语句
String sql = "select * from jdbc_user where username = '"+ username + "' and password = '" + pw + "'";
System.out.println(sql);
//5.执行SQL获取结果集
ResultSet resultSet = statement.executeQuery(sql);
//6.处理结果集
if(resultSet.next()) {
System.out.println("登陆成功! 欢迎您:" + username);
} else {
System.out.println("登陆失败!");
}
//7.关闭流
JDBCUtils.close(connection,statement,resultSet);
}
}
4.3 注入问题分析
1) 什么是SQL注入?
用户输入的内容作为了 SQL 语句语法的一部分,改变了 原有SQL 真正的意义,此问题称为 SQL 注入 .
2) 如何实现的注入
根据用户输入的数据,拼接处的字符串
select * from jdbc_user where username = 'abc' and password = 'abc' or '1'='1'
name='abc' and password='abc' 为假 '1'='1' 真
相当于 select * from user where true=true; 查询了所有记录
3) 如何解决
要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进 行简单的字符串拼接。
5. 预处理对象
5.1 PreparedStatement 接口介绍
- PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句对象.
- 预编译: 是指SQL 语句被预编译,并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。
5.2 PreparedStatement 特点
- 因为有预先编译的功能,提高 SQL 的执行效率。
- 可以有效的防止 SQL 注入的问题,安全性更高
5.3 获取PreparedStatement对象
- 通过Connection创建PreparedStatement对象
5.4 PreparedStatement接口常用方法
5.5 使用PreparedStatement的步骤
1) 编写 SQL 语句,未知内容使用?占位:
"SELECT * FROM jdbc_user WHERE username=? AND password=?";
2) 获得 PreparedStatement 对象
3) 设置实际参数:setXxx( 占位符的位置, 真实的值)
4) 执行参数化 SQL 语句
5)关闭资源
5.6 使用PreparedStatement完成登录案例
- 步骤:
1.获取数据库连接对象
2.编写SQL 使用? 占位符方式
3.获取预处理对象 (预编译对象会将Sql发送给数据库 进行预编译)
4.提示用户输入用户名 & 密码
5.设置实际参数:setXxx(占位符的位置, 真实的值)
6.执行查询获取结果集
7.判断是否查询到数据
8.关闭资源
代码: 使用预处理解决SQL注入, getConnection, prepareStatement, executeQuery, close
package com.lg.jdbc03;
import com.lg.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
/**
* @author CH
* @date 2020/10/28 14:53
*/
public class TestLogin02 {
/**
* SQl注入
* 用户输入的用户名和密码, 与编写好的SQL进行了拼接, 用户输入的内容成为了SQL的一部分
* 用户会利用这里的漏洞, 输入一些其他的字符串, 改变了SQL原有的意思
*
* 如何解决
* 要解决SQL注入, 就不能让用户输入的数据和我们的SQL进行直接的拼接
*
* 预处理对象 PrepareStatement 是Statement接口的子接口
* 使用预处理对象, 有预编译的功能, 提高SQL的执行效率
* 使用预处理对象, 通过占位符的方式, 设置参数, 可有效的防止SQL注入
*/
public static void main(String[] args) throws Exception{
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.获取PrepareStatement 预处理对象 编写SQL 使用 ? 占位符方式
String sql = "select * from jdbc_user where username = ? and password = ?";
PreparedStatement ps = connection.prepareStatement(sql);
//3.获取用户输入的用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名: ");
String username = sc.nextLine();
System.out.println("请输入密码: ");
String pw = sc.nextLine();
//4. 设置参数, 使用setXXX(占位符的位置(整数), 要设置的值)方法
ps.setString(1,username);
ps.setString(2,pw);
//5. 执行查询
ResultSet resultSet = ps.executeQuery();
//6. 处理结果集
if(resultSet.next()) {
System.out.println("登陆成功! 欢迎您:" + username);
} else {
System.out.println("登陆失败!");
}
//7.关闭流
JDBCUtils.close(connection,ps,resultSet);
}
}
- 使用 PreparedStatement 预处理对象,可以有效的避免SQL注入
5.7 PreparedStatement的执行原理
代码: 使用 Statement对象 和 PreparedStatement对象进行插入操作
package com.lg.jdbc03;
import com.lg.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author CH
* @date 2020/10/28 15:24
*/
public class TestPS {
public static void main(String[] args) throws SQLException {
Connection connection = JDBCUtils.getConnection();
//获取Statement
Statement statement = connection.createStatement();
//向数据库插入两条数据
statement.executeUpdate("insert into jdbc_user values(null, '张三', '123456', '2000/12/26')");
statement.executeUpdate("insert into jdbc_user values(null, '李四', '654321', '1900/12/26')");
//获取预处理对象
PreparedStatement ps = connection.prepareStatement("insert into jdbc_user values(?,?,?,?)");
//先插入第一条数据
ps.setObject(1, null);
ps.setString(2, "小兵");
ps.setString(3, "qwer");
ps.setString(4, "1999/11/11");
//执行插入
ps.executeUpdate();
//插入第二条数据
ps.setObject(1, null);
ps.setString(2, "长海");
ps.setString(3, "asdf");
ps.setString(4, "2000/11/11");
//执行插入
ps.executeUpdate();
//释放资源
JDBCUtils.close(connection,ps);
}
}
5.8 Statement 与 PreparedStatement的区别
1. Statement用于执行静态SQL语句,在执行时,必须指定一个事先准备好的SQL语句。
2. PrepareStatement是预编译的SQL语句对象,语句中可以包含动态参数“?”,在执行时可以为“?”动态设置参数值。
3. PrepareStatement可以减少编译次数提高数据库性能。
6. JDBC 控制事务
6.1 数据准备
-- 创建账户表
CREATE TABLE account(
-- 主键
id INT PRIMARY KEY AUTO_INCREMENT,
-- 姓名
NAME VARCHAR(10),
-- 转账金额
money DOUBLE
);
-- 添加两个用户
INSERT INTO account (NAME, money) VALUES ('tom', 1000), ('jack', 1000);
6.2 事务相关API
6.3 开发步骤
1. 获取连接
2. 开启事务
3. 获取到 PreparedStatement , 执行两次更新操作
4. 正常情况下提交事务
5. 出现异常回滚事务
6. 最后关闭资源
代码: JDBC 操作事务的方式, setAutoCommit(), commit(), rollback()
package com.lg.jdbc04;
import com.lg.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @author CH
* @date 2020/10/28 15:53
*/
public class TestJDBCTransaction {
//使用JDBC操作事务
public static void main(String[] args) {
Connection connection = null;
PreparedStatement ps = null;
try {
//1. 获取连接
connection = JDBCUtils.getConnection();
//2. 开启事务
connection.setAutoCommit(false);
//3. 获取预处理对象, 执行SQL(两次修改操作)
//3.1 tom账户 -500
ps = connection.prepareStatement(
"update account set money = money - ? where name = ?");
ps.setDouble(1, 500.0);
ps.setString(2, "tom");
ps.executeUpdate();
//模拟tom转账之后出现异常
System.out.println(1 / 0);
//3.2 jack账户 +500
ps = connection.prepareStatement(
"update account set money = money + ? where name = ?");
ps.setDouble(1, 500);
ps.setString(2, "jack");
ps.executeUpdate();
//4. 提交事务(正常情况下)
connection.commit();
System.out.println("转账成功!");
} catch (SQLException throwables) {
throwables.printStackTrace();
//5. 出现异常, 就回滚事务
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
} finally {
//6. 释放资源
JDBCUtils.close(connection, ps);
}
}
}