JDBC的介绍

news2024/9/22 12:52:02

一 JDBC的简介

1.1 ODBC的出现

        早期的数据库应用程序开发,因为没有通用的针对与数据库的编程接口,所以,开发人员需要学习相关数据库的API,才可以进行应用程序,这样增加了学习成本和开发周期。因此整个开发市场一直在呼吁有一套通用的编程接口

        因为有市场需要,微软定义了一组用于数据库应用程序的编程接口ODBC(open database connectivity)。这一套方案大大缩短了程序的开发周期,可以让开发人员只需要调用同一套编程接口,无需考虑具体实现。

ODBC分为四个部分:

  1. 应用程序:开发人员所写的代码,ODBC提供的调用接口

  2. 驱动程序管理器:用于管理驱动程序的。

  3. 驱动程序:对接口的实现部分,各个数据库厂商来完成的。

  4. 数据源:就是连接数据库的一些参数:url,username,password

1.2 JDBC简介

        Sun公司参考了ODBC方案,制定了一组专门为java语言连接数据库的通用接口JDBC。方便了java开发人员,开发人员不需要考虑特定的数据库的DBMS。JDBC不直接依赖于DBMS,而是通过驱动程序将sql语句转发给DBMS,由DBMS进行解析并执行,处理结果返回。

简单点说,它为Java开发者提供了一种标准的方法来连接和操作各种关系型数据库。

注意:驱动程序:由数据库厂商自己实现,程序员只需要拿来使用即可。

1.3 JDBC的工作原理

 

第一步: 注册驱动程序 
第二步:   请求连接 
第三步:   获取执行sql语句的对象,发送给DBMS 
第四步:返回结果集,程序员进行处理 
第五步:   关闭连接操作

1.4 JDBC中常用API

        JDBC API包含了一组类和接口,这些类和接口使得Java程序能够连接到数据库,执行SQL语句,并处理结果。  

java.sql.DriverManager
java.sql.Connection
java.sql.Statement
java.sql.Result
1)DriverManager

        JDBC的驱动管理类,负责加载和注册驱动,会根据所提供的连接信息(如URL、用户名和密码)自动选择合适的驱动程序。其提供了用于连接数据库的方法getConnection(…)

常用方法:

getConnection(String url, String user, String password)

方法参数解析:

- url:  连接指定数据库的地址            (比如,jdbc:mysql://ip:port/dbname) 
- user:  连接用户名 
- password:  密码 

2)Connection。

        是一个接口,代表了与数据库的一个会话;通过DriverManager的getConnection方法,程序可以建立与数据库的连接,返回该接口的一个实现类对象。可以用来获取Statement、PreparedStatement和CallableStatement等对象。

常用方法:

Statement statement =conn.createStatement();
作用:用于获取Statement对象,通过连接会话对象,来获取可以发送sql语句对象
3)Statement相关

Statement:也是一个接口,用于执行静态SQL语句。每次执行都会解析、编译和执行SQL语句,效率较低,但灵活性高。

常用方法:

execute(String sql):通常用于DDL 
executeUpdate(String sql):通常用于DML 
executeQuery(String sql):用于DQL

PreparedStatement:用于执行预编译的SQL语句。预编译的SQL语句只需要解析、编译一次,之后可以多次执行,提高了执行效率。适用于需要多次执行相同或类似SQL语句的场景。

常用方法:

execute() ;------用于DDL和DML 
executeUpdate();-----用于DML 
executeQuery();-----用于DQL

CallableStatement:用于执行存储过程和函数。它可以接收参数、返回结果集和处理输出参数。 

4)ResultSet

是一个接口,表示从数据库执行DQL语句时返回的结果集。其内部维护了一个指针,该指针默认指向的是第一行之前的位置。next方法用于移动指针到下一行。 指针指向某一行时,就可以调用相关的方法获取这一行上的所有列数据。

常用方法:

next():光标方法,向下移动一行,

getDate(int columnIndex)
getDate(String columnLabel)

getString(int columnIndex) 
getString(String columnLabel)

getInt(int columnIndex) 
getInt(String columnLabel)

getDouble(int columnIndex) 
getDouble(String columnLabel)

二 原生JDBC入门编程

2.1 编写步骤

在编写JDBC的原生代码时,先创建好项目,加载好相应的静态资源,如图片、第三方jar包等,

