JDBC连接池和预编译对象处理漏洞

预编译对象

SQL注入的漏洞:

  • 用户输入一些特殊的值,在拼接SQL字符串的时候,导致SQL的语法结构发生变化,从而绕过了SQL的条件。形成的安全问题,叫SQL注入漏洞。

  • 如何避免SQL注入漏洞:使用预编译对象PreparedStatement

如:

1.什么是预编译对象:


预编译对象:java.sql.PreparedStatement,是Statement的子接口,功能更强;是另外一种SQL执行平台对象,能够解决SQL注入漏洞;

1.1 Satement 和PreparedStatement的区别.

 Satement 对象每执行一条SQL语句都会先将这条SQL语句发送给数据库编译,数据库再执行。如果有一万条语句就要执行一万次就很效率很低。

PreparedStatement()会先将SQL语句发送数据库编译, PreparedStatement 会引用着预编译后的结果。 可以多次传入不同的参数给 PreparedStatement 对象并执行。相当于调用方法多次传入不同的参数。

1.2PreparedSatement的好处

1. prepareStatement() 会先将SQL语句发送给数据库预编译。 PreparedStatement 会引用着预编译后的结果。 可以多次传入不同的参数给 PreparedStatement 对象并执行。减少SQL编译次数,提高效率。

2. 安全性更高,没有SQL注入的隐患。

3. 提高了程序的可读性

1.3PreparedSatement使用步骤

1. 编写SQL语句,未知内容使用?占位: "SELECT * FROM user WHERE name=? AND password=?;";

2. 获得PreparedStatement对象

3. 设置实际参数

4. 执行参数化SQL语句

5. 关闭资源

代码演示:

package com.luliang.PreparedSatement;

import com.luliang.JDBCbao.JdbcUtils1;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;

public class Demo01 {
    public static void main(String[] args) throws Exception{
        Scanner scanner = new Scanner(System.in);
        System.out.println("请你输入账号:");
        String name = scanner.nextLine();
        System.out.println("请你输入密码:");
        String password = scanner.nextLine();
        //1.注册驱动获取连接
        Connection connection = JdbcUtils1.getConnection();
        //2.编写SQL语句,未知内容用?占位
        String sql="SELECT*FROM user WHERE name='"+name+"' AND password='"+password+"'";
        //2.1将PrepareStatement()会先将SQL语句发送给数据库预编译
        //如果查询到数据,锁门登陆成功
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //错误的密码
        ResultSet resultSet = preparedStatement.executeQuery();
        if(resultSet.next()){
            String name1 = resultSet.getString("name");
            System.out.println("恭喜你登陆:"+name1);
        }else {
            System.out.println("你输入的密码错误...");
        }
        //关闭资源
        JdbcUtils1.close(resultSet,preparedStatement, connection);
    }
}

PrepardSatement实现增删改查:

增:

package com.luliang.PreparedSatement;
import com.luliang.JDBCbao.JdbcUtils1;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

//增
public class Demo02 {
    public static void main(String[] args) throws Exception{
        //1.注册驱动,获取连接
        Connection connection = JdbcUtils1.getConnection();
        //2.编写SQL语句
        String sql="INSERT INTO liang VALUES(NULL,?,?,?)";
        //3.将PrepareStatement()会先将SQL语句发送给数据库预编译
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //设置参数
        preparedStatement.setString(1,"刘德华");
        preparedStatement.setString(2,"香港");
        preparedStatement.setInt(3,44);
        int resultSet = preparedStatement.executeUpdate();
        //影响的行数
        System.out.println("影响的行数:"+resultSet);
        //关闭资源
        JdbcUtils1.close(connection,preparedStatement);
        }
    }


删:

package com.luliang.PreparedSatement;

import com.luliang.JDBCbao.JdbcUtils1;

import java.sql.Connection;
import java.sql.PreparedStatement;

//删
public class Demo03 {
    public static void main(String[] args) throws Exception{
        //注册驱动,获取连接
        Connection connection = JdbcUtils1.getConnection();
        //编写SQL语句
        String sql="DELETE FROM liang WHERE id=?";
        //将PrepareStatement()会先将SQL语句发送给数据库预编译
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //设置参数
        preparedStatement.setInt(1,3);
        //执行DML语句:statement.executeUpdate(sql),返回int,表示影响行数
        int i = preparedStatement.executeUpdate();
        System.out.println("影响的行数:"+i);
        //关闭资源
        JdbcUtils1.close(connection,preparedStatement);
    }
}

改:

package com.luliang.PreparedSatement;

import com.luliang.JDBCbao.JdbcUtils1;

import java.sql.Connection;
import java.sql.PreparedStatement;

//改
public class Demo04 {
    public static void main(String[] args) throws Exception {
        //1.注册驱动,数据连接
        Connection connection = JdbcUtils1.getConnection();
        //2.编写SQL代码
        String sql="UPDATE liang SET salary=? WHERE id=?";
        //3.将PrepareStatement()会先将SQL语句发送给数据库预编译
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //设置数据
        //
        preparedStatement.setDouble(1,10000);
        preparedStatement.setInt(2,4);
        //设置影响的行数
        int i = preparedStatement.executeUpdate();
        System.out.println("影响的行数:"+i);
    }
}

