JDBC 连接池

连接池

一、预编译对象

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()

  2. 预编译对象的好处:

    1. 解决了SQL注入漏洞

    2. 性能高:同一语句,只要编译一次,可以执行多次

    3. 提高可读性:SQL语句易读

  3. 理解装饰者模式:进行方法的功能增强

  4. 理解连接池的原理:

  5. 理解连接池的好处:

    1. 效率高

    2. 连接循环使用:

      1. 少量连接,可以支持大量的数据库操作;不用创建大量的Connection对象,避免内存溢出

      2. 创建的连接少了,避免数据库所有可用连接,都被占用,导致没有连接可用

  6. 掌握c3p0/druid连接池的使用

    1. 导入jar包

    2. 提供配置文件

      1. 无论什么连接池,都有一些必须的配置项:

        1. 数据库连接地址

        2. 数据库的用户名

        3. 数据库的密码

        4. 数据库的驱动类名(建议提供)

      2. c3p0的配置文件:c3p0-config.xml,放在src下(类加载路径下)

      3. druid的配置文件:xxx.properties,建议放在src下

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

      1. 创建一个连接池对象

        1. c3p0连接池对象的创建:new ComboPooledDataSource()

        2. druid连接池对象的创建:DruidDataSourceFactory.createDataSource(properties对象)

      2. 从连接池中获取一个连接:getConnection()

      3. 使用连接操作数据库

      4. 归还到连接池:connection.close()

      5.  

 

posted @ 2019-05-18 16:50  HankPeng  阅读(203)  评论(0编辑  收藏  举报