==注意==:jar包要add到Library里

然后编写步骤如下:

  1. 注册驱动

  2. 建立连接

  3. 获取执行对象

  4. 处理结果集

  5. 关闭连接

2.2 案例1:JDBC更新数据

/*
    JDBC的第一个程序编写: 修改mydb库里的emp表中7369编号的员工的部门编号为30
    准备工作 准备好项目,然后加载第一方jar包,即MYSQL的驱动程序jar文件,注意,add as Library

 */


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class JDBCDemo01 {

    public static void main(String[] args) throws Exception {
        // 第一步:加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 第二步:获取连接,(DriverManager 自动识别驱动程序,并向数据库发送连接请求,如果成功,则返回链接会话)
        // 参数url: 连接数据库的路径地址 jdbc:mysql://ip:port/库名?serverTimezone=Asia/Shanghai&useTimezone= true
        // 参数user:连接数据库的用户名: root
        // 参数password:连接数据库的密码: 123456
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai&useTimezone= true", "root", "123456");
        System.out.println(conn);
        //第三步:通过连接会话对象,来获取可以发送sql语句对象
        Statement stat = conn.createStatement();
        //第四步:使用statement的方法来发送sql语句
        /**
         * execute(String sql): 发送DDL的方法
         * executeUpdate(String sql): 发送DML的方法
         * executeQuery(String sql): 发送DQL的方法
         * Statement该对象发送的sql语句,每次都会进行解析,编译,执行。效率低。
         *      解析SQL: 检验SQL的语法格式是否正确
         *      编译SQL: 验证书写的表名,字段等是否存在,如果存在,编译通过。
         *
         */
        int num=stat.executeUpdate("update emp set deptno=30 where empno=7369 ");
        // 第五步:处理结果集
        System.out.println("受影响的条数:"+num);
        // 第六步 关闭连接会话
        conn.close();

    }


}

2.3 案例2:JDBC查询数据

/*
    使用JDBC来完成第二个程序: 查询emp表中的20号部门的所有员工信息
 */

import java.sql.*;

public class JDBCDemo02 {
    public static void main(String[] args) throws Exception {
        //加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai&useTimezone= true", "root", "123456");
        //
        Statement state = conn.createStatement();
        //注意 DBMS执行后的结果被发送到客户端,被封装到resultSet的具体子类型的对象中
        ResultSet resultSet = state.executeQuery("select EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO from emp where deptno = 20 order by empno desc");
        //第四步:处理查询结果集,里面存储了N行记录,指针默认在第一行之前,因此想要获取每一行的数据,需要移动指针
        while (resultSet.next()) {// 向下移动指针,如果有数据,返回true
            int empno = resultSet.getInt(1);
            String ename = resultSet.getString(2);
            String job = resultSet.getString(3);
            int mgr = resultSet.getInt(4);
            Date hiredate = resultSet.getDate(5);
            double sal = resultSet.getDouble("SAL");
            double comm = resultSet.getDouble("COMM");
            int deptno = resultSet.getInt("DEPTNO");
            System.out.println(empno+","+ename+","+job+","+mgr+","+hiredate+","+sal+","+comm+","+deptno);
        }

        // 第五步: 关闭连接
        conn.close();

    }
}

2.4 练习:JDBC插入和删除数据

插入练习:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class JDBCDemo03 {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.cj.jdbc.Driver");

        Connection connect = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai&useTimezone= true", "root", "123456");
        Statement statement = connect.createStatement();
        String sql="insert into emp values (10000,'superman','hero',7369,'2024-08-30',2000,500,40)";
        int i = statement.executeUpdate(sql);
        System.out.println(i);
        connect.close();
    }
}

 删除练习

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

public class JDBCDemo04 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection root = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai&useTimezone= true", "root", "123456");
        Statement ss = root.createStatement();
        int i = ss.executeUpdate("Delete from emp where empno=10000");
        System.out.println(i);
        root.close();
    }
}

2.5 DBUtil工具类的封装

第一步:编写配置文件properties

#低版本的mysql的驱动和url
#driver=com.mysql.jdbc.Driver
#url=jdbc:mysql://localhost:3306/bd1906?useUnicode=true&characterEncoding=utf8

