04-The difference between the database operation object Statement object and PreparedStatement object, the advantages and disadvantages of SQL injection

Statement object and query result set

Statement对象相关的方法

Method to obtain database operation object in Connection interfaceStatement对象

method name Function
Statement createStatement() Create Statement object

Statement对象Execute the SQL statement of addition, deletion, modification and query(不含占位符"?"), the SQL statement in JDBC does not need to be terminated by a semicolon

method name Function
int executeUpdate(insert/delete/update) Execute dml statements (add, delete, modify) and return the number of affected rows
ResultSet executeQuery(select) Execute dql statement (query) and return ResultSet result set object
Connection conn = null;
Statement stmt = null;
//1、注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
//3、获取数据库操作对象
stmt = conn.createStatement();
//4、执行SQL语句	
// String sql = "delete from dept where deptno = 40";
String sql = "update dept set dname = '销售部', loc = '天津' where deptno = 20";
int count = stmt.executeUpdate(sql);
System.out.println(count == 1 ? "修改成功" : "修改失败");

ResultSet查询结果集的方法

When we use the database operation object执行查询语句, it will be generatedResultSet结果集对象(包含查询到的数据). This object保持一个光标 points to the current data row ( Initially the cursor is before the first line)

Insert image description here

ResulrSet结果集Commonly used methods of : When retrieving the value of the field in the current row, you can use 结果集中字段的名称(语义更明确)或字段所在索引(下标从1开始) as the basis to obtain

method name Function
boolean next() Initially, the cursor is located before the first line. Executing the next method will move the cursor to the next line. If there is no next line, it returns false.
boolean previous() Moves up one line, or returns false if there is no previous line
String getString(column index/field name) Regardless of the data type of the field in the result set, the field value is retrieved in the form of String.
Date getDate(column index/field name) Get the value of the field in the form of Date
int getInt(column index/field name) Get the value of the field in the form of int
Xxx getXxx(column index/field name) Get the data in the result set with the specified data type, provided that the data type can be converted normally
Object getObject(column index/field name) Retrieve data from the result set in the form of objects
String sql = "select empno as a,ename,sal from emp";
// 专门执行DQL语句的方法
rs = stmt.executeQuery(sql);

// 处理查询结果集
while(rs.next()){
    
    
	// 以结果集中列的下标获取,JDBC中所有下标从1开始,不是从0开始
	String empno = rs.getString(1);
	String ename = rs.getString(2);
	String sal = rs.getString(3);
	System.out.println(empno + "," + ename + "," + sal);

	// 以结果集中列的名称获取,列名称不是数据库表中的列名称而是查询结果集的列名称
	// 除了以String类型取出之外,还能以特定的类型取出
    int empno = rs.getInt("a");
	String ename = rs.getString("ename");
	double sal = rs.getDouble("sal");
	System.out.println(empno + "," + ename + "," + (sal + 200));
}

SQL injection problem

SQL注入的优缺点

SQL注入问题: When splicing SQL statements不使用占位符, the user-provided 非法信息 is directly spliced ​​into the SQL statement to be executed, which will cause The meaning of the original SQL statement is distorted

  • How to splice用户名zhangsan and its密码123456' or '1'='1(两个用or连接的条件去掉两边的引号) into a SQL statement
  • 字符串拼接变量或表达式的方法: Add double quotation marks first, add two + signs in the middle, and add an expression in the middle of the two plus signs
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
// select * from t_user where loginName = 'zhangsan' and loginPwd = '123456' or '1'='1';
String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";

// 完成了sql语句的拼接后发给DBMS,然后DBMS对拼接好的sql语句进行编译
rs = stmt.executeQuery(sql);

SQL注入的用途:Any business aspect that requires SQL statement splicing must use Statement objects, such as用户在控制台输入desc/asc决定降序/升序

// 用户在控制台输入desc就是降序,输入asc就是升序
Scanner s = new Scanner(System.in);
System.out.println("输入desc或asc,desc表示降序,asc表示升序");
System.out.print("请输入:");
String keyWords = s.nextLine();

Connection conn = null;
Statement stmt = null;
ResultSet rs = null;

// 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","333");

String sql = "select ename from emp order by ename ? " ;
// 给占位符传值后的结果select ename from emp order by ename 'desc或asc'(sql语法错误)
ps = conn.prepareStatement(sql);
ps.setString(1, keyWords);

// 使用Statement对象进行sql语句拼接时不会出现问题
stmt = conn.createStatement();
String sql = "select ename from emp order by ename " + keyWords;
rs = stmt.executeQuery(sql);
// 遍历结果集
while(rs.next()){
    
    
    System.out.println(rs.getString("ename"));
}

PreparedStatement objects and query result sets

PreparedStatement相关方法

Connection接口Method to obtain the database operation object in PreparedStatement对象, and precompile the SQL statement while creating the object

method name Function
PreparedStatement prepareStatement(sql) Create preprocessing objects

PreparedStatement接口 inherits java.sql.Statement and is a precompiled database operation object (SQL statement contains placeholder), which can solve SQL injection problems

  • Compile包含占位符的SQL语句 in advance, and then pass the value to the placeholder, even if the information provided by the user containssql语句关键字也无法参与编译过程,最终被当作普通的字符处理

