前言:

本文内容:Statement对象详解、SQL注入问题、PreparedStatement对象

推荐免费MySQL基础讲解视频:【狂神说Java】MySQL最新教程通俗易懂哔哩哔哩bilibili

Statement对象详解

JDBC中的Statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。

CRUD操作

create

使用executeUpdate(String sql)方法完成数据添加操作,示例操作:

1
2
3
4
5
6
Statement st = conn.createStatement();
String sql = "insert into user(...) values(....)";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!");
}

read

使用executeQuery(String sql)方法完成数据库查询操作:

1
2
3
4
5
6
Statement st = conn.createStatement();
String sql = " select * from user where id = 1 ";
int num = st.executeQuery(sql);
while(rs.next()){
//根据获取列表的数据类型,分别调用rs的相应方法映射到Java对象中
}

update

使用executeUpdate(String sql)方法完成数据修改操作:

1
2
3
4
5
6
Statement st = conn.createStatement();
String sql = "update user set name = '' where name = '' ";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("修改成功!");
}

delete

使用executeUpdate(String sql)方法完成数据删除操作:

1
2
3
4
5
6
Statement st = conn.createStatement();
String sql = "delete from user where id = 2";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("删除成功!");
}

代码实例

  1. 提取工具类

    db.properties

    1
    2
    3
    4
    5
    # db.properties
    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/jdbcstudy?useSSL=false&serverTimezone=UTC
    username=root
    password=123456

    JDBCUtils.java

    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
    package com.jokerdig.utils;

    import java.io.InputStream;
    import java.sql.*;
    import java.util.Properties;

    /**
    * @author Joker大雄
    * @data 2022/3/21 - 13:17
    **/
    public class JDBCUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;
    static{
    try {
    InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
    Properties properties = new Properties();
    properties.load(in);
    driver = properties.getProperty("driver");
    url = properties.getProperty("url");
    username = properties.getProperty("username");
    password = properties.getProperty("password");
    // 加载一次驱动
    Class.forName(driver);

    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    // 获取连接
    public static Connection getConnection() throws SQLException {
    return DriverManager.getConnection(url,username,password);
    }
    // 释放资源
    public static void release(Connection connection, Statement statement, ResultSet resultSet){
    try {
    if(resultSet!=null){
    resultSet.close();
    }
    } catch (SQLException throwables) {
    throwables.printStackTrace();
    }
    try {
    if(statement!=null){
    statement.close();
    }
    } catch (SQLException throwables) {
    throwables.printStackTrace();
    }
    try {
    if(connection!=null){
    connection.close();
    }
    } catch (SQLException throwables) {
    throwables.printStackTrace();
    }
    }
    }
  2. 增删改方法executeUpdate

    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
    package com.jokerdig.lesson02;

    import com.jokerdig.utils.JDBCUtils;

    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;

    /**
    * @author Joker大雄
    * @data 2022/3/21 - 13:44
    **/
    public class TestInsert {
    public static void main(String[] args) {
    Connection conn = null;
    Statement st = null;
    ResultSet rs = null;
    try {
    conn = JDBCUtils.getConnection();
    st = conn.createStatement();
    String sql = "insert into users(`id`,`name`,`password`,email,birthday) " +
    "values(4,'夏明','123456','xm@qq.com','1999-01-22')";
    int back = st.executeUpdate(sql);
    if(back==1){
    System.out.println("添加成功");
    }else{
    System.out.println("添加失败");
    }
    } catch (SQLException throwables) {
    throwables.printStackTrace();
    }finally{
    JDBCUtils.release(conn,st,rs);
    }
    }
    }
  3. 查询方法executeQuery

    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
    package com.jokerdig.lesson02;

    import com.jokerdig.utils.JDBCUtils;

    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;

    /**
    * @author Joker大雄
    * @data 2022/3/21 - 13:29
    **/
    public class TestQuery {
    public static void main(String[] args) {
    Connection conn = null;
    Statement st = null;
    ResultSet rs = null;
    try {
    conn = JDBCUtils.getConnection();
    st = conn.createStatement();
    String sql = "select * from users";
    rs = st.executeQuery(sql);
    while(rs.next()){
    System.out.println("id:"+rs.getInt("id")+" name:"+rs.getString("name")+" password:"+rs.getString("password")+"\n");
    }
    } catch (SQLException throwables) {
    throwables.printStackTrace();
    }finally{
    JDBCUtils.release(conn,st,rs);
    }
    }
    }

SQL注入问题

SQL存在漏洞,会被攻击导致数据泄露;

恶意拼接查询

SQL 语句可以查询、插入、更新和删除数据,且使用分号来分隔不同的命令。例如:

1
SELECT * FROM users WHERE user_id = $user_id

其中,user_id 是传入的参数,如果传入的参数值为“1234; DELETE FROM users”,那么最终的查询语句会变为:

1
SELECT * FROM users WHERE user_id = 1234; DELETE FROM users

如果以上语句执行,则会删除 users 表中的所有数据。

利用注释执行非法命令

SQL 语句中可以插入注释。例如:

1
SELECT COUNT(*) AS 'num' FROM game_score WHERE game_id=24411 AND version=$version

如果 version 包含了恶意的字符串'-1' OR 3 AND SLEEP(500)--,那么最终查询语句会变为:

1
SELECT COUNT(*) AS 'num' FROM game_score WHERE game_id=24411 AND version='-1' OR 3 AND SLEEP(500)--

以上恶意查询只是想耗尽系统资源,SLEEP(500) 将导致 SQL 语句一直运行。若在其中添加了修改、删除数据的恶意指令,那么将会造成更大的破坏。

传入非法参数

SQL 语句中传入的字符串参数是用单引号引起来的,如果字符串本身包含单引号而没有被处理,那么可能会篡改原本 SQL 语句的作用。 例如:

1
SELECT * FROM user_name WHERE user_name = $user_name

如果 user_name 传入参数值为 G’zhang,那么最终的查询语句会变为:

1
SELECT * FROM user_name WHERE user_name ='G'zhang'

一般情况下,以上语句会执行出错,这样的语句风险比较小。虽然没有语法错误,但可能会恶意产生 SQL 语句,并且以一种你不期望的方式运行。

添加额外条件

在 SQL 语句中添加一些额外条件,以此来改变执行行为。条件一般为真值表达式。例如:

1
UPDATE users SET userpass='$userpass' WHERE user_id=$user_id;

如果 user_id 被传入恶意的字符串“1234 OR TRUE”,那么最终的 SQL 语句会变为:

1
UPDATE users SET userpass= '123456' WHERE user_id=1234 OR TRUE;

这将更改所有用户的密码。

如何避免SQL注入

使用PreparedStatement对象

PreparedStatement对象

PreparedStatement对象防止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
package com.jokerdig.lesson03;

import com.jokerdig.utils.JDBCUtils;

import java.sql.*;
import java.util.Date;

/**
* @author Joker大雄
* @data 2022/3/21 - 13:44
**/
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
// 用占位符 ? 进行占位,之后在赋值 防止sql注入
String sql = "insert into users(`id`,`name`,`password`,email,birthday) values(?,?,?,?,?)";
ps = conn.prepareStatement(sql); // 预编译sql,先写SQL 不执行
// 手动给参数赋值
ps.setInt(1,5);
ps.setString(2,"小白");
ps.setString(3,"123123");
ps.setString(4,"xb@qq.com");
/*
注意点:
sql.Date SQL java.sql.Date();
util.Date Java new Date().getTime(); 获得时间戳
*/
ps.setDate(5,new java.sql.Date(new Date().getTime()));
// 执行
int back = ps.executeUpdate();
if(back==1){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
JDBCUtils.release(conn,ps,null);
}
}
}

查询

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
package com.jokerdig.lesson03;

import com.jokerdig.utils.JDBCUtils;

import java.sql.*;

/**
* @author Joker大雄
* @data 2022/3/21 - 13:29
**/
public class TestQuery {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select * from users where id = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1,5);
rs = ps.executeQuery();
while(rs.next()){
System.out.println("id:"+rs.getInt("id")+" name:"+rs.getString("name")+" password:"+rs.getString("password")+"\n");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
JDBCUtils.release(conn,ps,rs);
}
}
}

防止SQL注入

PreparedStatement 防止SQL注入的本质,把传递进来的参数当作字符;如果SQL语句中存在',会被直接转义,从而防止SQL注入。