查:

package com.luliang.PreparedSatement;

import com.luliang.JDBCbao.JdbcUtils1;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;

//查
public class Demo05 {
    public static void main(String[] args) throws Exception{
        //1.注册驱动,数据连接
        Connection connection = JdbcUtils1.getConnection();
        //书写SQL语句
        String sql="SELECT*FROM liang WHERE id<=?";
        //将PrepareStatement()会先将SQL语句发送给数据库预编译
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //设置数据
        preparedStatement.setInt(1,1);
        //影响数据的行数
        ResultSet resultSet = preparedStatement.executeQuery();
        //创建集合存放多个resultSet对象
        ArrayList<Employee> employees = new ArrayList<>();
        while (resultSet.next()){
            //移动到下一行有数据,取出这数据
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            String residese = resultSet.getString("residese");
            double salary = resultSet.getDouble("salary");
            //创建Employee对象
            Employee employee = new Employee(id, name, residese, salary);
            //将创建好的员工添加到集合中
            employees.add(employee);
        }
        //输出对象
        for (Employee employee : employees) {
            System.out.println(employee);
        }
        //关闭资源
        JdbcUtils1.close(resultSet,preparedStatement,connection);

    }
}

2.=怎么使用预编译=

//1.注册驱动
//2.获取连接

//3.创建SQL执行平台:预编译对象PreparedStatement
//3.1 改造SQL语句:使用?代替SQL里的参数值
//3.2 预编译SQL语句,得到预编译对象
//3.3 设置SQL语句里的参数值

//4.执行SQL语句
//5.处理结果
//6.释放资源

2.1SQL语句的改造

SQL语句里,所有的参数值,都要使用?代替掉。

2.2预编译SQL,得到的预编译对象

