3.Maven&JDBC

Pasted image 20250320202641

1. Maven配置

2. JDBC原生开发网站并进行数据库访问

2.1. 创建项目并安装mysql依赖项

创建好项目后,到maven的仓库 Maven Repository: Search/Browse/Explore
搜索Mysql,选这个
Pasted image 20250320204250
进去后选一个版本然后复制下面的代码
Pasted image 20250320204509
然后复制到 pom.xml
Pasted image 20250320204614
然后点这个刷新下载maven
Pasted image 20250320204646
下载好后就会有这个
Pasted image 20250320204708

2.2. 注册数据库驱动

新建一个Servlet包,创建一个类并写入基本的Get代码
Pasted image 20250320205057

public class JdbcServlet extends HttpServlet {  
    @Override  
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
        String id=req.getParameter("id");  
        String sql="select * from users where id="+id;  
  
    }  
}

完善注册数据库驱动代码

Note
首先,导入数据库驱动jar包(以MySQL为例,mysql-connector-java-5.1.40-bin.jar),然后注册驱动

// 方法一.导致MySql驱动被注册两次,还导致程序和具体驱动类绑定,切换数据库,需要修改java源码重新编译,不建议使用!
DriverManager.registerDriver(new Driver());

// 方法二.驱动类的静态代码块已经注册驱动,只需要加载驱动类即可
// 用反射加载,与驱动类字符串绑定,字符串可放在配置文件,切换数据库,无需改源码重新编译,只需要修改配置文件!    
Class.forName("com.mysql.jdbc.Driver");
Class.forName("com.mysql.jdbc.Driver");

2.3. 建立数据库连接

Note
Connection connection = DriverManager.getConnection(url,user,password); 
    
url格式
    MySql:      jdbc:mysql://ip:3306/sid  (本机地址简写jdbc:mysql:///sid)
    Oracle:     jdbc:oracle:thin:@ip:1521:sid
    SqlServer:  jdbc:microsoft:sqlserver://ip:1433;DatabaseName=sid
    
参数(可选,user和password可以写在url参数中)
    ?user=lioil&password=***&useUnicode=true&characterEncoding=UTF-8
    
协议:子协议://ip地址:端口号/库名?参数1=&参数2=值
jdbc:mysql://localhost:3306/sid?useUnicode=true&characterEncoding=utf-8
@WebServlet(name = "jdbc",value = "/sql")  
public class JdbcServlet extends HttpServlet {  
    @Override  
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
        String id=req.getParameter("id");  
        String sql="select * from users where id="+id;  
        String url="jdbc:mysql://localhost:3306/mysql?useUnicode=true&characterEncoding=utf-8";  
        try {  
            Class.forName("com.mysql.jdbc.Driver");  
            Connection connection =  DriverManager.getConnection(url,"root","123456");  
            System.out.println(connection);  
        } catch (ClassNotFoundException | SQLException e) {  
            throw new RuntimeException(e);  
        }  
    }  
}

String url="jdbc:mysql://localhost:3306/ mysql?useUnicode=true&characterEncoding=utf-8" 是数据库名字

2.4. 创建Statement执行SQL

Note
1.Statement常用方法:
    单个执行SQL语句
    boolean execute(String sql)         执行SQL语句,没有结果集返回false,有返回true(通过Statement.getResultSet获取结果集);
    ResultSet executeQuery(String sql)  执行select语句,返回结果集
    int executeUpdate(String sql)       执行insert delete update语句,返回影响行数
    
    批量执行SQL语句(insert update delete)
    void addBatch(String sql)           批量添加SQL语句(insert update delete)
    int[] executeBatch()                批量传输SQL到数据库执行,返回每个SQL语句影响行数(数组)
    void clearBatch()                   清空SQL

3.单个执行SQL语句
    // 方法一.Statement
    Statement statement = connection.createStatement();
    int row = statement.executeUpdate(sql);
    ResultSet resultSet = statement.executeQuery(sql);
    
    ResultSet默认不能反向修改数据库记录,但可通过指定参数Statement来创建可改数据的ResultSet,不建议使用,应该用update语句修改数据!!!      
        Statement state = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE);       
        ResultSet resultSet = state.executeQuery("select * from user");     
        resultSet.next();       
        resultSet.updateString("name", "lioil");        
        resultSet.updateRow();
        
        Statement createStatement(int resultSetType, int resultSetConcurrency)  
        resultSetType 结果集类型   
            ResultSet.TYPE_FORWARD_ONLY        不支持滚动,只能向前.
            ResultSet.TYPE_SCROLL_INSENSITIVE  支持滚动,迟钝不敏感
            ResultSet.TYPE_SCROLL_SENSITIVE    支持滚动,敏感          
        resultSetConcurrency 是否支持修改类型
            ResultSet.CONCUR_READ_ONLY   不支持修改
            ResultSet.CONCUR_UPDATABLE   支持修改

    // 方法二.PrepareStatement预编译防止SQL注入攻击,用户参数?只做参数,不参与编译,传入SQL关键字无用
    PrepareStatement statement = connection.prepareStatement("SELECT * FROM user WHERE name=? AND password=?");
    statement.setString(1, "lioil");
    statement.setString(2, "12345");    
    ResultSet resultSet = statement.executeQuery();
    
    statement.setInt(...);
    statement.setDouble(...);
    statement.setDate(...);
    
    SQL注入:用户恶意传入一些SQL特殊关键字,导致SQL语义变化
    SELECT * FROM user WHERE name='lioil' AND password='12345';          -- 正常  
    SELECT * FROM user WHERE name='lioil' OR 1=1; --' AND password='1';  -- 恶意注入'lioil' OR 1=1; --' 使语句在OR 1=1;处结束,password没有执行

