连接池

一、预编译对象

0. SQL注入漏洞

  • SQL注入:通过输入一些特殊的参数值,在拼接SQL时,导致SQL语句的结构发生变化,绕过了SQL的条件
  • 解决方案:使用预编译对象preparedStatement代替Statement

1. 什么是预编译对象

  • 预编译对象:PreparedStatementStatement的子接口,是另外一种SQL执行平台,用来执行SQL语句的

2. ==怎样使用预编译对象==

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

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

//5.处理结果
//6.释放资源

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

  • 减少编译次数,提高效率
  • 解决SQL注入漏洞
  • 提高程序可读性(SQL语句的可读性)

二、连接池

1. 什么是连接池

  • 连接池:存储了一堆连接对象的容器。当我们要使用Connection时,不需要创建,而是从连接池中取出一个使用;当使用完成之后,要把Connection对象归还到连接池里。
  • 好处:
  • 效率高
  • 连接池里的连接可以循环使用
  • 少量连接,就可以支持大量的数据库操作
  • 只要少量连接,避免了数据库可用连接被占用完
  • 只要少量连接,避免了创建的Connection过多造成的内存溢出

2. 自定义连接池

3. 装饰者模式

3.1 可以对某一对象的方法增强的方式
  • 继承重写父类方法
  • 动态代理:调用代理对象,让代理对象调用目标对象;代理对象里进行增强(依赖于反射技术)
  • 装饰者模式:包装类
3.2 装饰者模式
  • 之前使用过的:new BufferedReader(new FileReader()):BufferedReader,对FileReader对象进行了功能增强
  • 原理:见图
  • 示例:有一个接口Man,有实现类普通人CommonMan,要进行功能增强,创建包装类:IronMan
public interface Man {
    void eat();
    void fight();
}
public class CommonMan implements Man {
    @Override
    public void eat() {
        System.out.println("吃2碗");
   }

    @Override
    public void fight() {
        System.out.println("战斗力:5");
   }
}
public class IronMan implements Man {
    private Man commanMan;

    public IronMan(Man commanMan) {
        this.commanMan = commanMan;
   }

    @Override
    public void eat() {
        this.commanMan.eat();
   }

    @Override
    public void fight() {
        System.out.println("战斗力:99999");
   }
}
public class WrapperTest {

    public static void main(String[] args) {
        //先有一个被包装的目标对象
        Man man = new CommonMan();

        //不增强,直接调用目标对象
        man.eat();
        man.fight();

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

        //创建一个包装类对象,进行功能增强
        Man ironMan = new IronMan(man);
        ironMan.eat();
        ironMan.fight();
   }
}

4. ==常见连接池的使用==

4.1 介绍
  • 在实际开发中,不需要自己写连接池。市面上有一些成熟的连接池工具,功能更强大
  • 所有的连接池,都实现了JDBC规范的一个接口:javax.sql.DataSource。所有连接池使用的基本步骤:
  • 创建连接池对象:连接池的类名不同
  • 从连接池中获取连接:getConnection()
  • 使用连接操作数据库
  • 操作完成归还到连接池:close()
  • 常见的连接池有:
  • dbcp:Apache提供的开源、免费的连接池工具
  • c3p0:是一个开源的、免费的连接池工具,使用的很多,方便,功能比dbcp要强
  • druid:Alibaba的开源的、免费的、高效的连接池工具。
4.2 c3p0连接池的使用
4.2.1 c3p0使用步骤
  1. 导入jar包
  2. 提供配置文件
  3. 编写代码,使用连接池
4.2.2 c3p0的配置文件
  • 名称必须是:c3p0-config.xml
  • 位置必须在:src下(类加载路径下)
  • 配置示例
<c3p0-config>
    <default-config>
        <!--前边四项是必须配置的-->
        <!--驱动类名-->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <!--数据库url地址-->
        <property name="jdbcUrl">jdbc:mysql:///heima62</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="heima">
        <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>
4.2.3 编写代码,使用c3p0连接池
public class DemoC3p0 {

    /**
     * 无参构造,加载的是配置文件中的默认配置:default-config
     */
    @Test
    public void test1() throws SQLException {
        //1. 创建连接池对象
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        //2.从连接池中获取连接
        Connection connection = dataSource.getConnection();
        //3.创建SQL执行平台
        PreparedStatement preparedStatement = connection.prepareStatement("select * from emp");
        //4.执行SQL语句
        ResultSet resultSet = preparedStatement.executeQuery();
        //5.处理结果
        while (resultSet.next()) {
            String ename = resultSet.getString("ename");
            double salary = resultSet.getDouble("salary");
            System.out.println("ename:" + ename + ", salary:" + salary);
       }
        //6.释放资源
        resultSet.close();
        preparedStatement.close();
        connection.close();//归还到连接池

   }