Represent the parameters of the precompiled SQL statement as placeholders?(不能使用单引号括起来), one? represents a placeholder, and finally call ps对象的setXxx()方法Pass value to placeholder (下标从1开始)

  • 一个PreparedStatement对象每次只能预编译一条SQL语句And type safety checks will be done during the compilation phase.

When the database executes the SQL statement for the first time, it will be compiled first. If there is no change in the SQL statement during the second execution, it will be executed directly without compilation.

  • Statement对象: Compile and execute once and each execution is a complete SQL statement.
  • PreparedStatement对象(效率较高): Compile the SQL statement containing placeholders in advance, and then call the setXxx() method of the ps object to pass the value to the placeholder. The same SQL template can be executed N times after compilation.

PreparedStatement接口Method in: No more SQL statements can be written in the parameters of the method, otherwise the SQL statement will be recompiled.

method name Function
int executeUpdate() Execute dml statements (add, delete, modify) and return the number of affected rows
ResultSet executeQuery() Execute dql statement (query) and return ResultSet result set object
execute() Execute arbitrary sql and return a Boolean value
void setXxx(placeholder index, placeholder value) Set the value of the corresponding type to the placeholder. The placeholder subscript starts from 1
void setString(placeholder index, placeholder value) The value set for the placeholder is treated as a string in the SQL statement (automatically added quotes)
void setInt(placeholder index, placeholder value) The value set for the placeholder is treated as int type data in the SQL statement (without quotation marks)

ExplanationDQLPhrase

// 一个?表示一个占位符,一个?将来可以接收一个值
String sql = "select * from t_user where loginName = ? and loginPwd = ?";
// 程序执行到此处会将占位符的sql语句发送给DBMS,然后DBMS对该sql语句进行预编译
ps = conn.prepareStatement(sql);
// 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始)
ps.setString(1, loginName);
ps.setString(2, loginPwd);
// 执行sql,ps已经预编译过了sql语句,如果再传就会重新编译sql语句
rs = ps.executeQuery();
// 处理结果集
if(rs.next()){
    
    
    // 登录成功
    loginSuccess = true;
}

ExplanationDMLPhrase

// 执行插入语句
String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setInt(1, 60);
ps.setString(2, "销售部");
ps.setString(3, "上海");

// 执行更新语句
String sql = "update dept set dname = ?, loc = ? where deptno = ?";
ps2 = conn.prepareStatement(sql);
ps2.setString(1, "研发一部");
ps2.setString(2, "北京");
ps2.setInt(3, 60);

// 执行删除语句
String sql = "delete from dept where deptno = ?";
ps3 = conn.prepareStatement(sql);
ps3.setInt(1, 60);
System.out.println(count);

模糊查询

Find employees whose second letter contains A

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
// 使用工具类获取连接
conn = DBUtil.getConnection();

// 以下是错误的写法,?一定不能用单引号括起来
String sql = "select ename from emp where ename like '_?%'";
ps = conn.prepareStatement(sql);
ps.setString(1, "A");

// 正确写法
String sql = "select ename from emp where ename like ?";
ps = conn.prepareStatement(sql);
ps.setString(1, "_A%");
rs = ps.executeQuery();
while(rs.next()){
    
    
    System.out.println(rs.getString("ename"));
}

模拟用户登录功能(防止注入)

Requirements: Query user information from the database and implement user login function

public class JDBCTest {
    
    
    public static void main(String[] args) {
    
    
        // 初始化一个界面
        Map<String,String> userLoginInfo = initUI();
        // 验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        // 最后输出结果
        System.out.println(loginSuccess ? "登录成功" : "登录失败");
    }
}

Step one: 初始化用户界面 allows the user to enter login information such as username and password

private static Map<String, String> initUI() {
    
    
    Scanner s = new Scanner(System.in);
    System.out.print("用户名:");
    String loginName = s.nextLine();
    System.out.print("密码:");
    String loginPwd = s.nextLine();
    Map<String,String> userLoginInfo = new HashMap<>();
    userLoginInfo.put("loginName", loginName);
    userLoginInfo.put("loginPwd", loginPwd);
    return userLoginInfo;
}

Step 2: Business logic of 实现用户登陆

private static boolean login(Map<String, String> userLoginInfo) {
    
    
    // false表示失败,true表示成功
    boolean loginSuccess = false;
    // 单独定义变量
    String loginName = userLoginInfo.get("loginName");
    String loginPwd = userLoginInfo.get("loginPwd");
    // 获取预编译的数据库操作对象PreparedStatement
    Connection conn = null;
    PreparedStatement ps = null; 
    ResultSet rs = null;
    try {
    
    
        // 1、注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2、获取连接
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
        // 3、获取预编译的数据库操作对象同时对SQL语句进行预编译
        String sql = "select * from t_user where loginName = ? and loginPwd = ?";
        ps = conn.prepareStatement(sql);
        // 给占位符?传值,下标从一开始
        ps.setString(1, loginName);
        ps.setString(2, loginPwd);
        // 4、执行sql
        rs = ps.executeQuery();
        // 5、处理结果集
        if(rs.next()){
    
    
            // 登录成功
            loginSuccess = true;
        }
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        // 6、释放资源
        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();
            }
        }
    }
    return loginSuccess;
}

Guess you like

Origin blog.csdn.net/qq_57005976/article/details/134772746