4.批量执行SQL
Statement批处理        
    Statement smt = conn.createStatement();
    smt.addBatch(sql1);
    smt.addBatch(sql2);
    smt.addBatch(sql3); 
    smt.executeBatch();
    
    优点:可执行多条不同结构SQL语句
    缺点:没预编译,效率低
    
PrparedStatement批处理     
    PrepareStatement preSmt = conn.prepareStatement("insert into user values(null,?)");         
    for(int i=1;i<=100000;i++){
        preSmt.setString(1,"id_"+i);
        preSmt.addBatch();      
        // 1000条为一批次, 减少内存占用
        if(i%1000==0){
            preSmt.executeBatch();
            preSmt.clearBatch();
        }
    }
    preSmt.executeBatch();
    
    优点:SQL结构相同,只编译一次,效率高
    缺点:只能执行相同结构SQL语句

粘贴语句

Statement statement = connection.createStatement();
int row = statement.executeUpdate(sql);
ResultSet resultSet = statement.executeQuery(sql);

这里要把 executeUpdate 改成 executeQuery 不然会报错

String id=req.getParameter("id");  
        String sql="select * from user where id="+id;  
        String url="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";  
        try {  
            Class.forName("com.mysql.jdbc.Driver");  
            Connection connection =  DriverManager.getConnection(url,"root","123456");  
//            System.out.println(connection);  
            Statement statement = connection.createStatement();  
            ResultSet row = statement.executeQuery(sql);  
            ResultSet resultSet = statement.executeQuery(sql);  
        } catch (ClassNotFoundException | SQLException e) {  
            throw new RuntimeException(e);  
        }

2.5. 对结果ResultSet进行提取

Note
1.移动结果集的指针/游标/当前行:
    boolean next()            移到下一行, 这行有数据返回true, 无返回false
    boolean Previous()        移到前一行
    boolean absolute(int row) 移到指定行 
    boolean first()           移到首行
    boolean last()            移到尾行
    void beforeFirst()        移到首行前一行
    void afterLast()          移到尾行后一行
    
2.获取数据
    String getString(int cloumnCount)   根据列索引,从当前行中获得String 类型数据
    String getString(String columnName) 根据列名,从当前行中获得String类型数据
    String getInt(...)
    String getLong(...)
    String getFloat/getDouble(...)
    String getDate(...)
    
实例:
    while(resultSet.next()){
        String val2 = resultSet.getString(2);
        Float val3 = resultSet.getFloat(3);
        Date val4 = resultSet.getDate(4);
        ...
    }
ResultSet resultSet = statement.executeQuery(sql);  
    while(resultSet.next()) {  
        resp.getWriter().println(resultSet.getString("id"));  
        resp.getWriter().println(resultSet.getString("name"));  
        resp.getWriter().println(resultSet.getString("email"));  
        resp.getWriter().println(resultSet.getString("age"));  
    }  
} catch (ClassNotFoundException | SQLException e) {  
    throw new RuntimeException(e);  
}

访问后就会有回显了
Pasted image 20250320215226

3. JDBC sql注入

3.1. 不安全的sql语法

这样写的sql查询语句是不安全的

String sql="select * from user where id="+id;

比如
Pasted image 20250320215708

这里的原因就是因为它使用了 + 即会拼接我们的id,导致sql注入

3.2. 安全的写法

这里采用 jdbc 中的预编译机制
案例

// 方法二.PrepareStatement预编译防止SQL注入攻击,用户参数?只做参数,不参与编译,传入SQL关键字无用
PrepareStatement statement = connection.prepareStatement("SELECT * FROM user WHERE name=? AND password=?");
statement.setString(1, "lioil");
statement.setString(2, "12345");    
ResultSet resultSet = statement.executeQuery();

statement.setInt(...);
statement.setDouble(...);
statement.setDate(...);

SQL注入:用户恶意传入一些SQL特殊关键字,导致SQL语义变化
SELECT * FROM user WHERE name='lioil' AND password='12345';          -- 正常  
SELECT * FROM user WHERE name='lioil' OR 1=1; --' AND password='1';  -- 恶意注入'lioil' OR 1=1; --' 使语句在OR 1=1;处结束,password没有执行

首先需要把sql语句改了

//原来不安全的查询语句
String sql="select * from user where id="+id;
//现在的预编译查询语句
String sql = "SELECT * FROM user WHERE id = ?";

然后设置SQL语句的参数

// 加载数据库驱动(新版驱动)  
Class.forName("com.mysql.cj.jdbc.Driver");  
  
// 连接数据库  
try (Connection connection = DriverManager.getConnection(url, "root", "123456");  
     PreparedStatement preparedStatement = connection.prepareStatement(sql)) {  
  
    // 设置 SQL 语句的参数  
    preparedStatement.setString(1, id);  
  
    // 执行查询  
    try (ResultSet resultSet = preparedStatement.executeQuery()) {  
        if (!resultSet.isBeforeFirst()) {  
            resp.getWriter().println("未找到数据");  
            return;  
        }