package com.demo.jdbc;
import com.demo.utils.JdbcUtils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* 使用 PreparedStatement 执行带参数的 sql 语句
*/
public class JdbcDemo2 {
/**
* 增加数据
*/
@Test
public void testInsert(){
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 1、获取数据库连接对象
conn = JdbcUtils.getConnection();
// 2、准备预编译的 sql 语句
String sql = "insert into student(name, gender) values(?, ?)"; // ?:表示一个参数的占位符
// 3、执行预编译 sql 语句
pstmt = conn.prepareStatement(sql);
// 4、设置参数值(参数索引从 1 开始)
pstmt.setString(1, "田七");
pstmt.setString(2, "女");
// 5、执行 sql 语句
int count = pstmt.executeUpdate();
System.out.println("影响了" + count + "行");
}catch (Exception e){
e.printStackTrace();
}finally {
JdbcUtils.close(conn, pstmt, null);
}
}
/**
* 修改数据
*/
@Test
public void testUpdate(){
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 1、获取数据库连接对象
conn = JdbcUtils.getConnection();
// 2、准备预编译的 sql 语句
String sql = "update student set name=? where id=?"; // ?:表示一个参数的占位符
// 3、执行预编译 sql 语句
pstmt = conn.prepareStatement(sql);
// 4、设置参数值(参数索引从 1 开始)
pstmt.setString(1, "小花");
pstmt.setInt(2, 6);
// 5、执行 sql 语句
int count = pstmt.executeUpdate();
System.out.println("影响了" + count + "行");
}catch (Exception e){
e.printStackTrace();
}finally {
JdbcUtils.close(conn, pstmt, null);
}
}
/**
* 删除数据
*/
@Test
public void testDelete(){
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 1、获取数据库连接对象
conn = JdbcUtils.getConnection();
// 2、准备预编译的 sql 语句
String sql = "delete from student where id=?"; // ?:表示一个参数的占位符
// 3、执行预编译 sql 语句
pstmt = conn.prepareStatement(sql);
// 4、设置参数值(参数索引从 1 开始)
pstmt.setInt(1, 6);
// 5、执行 sql 语句
int count = pstmt.executeUpdate();
System.out.println("影响了" + count + "行");
}catch (Exception e){
e.printStackTrace();
}finally {
JdbcUtils.close(conn, pstmt, null);
}
}
/**
* 查询数据
*/
@Test
public void testQuery(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 1、获取数据库连接对象
conn = JdbcUtils.getConnection();
// 2、准备预编译的 sql 语句
String sql = "select * from student";
// 3、执行预编译 sql 语句
pstmt = conn.prepareStatement(sql);
// 4、执行 sql 语句
rs = pstmt.executeQuery();
// 5、遍历结果集
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String gender = rs.getString("gender");
System.out.println("id:" + id + ", name:" + name + ", gender:" + gender);
}
}catch (Exception e){
e.printStackTrace();
}finally {
JdbcUtils.close(conn, pstmt, rs);
}
}
}
PreparedStatement 和 Statement 的区别:
1、语法不同:PreparedStatement 可以使用带参数的 sql 语句,Statement 只能使用静态的 sql 语句;
2、效率不同:PreparedStatement 可以使用 sql 缓存区,而 Statement 不能使用,所以 PreparedStatement 效率较高;
注意:支持 sql 缓存区的数据库有:oracle、sql server;
mysql 不支持 sql 缓存区;
3、安全性不同:PreparedStatement 可以有效防止 sql 注入,而 Statement 不能防止 sql 注入;
模拟用户登录,防止 sql 注入:
首先创建一个 测试用数据库,插入一条数据:
然后使用 Statement 和 PreparedStatement 分别执行被注入的 sql 语句:
package com.demo.jdbc;
import com.demo.utils.JdbcUtils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* 模拟用户登录,防止 sql 注入
* */
public class JdbcDemo3 {
// 模拟用户输入的用户名和密码
private String username = "rose";
private String password = "123";
/**
* 使用 Statement 存在 sql 被注入的风险
*/
@Test
public void testStatement(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
// 1、获取数据库连接对象
conn = JdbcUtils.getConnection();
// 2、创建 Statement 对象
stmt = conn.createStatement();
/**
* 3、准备 sql 语句(存在被 注入 的风险)
* 正常登录成功应该是 select * from users where username='rose' and password='123'; 能查询到数据;
* 但是,有一种恒成立的情况:select * from users where 1=1; 永远成立;
* 所以,当 username 的值为:rose' OR 1=1 -- ' 时,不管用户名和密码是多少,条件恒成立,永远可以查到数据;
* 比如当 username 和 password 为如下 值时,依然能查询到数据:
*/
username = "rosesdadsdadsa' OR 1=1 -- '";
password = "sdeweqwewqeqwewq";
String sql = "select * from users where username='" + username + "' and password = '" + password + "'";
System.out.println(sql);
// 4、执行 sql 语句
rs = stmt.executeQuery(sql);
if (rs.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
}catch (Exception e){
e.printStackTrace();
}finally {
JdbcUtils.close(conn, stmt, rs);
}
}
/**
* 使用 PreparedStatement 就可以避免 sql 被注入
*/
@Test
public void testPreparedStatement(){
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try{
// 1、获取数据库连接对象
conn = JdbcUtils.getConnection();
// 2、准备预编译的 sql 语句
String sql = "select * from users where username=? and password =?";
// 3、执行预编译 sql 语句
stmt = conn.prepareStatement(sql);
/**
* 即使把 username 和 password 的值也设置成和上面一样,也查询不到数据
*/
username = "rosesdadsdadsa' OR 1=1 -- '";
password = "sdeweqwewqeqwewq";
// 4、设置参数的值
stmt.setString(1, username);
stmt.setString(2, password);
// 5、执行 sql 语句
rs = stmt.executeQuery();
if (rs.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
}catch (Exception e){
e.printStackTrace();
}finally {
JdbcUtils.close(conn, stmt, rs);
}
}
}
JdbcUtils.java 工具类:
package com.demo.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* jdbc 工具类
*/
public class JdbcUtils {
// 连接数据库的 url:jdbc协议:数据库子协议://主机:端口/数据库
private static String url = "jdbc:mysql://localhost:3306/day17";
private static String username = "root"; // 用户名
private static String password = "root"; // 密码
// 将注册驱动程序放入 静态代码块中,程序一启动的时候执行,只执行一次
static {
try {
// 注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取 数据库连接对象
*/
public static Connection getConnection() {
try {
// 获取数据库连接对象
return DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 释放资源
*
* @param conn Connection 对象
* @param stmt PreparedStatement 对象
* @param rs ResultSet 对象
*/
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null){
try {
rs.close();
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException(e);
}
}
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
}