JavaWeb01-JDBC

1.JavaWeb介绍

什么是JavaWeb?

  • Web:全球广域网,也称万维网(www),能够通过浏览器访问的网站
  • JavaWeb:使用Java技术来解决相关web互联网领域的技术栈

image

  1. 网页:展现数据
  2. 数据库:存储和管理数据
  3. JavaWeb程序:逻辑处理

数据库已学习

2.JDBC

  • JDBC就是使用Java语言操作关系型数据库的一套API

image

2.1、JDBC简介

JDBC概念

  • JDBC就是使用Java语言操作关系型数据库的一套API
  • 全称:(Java DataBase Connectivity)Java数据库连接

JDBC本质:

  • 官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口
  • 各个数据库厂商去实现这套接口,提供数据库驱动jar包
  • 我们可以使用这套接口(JDBC)编程,真正执行代码的是驱动jar包中的实现类

JDBC的好处:

  • 各数据库厂商使用相同的接口,Java代码不需要针对不同数据库分别开发
  • 可随时替换底层数据库,访问数据库的Java代码基本不变

image

2.2、JDBC快速入门

步骤:

  1. 创建工程,导入驱动jar包

    image

  2. 注册驱动

    Class.forName("com.mysql.jdbc.Driver");
    
  3. 获取连接

    Connection conn = DriverManager.getConnection(url,username,password);
    
  4. 定义SQL语句

    String sql = "update..."
    
  5. 获取执行SQL对象

    Statement stmt = conn.createStatement();
    
  6. 执行SQL

    stmt.executeUpdate(sql);
    
  7. 处理返回结果

  8. 释放资源

package com.itheima.jdbc;

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

public class JDBCDemo {

    public static void main(String[] args) throws Exception {
        // 1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2.获取连接
        String url = "jdbc:mysql://127.0.0.1:3306/itheima";
        String username = "root";
        String password = "wwh030705";
        Connection conn = DriverManager.getConnection(url,username,password);

        // 3.定义sql
        String sql = "update account set money = 2000 where id = 1";
        // 4.获取执行sql的对象 Statement
        Statement stmt = conn.createStatement();
        // 5.执行sql
        int count = stmt.executeUpdate(sql);//受影响的行数
        // 6.处理返回结果
        System.out.println(count);
        // 7.释放资源
        stmt.close();
        conn.close();


    }
}

image

2.3、JDBC API详解

(1)DriverManager

  • DriverManager(驱动管理类)作用:

    1. 注册驱动

      Class.forName("com.mysql.jdbc.Driver");
      
      • 查看Driver类源码

        image

        提示:

        • MySQL 5之后的驱动包,可以省略注册驱动的步骤
        • 自动加载jar包中的META-INF/services/java.sql.Driver文件中的驱动类
    2. 获取数据库连接

      image

      • 参数

        1. url:连接路径

          语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2...
          示例:jdbc:mysql://127.0.0.1:3306/itheima
          细节:
          1.如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数键值对
          2.配置 useSSL = false参数,禁用安全连接方式,解决警告问题
          
        2. user:用户名

        3. password:密码

      package com.itheima.jdbc;
      
      import java.sql.Connection;
      import java.sql.DriverManager;
      import java.sql.Statement;
      
      /**
       * JDBC API详解:DriverManager
       */
      public class JDBCDemo2_DriverManager {
      
          public static void main(String[] args) throws Exception {
              // 1.注册驱动
              //Class.forName("com.mysql.jdbc.Driver");
              // 2.获取连接 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,可以简化书写
              String url = "jdbc:mysql:///itheima?useSSL=false";
              String username = "root";
              String password = "wwh030705";
              Connection conn = DriverManager.getConnection(url,username,password);
      
              // 3.定义sql
              String sql = "update account set money = 2000 where id = 1";
              // 4.获取执行sql的对象 Statement
              Statement stmt = conn.createStatement();
              // 5.执行sql
              int count = stmt.executeUpdate(sql);//受影响的行数
              // 6.处理返回结果
              System.out.println(count);
              // 7.释放资源
              stmt.close();
              conn.close();
      
      
          }
      }
      
      