#高版本的mysql的驱动和url
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai&useTimezone=true
username=root
passwd=123456

 第二步:定义工具类DBUtil,读取配置文件,定义连接方法,关闭方法等

/*
    自定义一个链接、关闭数据库的工具类型。
    提供一个链接方法:getConnection()
    提供一个关闭方法: closeConnection(Connection conn){}
 */


import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class DBUtil {
    //定义四个静态变量
    private  static String driver;
    private  static String url;
    private  static String user;
    private  static String password;
    static{
        try {
            //使用IO流读取jdbc.properties这个配置文件 相对位置"src/"
            InputStream inputStream = DBUtil.class
                        .getClassLoader().
                        getResourceAsStream("jdbc.properties");
            // 创建配置文件集合对象 Properties
            Properties prop = new Properties();
            //调用Properties对象的load方法,读取流里的信息
            prop.load(inputStream);
            //从对象身上获取键值对,注意,传入的key是文件中等号前的名
            driver = prop.getProperty("driver");
            url = prop.getProperty("url");
            user = prop.getProperty("username");
            password = prop.getProperty("password");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

//    public static void main(String[] args) {
//        Connection conn = DBUtil.getConnection();
//        System.out.println(conn);
//
//        DBUtil.closeConnection(conn);
//
//    }

    /**
     * 请求连接数据库,返回连接会话
     * @return
     */
    public static Connection getConnection(){
        Connection conn = null;
        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return conn;
    }

    /**
     * 关闭连接
     * @param conn
     */
    public static void closeConnection(Connection conn){
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

 第三步:调用DBUtil工具类,进行测试

 使用DBUtil重构  修改上面的Update方法里的代码。

/*
    使用DBUtil  重构  修改_01Simple.JDBCDemo01里的代码。

 */

public class JDBCDemo01 {

    public static void main(String[] args) throws Exception {
        // 调用DBUtil里的getConnection方法,获取连接对象
        Connection conn = DBUtil.getConnection();
        Statement stat = conn.createStatement();
        int num=stat.executeUpdate("update emp set deptno=20 where empno=7369 ");
        // 第五步:处理结果集
        System.out.println("受影响的条数:"+num);
        // 第六步 关闭连接会话
        DBUtil.closeConnection(conn);

    }
}

 使用DBUtil重构上述查询的代码

/*
    使用DBUtil 重构 _01Simple.JDBCDemo02
 */

import com.jdbc.day01.util.DBUtil;

import java.sql.*;

public class JDBCDemo02 {
    public static void main(String[] args) throws Exception {

//        重构
        Connection conn = DBUtil.getConnection();

        Statement state = conn.createStatement();
        //注意 DBMS执行后的结果被发送到客户端,被封装到resultSet的具体子类型的对象中
        ResultSet resultSet = state.executeQuery("select EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO from emp where deptno = 20 order by empno desc");
        //第四步:处理查询结果集,里面存储了N行记录,指针默认在第一行之前,因此想要获取每一行的数据,需要移动指针
        while (resultSet.next()) {// 向下移动指针,如果有数据,返回true
            int empno = resultSet.getInt(1);
            String ename = resultSet.getString(2);
            String job = resultSet.getString(3);
            int mgr = resultSet.getInt(4);
            Date hiredate = resultSet.getDate(5);
            double sal = resultSet.getDouble("SAL");
            double comm = resultSet.getDouble("COMM");
            int deptno = resultSet.getInt("DEPTNO");
            System.out.println(empno+","+ename+","+job+","+mgr+","+hiredate+","+sal+","+comm+","+deptno);
        }

        // 重构
        DBUtil.closeConnection(conn);

    }
}

2.6 jdbc的批处理

        每一次的sql操作都会占用数据库的资源。如果将N条操作先存储到缓存区中,然后再一次性刷到数据库中,这就减少了与数据库的交互次数。因此可以提高效率。

Statement 提供了以下两个常用的方法,用做批处理

addBatch(String sql):将sql语句添加到缓存中
executeBatch():将缓存中的sql一次性刷到数据库中 

案例演示

/*
    为什么使用JDBC的批处理:
        因为Statement发生的SQL语句,DBMS每次都会解析,编译,因此性能比较低。
    批处理的方法:可以提前将一部分SQL存储在缓存区中,然后一次性的将缓存区哄的SQL刷到DBMS里。
                这样可以大大减少了客户端与数据库的交互次数,从而边相等提高效率。
 */


import com.jdbc.day01.util.DBUtil;

import java.sql.Connection;
import java.sql.Statement;

public class BatchTest {
    public static void main(String[] args) {
        Connection conn = null;
        try {
            conn = DBUtil.getConnection();
            //向数据库中插入1030条记录
            Statement stat = conn.createStatement();
            for (int i = 0; i <=1030; i++) {
                String[] genders = {"F","M"};
                String gender = genders[(int)(Math.random()*2)];
                String sql = "insert into testbatch values(null,'batch74200"+i+"','"+gender+"')";
               // 将sql添加到缓存区
                stat.addBatch(sql);
                // 缓存区里存储了50个就冲刷一次
                if(i%50==0){
                    // 将缓存里的sql冲刷到DBMS中
                    stat.executeBatch();
                }
            }
            stat.executeBatch();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtil.closeConnection(conn);
        }
    }
}

三 SQL注入问题

3.1 登陆案例

3.1.1 需求分析以及数据准备

1.需求:实现输入用户名和密码后,实现跳转到主页面的功能
2.逻辑分析:
        - 客户端:接收用户名和密码,并将这些信息发送到服务端
        - 服务端:接收到客户端传过来的用户名和密码后,进行数据库校验是否存在这样的数据,如果存在,就将用户名对应的这一条记录返回,并封装成一个User对象。返回给客户端。
        - 客户端收到返回信息后,判断Account对象是否存在,如果存在,就实现跳转.....

 账户类代码:

package com.jdbc.day01._04SQLInject;

/*
    定义一个java类型Account与表记录进行映射。 一个Account对象表示表中的一条记录信息

 */

import java.sql.Date;
import java.util.Objects;

public class Account {
    private int id;
    private String accountId;
    private double balance;
    private String usrname;
    private String password;
    private String idcard;
    private Date opertime;
    private char gender;

    public Account()
    {
    }

    public Account(int id, String accountId, double balance, String usrname, String password, String idcard, Date opertime, char gender) {
        this.id = id;
        this.accountId = accountId;
        this.balance = balance;
        this.usrname = usrname;
        this.password = password;
        this.idcard = idcard;
        this.opertime = opertime;
        this.gender = gender;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getAccountId() {
        return accountId;
    }

    public void setAccountId(String accountId) {
        this.accountId = accountId;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public String getUsrname() {
        return usrname;
    }

    public void setUsrname(String usrname) {
        this.usrname = usrname;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getIdcard() {
        return idcard;
    }

    public void setIdcard(String idcard) {
        this.idcard = idcard;
    }

    public Date getOpertime() {
        return opertime;
    }

    public void setOpertime(Date opertime) {
        this.opertime = opertime;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    @Override
    public boolean equals(Object object) {
        if (this == object) return true;
        if (object == null || getClass() != object.getClass()) return false;
        Account account = (Account) object;
        return id == account.id && Double.compare(balance, account.balance) == 0 && gender == account.gender && Objects.equals(accountId, account.accountId) && Objects.equals(usrname, account.usrname) && Objects.equals(password, account.password) && Objects.equals(idcard, account.idcard) && Objects.equals(opertime, account.opertime);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, accountId, balance, usrname, password, idcard, opertime, gender);
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", accountId='" + accountId + '\'' +
                ", balance=" + balance +
                ", usrname='" + usrname + '\'' +
                ", password='" + password + '\'' +
                ", idcard='" + idcard + '\'' +
                ", opertime=" + opertime +
                ", gender=" + gender +
                '}';
    }
}

服务端代码:提供了一个check方法,来验证账号密码是否能登录 

import com.jdbc.day01.util.DBUtil;

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

public class AppServer {

    public Account checkLogin(String username,String password){

        Account account = null;
        Connection conn = null;
        try{
            //连接数据库
            conn=DBUtil.getConnection();
            Statement state = conn.createStatement();
            // 用户名和密码同时作为where里的条件,进行查询,如果能查询到,则说明用户名和密码正确
            String sql = "select * from bank_account where user_name = '"+username+"' and user_pwd = '"+password+"'";
            //发送到数据库中执行
            ResultSet rs = state.executeQuery(sql);
            if(rs.next()){
               int id = rs.getInt("id");
               String accountId = rs.getString("account_id");
               double balance = rs.getDouble("account_balance");
               String usrname = rs.getString("user_name");
               String password1 = rs.getString("user_pwd");
               String idcard = rs.getString("user_idcard");
               Date opertime = rs.getDate("oper_time");
               char gender = rs.getString("gender").charAt(0);
               account = new Account(id,accountId,balance,usrname,password1,idcard,opertime,gender);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtil.closeConnection(conn);
        }
        return  account;

    }
}

 客户端:

/*
    使用Scanner来模拟登录案例的客户端界面
 */

import java.util.Scanner;

public class AppClient {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();
        // 来一个服务端对象
        AppServer server = new AppServer();
        // 调用服务端的checkLogin方法检查
        Account account = server.checkLogin(username,password);
        if(account==null)
            System.out.println("用户名或密码错误!");
        else
            System.out.println("登录成功!正在调转");
    }
}

登录成功演示: 

 

登录失败演示:

 

问题演示:可以使用or连接,来破坏sql语句的检验,很久之前的WiFi万能钥匙就是这个原理。

 

所以就出现了问题:sql注入问题,存在安全隐患。 

3.2 SQL注入问题(安全隐患)

        Statement对象发送的语句可以被改变结构,即如果之前在where中设置的是两个条件,那么可以通过一些参数 比如 添加or 后面再跟其他条件。此时,where子句中是三个条件。

这种情况就叫做SQL注入。有安全隐患问题。

3.3 PreparedStatement类

3.3.1 预编译类的简介

- PreparedStatement是Statement的子类型
- 此类型可以确定SQL语句的结构无法通过其它方式来增减条件
- 此类型还通过占位符 "?"来提前占位,并确定语句的结构
- 提供了相应的赋值方式:
    ps.setInt(int index,int value) 
    ps.setString(int index,String value) 
    ps.setDouble(int index,double value) 
    ps.setDate(int index,Date value)

    index:表示sql语句中占位符?的索引。从1开始 
    value:占位符所对应的要赋予的值
- 执行方法: 
    ps.execute() ;------用于DDL和DML 
    ps.executeUpdate();-----用于DML 
    ps.executeQuery();-----用于DQL

 

3.3.2 修改Server里的代码(优化)

/*
    提供一个用于检测用户名和密码是否正确的方法。
    如果正确,返回一个Account对象
    如果不正确,返回一个null
 */
/*
    SQL注入:黑客通过传值的方式,改安SQL的条件结构,比如由两个条件,发成了三个条件
    and password='"+password+"'....where username ='"+username+"'
    password的值,传入了 111111'or1'='1  因此就会变成

    "....where username ='"+username+""and password='111111'or '1'='1'"
    可以看出,多一个or连接 1=1恒成立的条件。
    也就是说Statement可以通过SQL注入的方法将条件的个数改变,换句话说,就是改变了SQL语句的整体结构。
    因此Sun公司有设计了一个Statement的子接口PreparedStatement。
    Prepatedstatement这个接口会先将SQL结构提前发送到DBMS中,并且DBMS会将该结构锁死。黑客再次通过SQL注入
    的方法传入了带有or连接的恒成立的条件,DBMS也只会将其当成一个参数,而不是条件。


    解决办法:使用PreparedStatement
 */

import com.jdbc.day01.util.DBUtil;

import java.sql.*;

public class AppServer2 {

    public Account checkLogin(String username,String password){

        Account account = null;
        Connection conn = null;
        try{
            //连接数据库
            conn=DBUtil.getConnection();
            /*
                调用prepareStatement(String sql) 先确定SQL的结果,发送到DBMS中
                使用?来表示占位,用于传参
             */
            // 用户名和密码同时作为where里的条件,进行查询,如果能查询到,则说明用户名和密码正确
            String sql = "select * from bank_account where user_name = ? and user_pwd = ?";
            PreparedStatement state = conn.prepareStatement(sql);

            //提前发送完毕后,要继续给?赋值
            state.setString(1,username);
            state.setString(2,password);

            //再次将参数发送到数据库中执行
            ResultSet rs = state.executeQuery();
            if(rs.next()){
               int id = rs.getInt("id");
               String accountId = rs.getString("account_id");
               double balance = rs.getDouble("account_balance");
               String usrname = rs.getString("user_name");
               String password1 = rs.getString("user_pwd");
               String idcard = rs.getString("user_idcard");
               Date opertime = rs.getDate("oper_time");
               char gender = rs.getString("gender").charAt(0);
               account = new Account(id,accountId,balance,usrname,password1,idcard,opertime,gender);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtil.closeConnection(conn);
        }
        return  account;

    }
}

3.3.3 客户端的代码

package com.jdbc.day01._04SQLInject;

/*
    使用Scanner来模拟登录案例的客户端界面
 */

import java.util.Scanner;

public class AppClient {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();
        // 来一个服务端对象
        AppServer2 server = new AppServer2();
        // 调用服务端的checkLogin方法检查
        Account account = server.checkLogin(username,password);
        if(account==null)
            System.out.println("用户名或密码错误!");
        else
            System.out.println("登录成功!正在调转");
    }
}

 结果展示:

错误:

利用漏洞:

所以sql注入问题得到了解决。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2105909.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QT creator堆栈窗体demo

创建项目选择QDialog,不要选QMainWindow #ifndef DIALOGSTACK_H #define DIALOGSTACK_H#include <QDialog> #include <QListWidget>//列表框 #include <QStackedWidget>//堆栈窗体 #include <QLabel>QT_BEGIN_NAMESPACE namespace Ui { class DialogSt…

集成电路学习:什么是NOR Flash Memory非易失性闪存存储器

一、NOR Flash Memory&#xff1a;非易失性闪存存储器 NOR Flash Memory&#xff0c;即非易失性闪存存储器的一种&#xff0c;是Flash存储器的一个重要分支。Flash存储器&#xff0c;又称为闪存&#xff0c;结合了ROM&#xff08;只读存储器&#xff09;和RAM&#xff08;随机存…

力扣面试150 分隔链表 模拟

Problem: 86. 分隔链表 &#x1f468;‍&#x1f3eb; 参考题解 Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNo…

SQL - SQL优化

在sql查询中为了提高查询效率&#xff0c;我们常常会采取一些措施对查询语句进行sql优化&#xff0c;下面总结的一些方法&#xff0c;有需要的可以参考参考 一、查询SQL尽量不要使用select *&#xff0c;而是具体字段 // 建议 SELECT id,user_name,age,tel FROM user// 不建议…

UEFI BIOS之SMBIOS

SMBIO spec: SMBIOS Specification (dmtf.org) https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.5.0.pdf SMBIOS SMBIOS&#xff08;System Management BIOS&#xff09;是一种标准化的系统信息数据结构&#xff0c;旨在提供有关计算机硬件配置、…

Java SPI机制源码

文章目录 SPI简介使用案例SPI的应用SPI机制源码SPI与类加载器双亲委派机制 SPI简介 Java的SPI&#xff08;Service Provider Interface&#xff09;机制允许第三方为应用程序提供插件式的扩展&#xff0c;而不需要修改应用程序本身的代码&#xff0c;从而实现了解耦。Java标准库…

HUAWEI华为MateBook B5-420 i5 集显(KLCZ-WXX9,KLCZ-WDH9)原装出厂Windows10系统文件下载

适用型号&#xff1a;KLCZ-WXX9、KLCZ-WDH9 链接&#xff1a;https://pan.baidu.com/s/12xnaLtcPjZoyfCcJUHynVQ?pwdelul 提取码&#xff1a;elul 华为原装系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、华为浏览器、Office办公软件、华为…

[数据集][目标检测]石油泄漏检测数据集VOC+YOLO格式6633张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;6633 标注数量(xml文件个数)&#xff1a;6633 标注数量(txt文件个数)&#xff1a;6633 标注…

网络安全硬件

传统防火墙 传统防火墙 技术&#xff1a;访问控制、代理技术、会话机制 工作层次&#xff1a;应用层一下 防御模式&#xff1a;通过防御设备划分边界&#xff0c;基于IP/端口和特征进行判断&#xff1b;以隔离为基础&#xff0c;基于信任原则构建安全框架&#xff1b;以防护为…

【Rust】007-包管理与模块管理

【Rust】007-包管理与模块管理 文章目录 【Rust】007-包管理与模块管理一、包管理器&#xff1a;Cargo1、简介Cargo 官方文档仓库 2、项目初始化3、写一个小程序任务目标寻找合适的库添加库到我们的项目中代码实现cargo run运行 二、模块管理1、概述2、文件作为模块第一步&…

可靠性定义

一、广义可靠性 包含以下三个方面 1、可靠性&#xff08;Reliability&#xff09; 定义&#xff1a;产品、系统或组件在预定条件下和规定时间内&#xff0c;完成既定功能的能力。 平均故障间隔时间&#xff08;Mean Time Between Failures, MTBF&#xff09;&#xff1a; …

每天五分钟深度学习:广播机制(以python语言为例)

本文重点 因为向量化的计算方式导致效率的提升,所以现在很多时候,我们都是用向量化的计算,但是向量化计算有一个问题让人头痛就是维度的问题,本节课程我们将讲解python中的广播机制,你会发现这个机制的优秀之处。 代码实例 import numpy as np a=np.random.randn(3,4) …

使用 Milvus 和 Streamlit 搭建多模态产品推荐系统

我们可以使用 Milvus 搭建多模态 RAG 应用&#xff0c;用于产品推荐系统。用户只需简单上传一张图片并输入文字描述&#xff0c;Google 的 MagicLens 多模态 Embedding 模型就会将图像和文本编码成一个多模态向量。然后&#xff0c;使用这个向量从 Milvus 向量数据库中找到最相…

负载均衡 Ribbon 与 Fegin 远程调用原理

文章目录 一、什么是负载均衡二、Ribbon 负载均衡2.1 Ribbon 使用2.2 Ribbon 实现原理 (★)2.3 Ribbon 负载均衡算法 三、Feign 远程调用3.1 Feign 简述3.2 Feign 的集成3.3 Feign 实现原理 (★) 一、什么是负载均衡 《服务治理&#xff1a;Nacos 注册中心》 末尾提到了负载均…

简单的Linux Ftp服务搭建

简单的Linux FTP服务搭建 1.需求 公司有一个esb文件传输代理&#xff0c;其中我们程序有文件传输功能&#xff0c;需要将本地文件传输到esb文件代理服务器上&#xff0c;传输成功之后发送http请求&#xff0c;告知esb将固定文件进行传输到对应外围其他服务的文件目录中&#…

【高阶数据结构】秘法(二)——图(一):图的基本概念和存储结构

前言&#xff1a; 今天我们要讲解的是数据结构中图的部分&#xff0c;这部分在我们实际生活中也是经常会碰到的&#xff0c;同时这部分也是数据结构中比较有难度的部分&#xff0c;这部分内容我会把它分为多章来进行讲解&#xff0c;今天我们先来讲解一下图的基本概念和存储结构…

Codeforces Round 920 (Div. 3)(A,B,C,D)

A 在二维坐标轴上有一个正方形&#xff0c;给你一个正方形的四个顶点坐标&#xff0c;求面积 知道一个边长&#xff0c;平方即可 for(int i0;i<4;i)x[i]x1; Arrays.sort(x); //1122 kMath.abs(x[2]-x[1]); System.out.println(k*k); B 操作1、2是添加和修改&#xff0c;操…

Windows系统下的Spark环境配置

一&#xff1a;Spark的介绍 Apache Spark 是一个开源的分布式大数据处理引擎&#xff0c;它提供了一整套开发API&#xff0c;包括流计算和机器学习。Spark 支持批处理和流处理&#xff0c;其显著特点是能够在内存中进行迭代计算&#xff0c;从而加快数据处理速度。尽管 Spark …

【专题】2024年8月中国企业跨境、出海、国际化、全球化行业报告汇总PDF合集分享(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p37584 在全球化浪潮汹涌澎湃的当下&#xff0c;中国企业积极探索海外市场&#xff0c;开启了出海跨境的新征程。本报告合集旨在全面梳理出海跨境全球化行业的发展态势&#xff0c;涵盖多个领域的深度洞察。 从游戏、快消品、医疗器…

Python行结构(逻辑行、物理行、显式拼接行、隐式拼接行、空白行)

Python行结构 &#xff08;逻辑行、物理行、显式拼接行、隐式拼接行、空白行&#xff09; 本文目录&#xff1a; 零、时光宝盒 一、Python PEP8 编码行规范 1.1、Maximum Line Length 行的最大长度 1.2、在二元运算符之前应该换行吗&#xff1f; 二、Python行结构 2.1、物…