Java学习日志(三十三): JDBC预处理对象,连接池C3P0

JavaEE学习日志持续更新----> 必看!JavaEE学习路线(文章总汇)

JDBC预处理对象

登陆注册案例模拟:用户注入攻击数据库

在这里插入图片描述
首先创建一张用户表

-- 创建数据库day04
CREATE DATABASE day04;
-- 使用数据库day04
USE day04;
-- 创建用户表users 字段:用户主键,用户名,密码
CREATE TABLE users(
  -- 用户逐渐
  uid INT PRIMARY KEY AUTO_INCREMENT,
  -- 用户名
  username VARCHAR(20),
  -- 密码
  PASSWORD VARCHAR(20)
);

注册

INSERT INTO users(username,PASSWORD) VALUES('a','123');

在这里插入图片描述
登陆

SELECT * FROM users WHERE username='a' AND PASSWORD='123';-- 登陆成功
SELECT * FROM users WHERE username='a' AND PASSWORD='124';-- 登陆失败

在这里插入图片描述
使用sql语句注入攻击数据库:sql并不严谨,在条件的末尾or上一个为true的值,那么前边的条件是什么则无所谓。
输入的密码:2' OR '1=1

SELECT * FROM users WHERE username='a' AND PASSWORD='2' OR '1=1';

在这里插入图片描述
使用Java程序模拟用户的登陆操作

  • 使用Scanner获取用户输入的用户名和密码
  • 使用JDBC技术操作数据库
    1. 注册驱动,获取数据库连接对象Connection,使用JDBCUtils完成
    2. 获取执行者对象Statement
    3. 执行sql语句,获取结果
    4. 处理结果
    - 有结果:登陆成功
    - 没有结果:您输入的用户名或密码有误
    5. 释放资源,使用JDBCUtils完成
public class Demo01Login {
    public static void main(String[] args) throws SQLException {
        //1.使用Scanner获取用户输入的用户名和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();//nextLine直接获取一行
        System.out.println("请输入密码:");
        String password = sc.nextLine();//nextLine直接获取一行
        //2.使用JDBC技术操作数据库
        //注册驱动,获取数据库连接对象Connection,使用JDBCUtils完成
        Connection conn = JDBCUtils.getConnection();
        //获取执行者对象Statement
        Statement stat = conn.createStatement();
        //执行sql语句,获取结果
        String sql = "SELECT * FROM users WHERE username='"+username+"' AND PASSWORD='"+password+"';";
        System.out.println(sql);
        ResultSet rs = stat.executeQuery(sql);
        //处理结果
        if(rs.next()){
            //有结果:登陆成功
            System.out.println("登陆成功"+rs.getString("username")+"\t"+rs.getString("password"));
        }else {
            //没有结果:您输入的用户名或密码有误
            System.out.println("您输入的用户名密码有误");
        }
        //释放资源,使用JDBCUtils完成
        JDBCUtils.close(rs,stat,conn);

    }
}

注入式攻击
在这里插入图片描述

解决用户注入式攻击

使用statement接口的子接口PreparedStatement实现:
java.sql.PreparedStatement接口 extends Statement接口,表示预编译的sql语句对象,SQL语句已预编译并存储在PreparedStatement对象中。 然后,可以使用此对象多次有效地执行此语句。

注意:PreparedStatement执行sql语句比statement效率更高

PreparedStatement使用:

  1. 获取PreparedStatement对象,可以使用collection中的方法
    PreparedStatement prepareStatement​(String sql) 创建一个 PreparedStatement对象,用于将参数化SQL语句发送到数据库。

    参数:String sql:传递sql语句,可以使用?占位符
    
  2. 设置?占位符的实际参数

  3. 执行sql语句,获取结果

设置?占位符的实际使用参数,可以使用PreparedStatement对象中的方法:
void setObject​(int parameterIndex, Object x) 使用给定对象设置指定参数的值。

参数:
    int parameterIndex:要设置的第几个?占位符,从1开始
    Object x:?占位符的实际参数

注意:有几个问号占位符,就调用几次setObject方法,设置几个参数

代码示例:

public class Demo02Login {
    public static void main(String[] args) throws SQLException {
        //1.使用Scanner获取用户输入的用户名和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();//nextLine直接获取一行
        System.out.println("请输入密码:");
        String password = sc.nextLine();//nextLine直接获取一行
        //2.使用JDBC技术操作数据库
        //注册驱动,获取数据库连接对象Connection,使用JDBCUtils完成
        Connection conn = JDBCUtils.getConnection();
        //获取预编译的执行者对象
        //查询的sql语句,可以使用?代替实际参数
        String sql = "SELECT * FROM users WHERE username= ? AND PASSWORD= ?;";
        PreparedStatement pst = conn.prepareStatement(sql);
        /*
            设置?占位符的实际使用参数,可以使用PreparedStatement对象中的方法
                void setObject​(int parameterIndex, Object x) 使用给定对象设置指定参数的值。
                参数:
                    int parameterIndex:要设置的第几个?占位符,从1开始
                    Object x:?占位符的实际参数
                注意:有几个问号占位符,就调用几次setObject方法,设置几个参数
         */
        pst.setObject(1,username);
        pst.setObject(2,password);
        /*
            执行sql语句,获取结果
            使用PreparedStatement对象中的方法,和statement相同
         */
        ResultSet rs = pst.executeQuery();
        //处理结果
        if(rs.next()){
            //有结果:登陆成功
            System.out.println("登陆成功"+rs.getString("username")+"\t"+rs.getString("password"));
        }else {
            //没有结果:您输入的用户名或密码有误
            System.out.println("您输入的用户名密码有误");
        }
        //释放资源,使用JDBCUtils完成
        JDBCUtils.close(rs,pst,conn);
    }
}

注入式攻击失败
在这里插入图片描述

PreparedStatement对象对数据库表进行增删改查

增删改

@Test
    public void testInsert() throws SQLException {
        //获取Connection对象
        Connection conn = JDBCUtils.getConnection();
        //获取预编译的执行者对象
        PreparedStatement pst = conn.prepareStatement("INSERT INTO users(username,PASSWORD) VALUES(?,?);");
        //PreparedStatement pst = conn.prepareStatement("UPDATE users SET PASSWORD=? WHERE username=?;");
        //PreparedStatement pst = conn.prepareStatement("DELETE FROM users WHERE uid=?;");
        //设置问号占位符的实际参数
        //增加
        pst.setObject(1, "老王");
        pst.setObject(2, "abc");
        //修改
//        pst.setObject(1,"123");
//        pst.setObject(2,"老王");
        //删除
//        pst.setObject(1,2);
        //执行sql语句获取结果
        int row = pst.executeUpdate();
        //处理结果集
        if (row > 0) {
            System.out.println("数据库操作成功");
        }else {
            System.out.println("数据库操作失败!");
        }
        //释放资源
        JDBCUtils.close(null, pst, conn);

    }

查询

@Test
    public void testSelect() throws SQLException {
        //获取Connection对象
        Connection conn = JDBCUtils.getConnection();
        //获取预编译的执行者对象
        PreparedStatement pst = conn.prepareStatement("select * from users where uid = ?");
        //设置问号占位符的实际参数
        pst.setObject(1,2);
        //执行sql语句,获取结果
        ResultSet rs = pst.executeQuery();
        //处理结果
        while (rs.next()){
            System.out.println(rs.getString("username")+'\t'+rs.getString("password"));
        }
        JDBCUtils.close(rs,pst,conn);

    }

连接池

连接池的原理

在这里插入图片描述

连接池的规范接口DataSource

javax.sql.DataSource
在这里插入图片描述

创建并测试C3P0连接池的工具类

C3P0连接池的工具类:使用C3P0连接池获取数据库连接对象Connection并返回

连接池规范接口:javax.sql.DataSource接口

定义了一个从连接池中获取连接的方法:
Connection getConnection() 尝试与此 DataSource对象表示的数据源建立连接。

C3P0实现了连接池的规范接口DataSource,重写了getConnection方法
com.mchange.v2.c3p0.ComboPooledDataSource implements DataSource接口

使用步骤

  1. 在成员位置创建一个静态的ComboPooledDataSource对象
  2. 在静态代码中,使用ComboPooledDataSource对象的setXXX方法,设置数据库的连接信息
  3. 定义一个静态方法,从ComboPooledDataSource对象中获取数据库连接对象Connection并返回
  4. 定义一个释放资源的方法