(2)Connection

  1. 获取执行SQL的对象

    • 普通执行SQL对象

      Statement createStatement()
      
    • 预编译SQL的执行SQL对象:防止SQL注入

      PreparedStatement prepareStatement(sql)
      
    • 执行存储过程的对象

      CallableStatement prepareCall(sql)
      
  2. 事务管理

    • MySQL事务管理

      开启事务:begin;/start transaction;
      提交事务:commit
      回滚事务:rollback;
      
      MySQL默认自动提交事务
      
    • JDBC事务管理:Connection接口中定义了3个对应的方法

      开启事务:setAutoCommit(boolean autoCommit):true为自动提交事务;false为手动提交事务,即为开启事务
      提交事务:commit()
      回滚事务:rollback()
      
    package com.itheima.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    /**
     * JDBC API详解:Connection
     */
    public class JDBCDemo3_Connection {
    
        public static void main(String[] args) throws Exception {
            // 1.注册驱动
            //Class.forName("com.mysql.jdbc.Driver");
            // 2.获取连接 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,可以简化书写
            String url = "jdbc:mysql:///itheima?useSSL=false";
            String username = "root";
            String password = "wwh030705";
            Connection conn = DriverManager.getConnection(url,username,password);
    
            // 3.定义sql
            String sql1 = "update account set money = 3000 where id = 1";
            String sql2 = "update account set money = 3000 where id = 2";
            // 4.获取执行sql的对象 Statement
            Statement stmt = conn.createStatement();
    
    
    
    
            try {
                //开启事务
                conn.setAutoCommit(false);
    
                // 5.执行sql
                int count1 = stmt.executeUpdate(sql1);//受影响的行数
                // 6.处理返回结果
                System.out.println(count1);
                // 5.执行sql
                int count2 = stmt.executeUpdate(sql2);//受影响的行数
                // 6.处理返回结果
                System.out.println(count2);
    
                //提交事务
                conn.commit();
            } catch (Exception throwables) {
                //事务回滚
                conn.rollback();
                throwables.printStackTrace();
            }
    
            // 7.释放资源
            stmt.close();
            conn.close();
    
    
        }
    }
    
    

    使用try catch 来捕捉异常,一旦在sql语句执行过程中出现异常,进行事务回滚。

    如:

    try {
                //开启事务
                conn.setAutoCommit(false);
    
                // 5.执行sql
                int count1 = stmt.executeUpdate(sql1);//受影响的行数
                // 6.处理返回结果
                System.out.println(count1);
                int i = 3/0;
                // 5.执行sql
                int count2 = stmt.executeUpdate(sql2);//受影响的行数
                // 6.处理返回结果
                System.out.println(count2);
    
                //提交事务
                conn.commit();
            } catch (Exception throwables) {
                //事务回滚
                conn.rollback();
                throwables.printStackTrace();
            }
    

    在第9行代码中 出现int i= 3/0 异常,account表中还是保持原来的数据不变,若没有开启事务,则会将第一条数据改为3000;

(3)Statement

  • Statement作用:

    1. 执行SQL语句
  • 执行SQL语句

    int executeUpdate(sql):执行DML、DDL语句
     返回值:(1)DML语句影响的行数(2)DDL语句执行后,执行成功也可能返回0
    
    ResultSet executeQuery(sql):执行DQL语句
     返回值:ResultSet结果集对象
    
    • 执行DML语句
    package com.itheima.jdbc;
    
    import org.junit.Test;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    /**
     * JDBC API详解:Statement
     */
    public class JDBCDemo4_Statement {
    
        @Test
         public void testDML() throws Exception {
            // 1.注册驱动
            //Class.forName("com.mysql.jdbc.Driver");
            // 2.获取连接 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,可以简化书写
            String url = "jdbc:mysql:///itheima?useSSL=false";
            String username = "root";
            String password = "wwh030705";
            Connection conn = DriverManager.getConnection(url, username, password);
    
            // 3.定义sql
            String sql1 = "update account set money = 3000 where id = 2";
            String sql2 = "insert into account values (3,'王五',1000)";
            // 4.获取执行sql的对象 Statement
            Statement stmt = conn.createStatement();
            // 5.执行sql
            int count1 = stmt.executeUpdate(sql1);//执行完DML语句,受影响的行数
            int count2 = stmt.executeUpdate(sql2);//执行完DML语句,受影响的行数
            // 6.处理返回结果
            //System.out.println(count);
            if (count1 > 0) {
                System.out.println("修改成功");
            } else {
                System.out.println("修改失败");
    
            }
            if (count2 > 0) {
                System.out.println("添加成功");
            } else {
                System.out.println("添加失败");
                // 7.释放资源
                stmt.close();
                conn.close();
            }
    
    
        }
    }
    
    
    • 执行DQL语句

      image