    /**
     * 加载配置文件中,heima的配置
     */
    @Test
    public void test2() throws SQLException {
        //1.创建连接池对象
        ComboPooledDataSource dataSource = new ComboPooledDataSource("heima");
        //2.获取连接
        Connection connection = dataSource.getConnection();
        //3.创建SQL执行平台
        PreparedStatement preparedStatement = connection.prepareStatement("select * from account");
        //4.执行SQL语句
        ResultSet resultSet = preparedStatement.executeQuery();
        //5.处理结果
        while (resultSet.next()) {
            String name = resultSet.getString("name");
            double money = resultSet.getDouble("money");
            System.out.println("name:" + name + ", money: " + money);
       }
        //6.释放资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
   }
}
4.3 druid连接池的使用
4.3.1 使用步骤
  1. 导入jar包:
  2. 提供配置文件
  3. 编写代码,使用连接池
4.3.2 提供配置文件
  • 文件名:xxx.properties
  • 文件位置:建议放在src下(类加载路径下)
  • 示例
#数据库的url地址
url=jdbc:mysql:///heima62
#数据库的用户名
username=root
#数据库密码
password=root
#数据库的驱动类名
driverClassName=com.mysql.jdbc.Driver
#连接池初始化容量
initialSize=30
#连接池最大容量
maxActive=50
#连接池最小容量
minIdle=10
4.3.3 编写代码,使用druid连接池
public class DemoDruid {
    /**
     * 读取配置文件,使用druid连接池
     */
    @Test
    public void test() throws Exception {
        //0.自己读取配置文件,得到properties对象
        Properties properties = new Properties();
        InputStream inputStream = DemoDruid.class.getClassLoader().getResourceAsStream("druid.properties");
        properties.load(inputStream);

        //1.得到一个连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //2.得到一个连接对象
        Connection connection = dataSource.getConnection();
        //3.创建SQL执行平台
        PreparedStatement preparedStatement = connection.prepareStatement("select * from emp");
        //4.执行SQL语句
        ResultSet resultSet = preparedStatement.executeQuery();
        //5.处理结果
        while (resultSet.next()) {
            String ename = resultSet.getString("ename");
            double salary = resultSet.getDouble("salary");
            System.out.println("ename:" + ename + ", salary:" + salary);
       }
        //6.释放资源
        resultSet.close();
        preparedStatement.close();
        connection.close();//已经增强了:归还到连接池
        
        inputStream.close();
   }
}

5. 封装工具类

  • 连接池对象,一个项目中只要一个就可以了,通常设置成某个类的静态变量
  • 对JdbcUtils进行改造
  • 原本的JdbcUtils:
  • 静态代码块里:读取配置文件,得到配置信息
  • 提供getConnection方法:使用配置信息,创建连接
  • 提供close方法:释放资源
  • 改造之后的JdbcUtils:
  • 提供一个静态变量:连接池对象
  • 提供getConnection方法:从连接池里获取连接
  • 提供close方法:释放资源

内容总结

  1. 掌握预编译对象的使用
  1. 改造SQL语句:把参数写成占位符?
  2. 编译SQL,得到预编译对象:PreparedStatement pstmt = connection.prepareStatement(sql)
  3. 设置SQL的参数值:pstmt.setXXX(参数序号, 参数值)
  4. 执行SQL语句:pstmt.executeQuery()pstmt.executeUpdate()
  1. 预编译对象的好处:
  1. 解决了SQL注入漏洞
  2. 性能高:同一语句,只要编译一次,可以执行多次
  3. 提高可读性:SQL语句易读
  1. 理解装饰者模式:进行方法的功能增强
  2. 理解连接池的原理:
  3. 理解连接池的好处:
  1. 效率高
  2. 连接循环使用:
  1. 少量连接,可以支持大量的数据库操作;不用创建大量的Connection对象,避免内存溢出
  2. 创建的连接少了,避免数据库所有可用连接,都被占用,导致没有连接可用
  1. 掌握c3p0/druid连接池的使用
  1. 导入jar包
  2. 提供配置文件
  1. 无论什么连接池,都有一些必须的配置项:
  1. 数据库连接地址
  2. 数据库的用户名
  3. 数据库的密码
  4. 数据库的驱动类名(建议提供)
  1. c3p0的配置文件:c3p0-config.xml,放在src下(类加载路径下)
  2. druid的配置文件:xxx.properties,建议放在src下
  1. 编写代码,使用连接池
  1. 创建一个连接池对象
  1. c3p0连接池对象的创建:new ComboPooledDataSource()
  2. druid连接池对象的创建:DruidDataSourceFactory.createDataSource(properties对象)
  1. 从连接池中获取一个连接:getConnection()
  2. 使用连接操作数据库
  3. 归还到连接池:connection.close()