PreparedStatement pstmt = connection.prepareStatement(sql)`

2.3设置SQL的参数值

  • SQL里有几个?,就要设置几个值

  • 设置参数值的方法:pstmt.setXXX(参数序号, 参数值)

2.4执行SQL语句

  • 执行DQL语句:pstmt.executeQuery()

  • 执行DML语句:pstmt.executeUpdate()

  • 执行任意语句:pstmt.execute()

注意:预编译对象执行SQL的所有方法,都是无参方法

3.预编译对象的原理和好处

好处:

  • 能够解决SQL注入漏洞

  • 效率高:SQL编译一次,执行多次

  • 可读性高:SQL语句结构清晰易读

连接池:

什么是连接池:

连接池:存储了一批连接对象的容器。如果需要操作数据库,不用再创建Connection对象,而是从连接池里取出一个使用;使用完成之后,把连接对象归还到连接池。

好处:

性能高:拿出一个使用,比创建一个速度快

连接对象循环使用,连接数就少了:

不会导致数据库所有可用连接,都被占用

不会Connection对象过多,导致内存溢出


自定义连接池

数据库连接池相关的API

java为数据库连接池提供了公共的接口:javax.sql.DateSource,各个厂商需要自己的连接池实现这个接口,这样应用程序可以方便的切换不同厂商的连接池.

自定义的步骤:

1. 定义一个类实现 javax.sql.DataSource 接口

2. 实现接DataSource口中的抽象方法

3. 定义连接池相关参数

4. 创建容器保存连接

5. 提供获取连接方法

6. 提供关闭连接方法

连接池的使用:

1.1定义一个类实现接口

package com.luliang.lianjieci;

import com.luliang.JDBCbao.JdbcUtils1;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.*;
import java.util.LinkedList;
import java.util.logging.Logger;

public class MyPool implements DataSource {
    private int initCount = 5;//初始化的个数
    private int maxCount = 10;//最大的连接个数
    private int curCount = 0;//当前已经创建的连接池个数
    //储存连接的容器
    private LinkedList<Connection> list = new LinkedList<>();

    //构造方法,初始化连接池的,连接池一旦创建那么连接池中就应该要有指定的个数连接
    public MyPool() throws Exception {
        for (int i = 0; i < initCount; i++) {
            Connection connection = createConnection();
            //把连接存储到容器中
            list.add(connection);
        }
    }

    //创建容器保存连接
    //创建Connection的方法
    public Connection createConnection() throws Exception {
        //注册驱动,获取连接
        Connection connection = JdbcUtils1.getConnection();
        curCount++;
        return connection;
    }

    //提供获取连接的连接方式,提供关闭连接的方法
    //陪人问你连接池要连接
    @Override
    public Connection getConnection() throws SQLException {
        //情况1:先判断是否连接
        if (list.size() > 0) {
            //如果连接池有连接,那么直接取出连接,返回即可
            Connection connection = list.removeFirst();
            return connection;
        }
        //情况2:连接池中没有连接,然后先判断目前的连接个数是否已经草果了最大的连接的个数
        if (curCount < maxCount) {
            //创建连接
            try {
                Connection connection = createConnection();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            //没有连接,并将已经草果了最大的连接个数
            throw new RuntimeException("已经达到连接的连接的个数.请等候.");
        }
        return null;
    }

    //回收Connection
    public void close(Connection connection) {
        list.add(connection);
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public ConnectionBuilder createConnectionBuilder() throws SQLException {
        return null;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }

    @Override
    public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }


    public static void main(String[] args) throws Exception {
        MyPool myPool = new MyPool();
        System.out.println("连接池1:"+myPool.getConnection());
        Connection connection=myPool.getConnection();
        System.out.println("连接10:"+connection);
    }
}

运行结果:

常见的连接池:

C3po-jar包和配置文件

第一步:

配置文件参数

使用步骤:

  1. 导入jar包:c3p0-0.9.2-pre5.jarmchange-commons-java-0.2.3.jar

  2. 提供配置文件

  3. 编写代码,使用连接池

配置文件

  • 配置文件名称必须是:c3p0-config.xml

  • 配置文件的位置必须在:类加载路径下(src下)

c3p0-config>
    <default-config>
        <!-- 前边4个是必须的配置项 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///heima63</property>
        <property name="user">root</property>
        <property name="password">root</property>
        
        <!-- 后边是可以不配置的配置项 -->
        <!--最大等待时间-->
        <property name="checkoutTimeout">30000</property>
        <!--连接池初始化容量-->
        <property name="initialPoolSize">10</property>
        <!--最大空闲回收时间-->
        <property name="maxIdleTime">30</property>
        <!--连接池最大容量-->
        <property name="maxPoolSize">100</property>
        <!--连接池最小容量-->
        <property name="minPoolSize">10</property>
    </default-config>

    <!-- This app is massive! -->
    <named-config name="itcast">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///heima</property>
        <property name="user">root</property>
        <property name="password">root</property>
    </named-config>
</c3p0-config>

常见的配置文件解释

运行代码:

package com.luliang.lianjieci;

import com.luliang.JDBCbao.JdbcUtils2;
import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class Demo03 {
    public static void main(String[] args) throws Exception {
        //查询
        //获取连接
        ComboPooledDataSource dataSource = new ComboPooledDataSource("c3p0-config.xml");
        //从连接池中获取连接
        Connection connection = dataSource.getConnection();
        //使用连接操作数据
        //从创建SQL执行平台
        PreparedStatement preparedStatement = connection.prepareStatement("SELECT*FROM product1 WHERE price>?");
        //添加数据
        preparedStatement.setDouble(1,500.0);
        //执行SQL语句
        ResultSet resultSet = preparedStatement.executeQuery();
        //处理结果
        while (resultSet.next()){
            String pname = resultSet.getString("pname");
            double price = resultSet.getDouble("price");
            System.out.println("姓名:"+pname+", 工资:"+price);
        }
        //释放资源
        resultSet.close();
        preparedStatement.close();
        connection.close();//从连接池中获取对象
    }
}

druid连接池的使用

使用步骤:
1.导入jar包

2.提供配置文件

常用的配置文件

API介绍:

com.alibaba.druid.pool.DruidDataSourceFactory 类有创建连接池的方法

public static DataSource createDataSource(Properties properties)

创建一个连接池 ,连接池的参数使用properties中的数据

我们可以看到DRUID连接池在创建的时候需要一个Properties对象来设置参数,所以我们使用properties文件来保 存对应的参数。 DRUID连接池的配置文件名称随便,建议放到src目录下面方便加载。 druid.properties 文件内 容:

#数据库连接地址
url=jdbc:mysql:///lu
#数据库驱动类名
driverClassName=com.mysql.jdbc.Driver
#数据库用户名
username=root
#数据库密码
password=root
#连接池初始化容量
initialSize=20
#连接池最大容量
maxActive=50
#连接池最小容量
minIdle=10
#最大等待时间
maxWait=100000

编写代码:

连接池的使用:

所有连接池的使用,编写代码的步骤:

  1. 创建一个连接池对象:类名不同

  2. 从连接池里获取连接:方法一定是getConnection()

  3. ....使用连接对象操作数据库...

  4. 把连接归还到连接池:方法一定是close()

编写代码,使用连接池

package com.luliang.lianjieci;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

public class Demo04 {
    //druid连接池的使用查询语句
    public static void main(String[] args) throws Exception {
        //读取配置文件得到Properties对象
        Properties properties = new Properties();
        InputStream inputStream = Demo04.class.getClassLoader().getResourceAsStream("luliang.properties");
        //一个方法获取一个流
        properties.load(inputStream);
        //1.创建连接池的获取连接
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //2.从连接池中获取连接
        Connection connection = dataSource.getConnection();
        //3.操作数据库
        //3.1创建SQL执行平台
        //添加代码
        String sql = "SELECT*FROM liang";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //执行SQL语句
        ResultSet resultSet = preparedStatement.executeQuery();
        //4.处理结果
        while (resultSet.next()) {
            String name = resultSet.getString("name");
            String residese = resultSet.getString("residese");
            double salary = resultSet.getDouble("salary");
            System.out.println("姓名:" + name + ", 住宅:" + residese + ", 工资:" + salary);
        }
        //关闭资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值