(4)ResultSet

  • ResultSet(结果集对象)作用:

    1. 封装了DQL查询语句的结果
    ResultSet stmt.executeQuery(sql):执行DQL语句,返回ResultSet对象
    
  • 获取查询结果

    boolean next():
    (1)将光标从当前位置向下移动一行 
    (2)判断当前行是否为有效行
        返回值:
        	true:有效行,当前行有数据
        	false:无效行,当前行没有数据
    
    xxx getXxx(参数):获取数据
        xxx:数据类型;如:int getInt(参数);String getString(参数)
        
        参数:
        	int:列的编号,从1开始
        	String:列的名称
    
  • 使用步骤

    1. 游标向下移动一行,并判断该行是否有数据:next()
    2. 获取数据:getXxx(参数)
    //循环判断游标是否是最后一行末尾
    while(rs.next()){
        //获取数据
        rs.getXxx(参数);
    }
    

    image

案例:

image

步骤:

  1. 定义实体类Account

    package com.itheima.pojo;
    
    public class Account {
        private int id;
        private String name;
        private double money;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public double getMoney() {
            return money;
        }
    
        public void setMoney(double money) {
            this.money = money;
        }
    
        @Override
        public String toString() {
            return "Account{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", money=" + money +
                    '}';
        }
    }
    
    
    
  2. 查询数据,封装到Account对象中

  3. 将Account对象存入ArrayList集合中

package com.itheima.jdbc;

import com.itheima.pojo.Account;
import org.junit.Test;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * JDBC API详解:ResultSet
 */
public class JDBCDemo5_ResultSet {
    /**
     * 执行DQL
     * @throws Exception
     */
    @Test
    public void testResultSet() throws Exception {

        // 1.注册驱动
        //Class.forName("com.mysql.jdbc.Driver");
        // 2.获取连接 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,可以简化书写
        String url = "jdbc:mysql:///itheima?useSSL=false";
        String username = "root";
        String password = "wwh030705";
        Connection conn = DriverManager.getConnection(url,username,password);

        //3.定义SQL
        String sql = "select * from account";

        //4.获取statement对象
        Statement stmt = conn.createStatement();

        //5.执行sql
        ResultSet rs = stmt.executeQuery(sql);
        //6.处理结果,遍历rs中所有数据
        // 6.1光标向下移动一行,并判断该行是否有数据
        while(rs.next()){
            //6.2获取数据 getXxx()
            int id = rs.getInt(1);
            String name = rs.getString(2);
            double money = rs.getDouble(3);

            System.out.println(id);
            System.out.println(name);
            System.out.println(money);

            System.out.println("------------------------");


        }
        //7.释放资源
        rs.close();
        stmt.close();
        conn.close();

    }
    /**
     * 查询account账户表数据,封装为Account对象中,并且存储到Arraylist集合中
     * 1.定义实体类Account
     * 2.查询数据,封装到Account对象中
     * 3.将Account对象存入ArrayList集合中
     * @throws Exception
     */
    @Test
    public void testResultSet2() throws Exception {

        // 1.注册驱动
        //Class.forName("com.mysql.jdbc.Driver");
        // 2.获取连接 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,可以简化书写
        String url = "jdbc:mysql:///itheima?useSSL=false";
        String username = "root";
        String password = "wwh030705";
        Connection conn = DriverManager.getConnection(url,username,password);

        //3.定义SQL
        String sql = "select * from account";

        //4.获取statement对象
        Statement stmt = conn.createStatement();

        //5.执行sql
        ResultSet rs = stmt.executeQuery(sql);

        //创建集合
        List<Account> list = new ArrayList<>();
        //6.处理结果,遍历rs中所有数据
        // 6.1光标向下移动一行,并判断该行是否有数据
        while(rs.next()){
            Account account = new Account();

            //6.2获取数据 getXxx()
            int id = rs.getInt(1);
            String name = rs.getString(2);
            double money = rs.getDouble(3);

            //赋值
            account.setId(id);
            account.setName(name);
            account.setMoney(money);
            // 存入集合
            list.add(account);
        }
        System.out.println(list);
        //7.释放资源
        rs.close();
        stmt.close();
        conn.close();

    }

}

image

(5)PreparedStatement

  • PrepareStatement作用:
    1. 预编译SQL语句并执行:预防SQL注入问题
  • SQL注入
    • SQL注入是通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法。

image

package com.itheima.jdbc;

import org.junit.Test;

import java.sql.*;

/**
 * 用户登录
 */