创建C3P0连接池的工具类:

public class C3P0Utils {
    //1.在成员位置创建一个静态的ComboPooledDataSource对象
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
    //2.在静态代码中,使用ComboPooledDataSource对象的setXXX方法,设置数据库的连接信息
    //优先执行,只执行一次
    static {
        try {
            //注册驱动
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            //设置url
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/day04");
            //设置用户名
            dataSource.setUser("root");
            //设置密码
            dataSource.setPassword("root");
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }

    }
    //3.定义一个静态方法,从ComboPooledDataSource对象中获取数据库连接对象Connection并返回
    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            //获取Connection失败,让程序停止,把编译异常转为运行时异常
            throw new RuntimeException("获取数据库连接对象失败"+e);
        }
    }
    //4.定义一个释放资源的方法
    public static void close(ResultSet rs, Statement stat, Connection conn){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(stat!=null){
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();//把连接归还给连接池
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

测试工具类:

public class TestC3P0Utils {
    @Test
    public void test01() throws SQLException {
        //使用C3P0Utils工具类的方法getConnection,获取数据库连接对象
        Connection conn = C3P0Utils.getConnection();
        //System.out.println(conn);//com.mchange.v2.c3p0.impl.NewProxyConnection@13e39c73
        //获取执行者对象
        Statement stat = conn.createStatement();
        //执行sql语句,获取结果
        ResultSet rs = stat.executeQuery("select * from users");
        //处理结果
        while(rs.next()){
            System.out.println(rs.getString("username")+"\t"+rs.getString("password"));
        }
        //释放资源
        C3P0Utils.close(rs,stat,conn);

    }
}

结果
在这里插入图片描述

带XML配置文件的C3P0连接池工具类

使用步骤:

  1. 在成员位置创建一个静态的ComboPooledDataSource对象
  2. 把c3p0-config.xml复制到当前模块的src下;C3P0就会自动解析xml,获取数据库连接信息给ComboPooledDataSource对象赋值
  3. 定义一个静态方法,从ComboPooledDataSource对象中获取数据库连接对象Connection并返回
  4. 定义一个释放资源的方法

配置文件:注意编码格式为UTF-8

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!-- 使用默认的配置读取连接池对象 -->
    <default-config>
        <!--  连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day04</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <!-- 连接池参数 -->
        <!-- 初始连接数 -->
        <property name="initialPoolSize">5</property>
        <!-- 最大连接数 -->
        <property name="maxPoolSize">10</property>
        <!-- 最大等待时间 -->
        <property name="checkoutTimeout">2000</property>
        <!-- 最大空闲回收时间 -->
        <property name="maxIdleTime">1000</property>
    </default-config>
</c3p0-config>

C3P0工具类

public class C3P0UtilsXML {
    //1.在成员位置创建一个静态的ComboPooledDataSource对象
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

    //3.定义一个静态方法,从ComboPooledDataSource对象中获取数据库连接对象Connection并返回
    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            //获取Connection失败,让程序停止,把编译异常转为运行时异常
            throw new RuntimeException("获取数据库连接对象失败"+e);
        }
    }
    //4.定义一个释放资源的方法
    public static void close(ResultSet rs, Statement stat, Connection conn){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(stat!=null){
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();//把连接归还给连接池
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

测试类

@Test
    public void test02() throws SQLException {
        //使用C3P0UtilsXML工具类的方法getConnection,获取数据库连接对象
        Connection conn = C3P0UtilsXML.getConnection();
        //System.out.println(conn);//com.mchange.v2.c3p0.impl.NewProxyConnection@1ca3b418
        //获取预编译的执行者对象
        PreparedStatement pst = conn.prepareStatement("SELECT * FROM users");
        //执行sql语句,获取结果
        ResultSet rs = pst.executeQuery();
        //处理结果
        while(rs.next()) {
            System.out.println(rs.getInt("uid") + "\t" + rs.getString("username") + "\t" + rs.getString("password"));
        }
        //释放资源
        C3P0UtilsXML.close(rs,pst,conn);
    }

结果
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值