public class JDBCDemo6_UserLogin {
    @Test
    public void testLogin() throws Exception {
        // 1.注册驱动
        //Class.forName("com.mysql.jdbc.Driver");
        // 2.获取连接 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,可以简化书写
        String url = "jdbc:mysql:///test?useSSL=false";
        String username = "root";
        String password = "wwh030705";
        Connection conn = DriverManager.getConnection(url,username,password);

        //接受用户输入的用户名和密码
        String name = "zhangsan";
        String pwd = "123";

        String sql = "select * from tb_user where username = '"+name+"' and password = '"+pwd+"'";
        //获取stmt对象
        Statement stmt = conn.createStatement();

        //执行sql
        ResultSet rs = stmt.executeQuery(sql);

        //判断登录是否成功
        if(rs.next()){
            System.out.println("登陆成功");
        }else {
            System.out.println("登陆成功");
        }
        // 7.释放资源
        rs.close();
        stmt.close();
        conn.close();
    }

    @Test
    public void testLogin_inject() throws Exception {
        // 1.注册驱动
        //Class.forName("com.mysql.jdbc.Driver");
        // 2.获取连接 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,可以简化书写
        String url = "jdbc:mysql:///test?useSSL=false";
        String username = "root";
        String password = "wwh030705";
        Connection conn = DriverManager.getConnection(url,username,password);

        //接受用户输入的用户名和密码
        String name = "sajdhsad";
        String pwd = "'or '1' = '1";

        String sql = "select * from tb_user where username = '"+name+"' and password = '"+pwd+"'";
        System.out.println(sql);
        //获取stmt对象
        Statement stmt = conn.createStatement();

        //执行sql
        ResultSet rs = stmt.executeQuery(sql);

        //判断登录是否成功
        if(rs.next()){
            System.out.println("登陆成功");
        }else {
            System.out.println("登陆成功");
        }
        // 7.释放资源
        rs.close();
        stmt.close();
        conn.close();
    }

}

第一个测试用例是输入正常的用户名和密码 显示登陆成功

但是第二个测试,用户名和密码并不正确,为何会显示登陆成功呢?

我们通过打印sql语句可以看到:

image

where条件后 username = 'sajdhsad' and password = ''这个条件为false。'1' = '1'恒等式 为true 则'sajdhsad' and password = ''or '1' = '1'为true 所以也显示登录成功,这就是SQL注入。PreparedStatement这个接口就是为了解决SQL注入的。

如何解决?

image

@Test
    public void testPreparedStatement() throws Exception {
        // 1.注册驱动
        //Class.forName("com.mysql.jdbc.Driver");
        // 2.获取连接 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,可以简化书写
        String url = "jdbc:mysql:///test?useSSL=false";
        String username = "root";
        String password = "wwh030705";
        Connection conn = DriverManager.getConnection(url,username,password);

        //接受用户输入的用户名和密码
        String name = "zhangsan";
        String pwd = "'or '1' = '1";

        //定义sql语句,其中的参数用?来代替
        String sql = "select * from tb_user where username = ? and password = ? ";
        //通过Connection对象获取,并传入对应的sql语句
        PreparedStatement pstmt = conn.prepareStatement(sql);
        //设置参数
        pstmt.setString(1,name);
        pstmt.setString(2,pwd);
        //执行sql
        ResultSet rs = pstmt.executeQuery();

        //判断登录是否成功
        if(rs.next()){
            System.out.println("登陆成功");
        }else {
            System.out.println("登陆失败");
        }
        // 7.释放资源
        rs.close();
        pstmt.close();
        conn.close();
    }

这样的话则登录失败~ 实质上是将敏感字符进行转移如密码中的单引号进行转义

image

2.4、数据库连接池

(1)数据库连接池简介

  • 数据库连接池是个容器,负责分配、管理数据库连接(Connection)
  • 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
  • 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
  • 好处:
    • 资源重用
    • 提升系统响应速度
    • 避免数据库连接遗漏

image

(2)数据库连接池实现

  • 标准接口:DataSource

    • 官方(SUN)提供的数据库连接池标准接口,有第三方组织实现此接口。

    • 功能:获取连接

      Connection getConnection()
      
  • 常见的数据库连接池

    • DBCP
    • C3P0
    • Druid
  • Druid(德鲁伊)

    • Druid连接池是阿里巴巴开源的数据库连接池项目
    • 功能强大,性能优秀,是Java语言最好的数据库连接池之一

Driud使用步骤

  1. 导入jar包
  2. 定义配置文件
  3. 加载配置文件
  4. 获取数据库连接池对象
  5. 获取连接

image

posted @ 2025-10-28 21:27  齐天大圣951  阅读(3)  评论(0)    收藏  举报