暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

JDBC—连接池

dunk的日志 2020-09-17
120

JDBC—连接池

连接池的概念和作用

概念:概念:连接池的本质就是一个容器,该容器中会初始化一些Connection对象,我们程序只需要从连接池中获取连接,使用完毕之后归还连接即可。

作用:大大减少了频繁的创建和释放连接的时间,提高数据库操作的效率

连接池对程序带来的差异对比:

未使用连接池时,每来一个对数据库的访问就会创建一个连接,使用完了就关闭连接,这样导致当有大量的数据访问时,会频繁地创建和关闭连接,非常耗时。而在使用连接池之后,会提前准备一些数据连接,使用的时候就从池中获取,用完后重新归还给池中,可反复获取调用。

c3p0连接池的使用

使用步骤:

【前提】导入c3p0的依赖jar包(c3p0-0.9.5.2.jar)

【第1步】将c3p0-config.xml配置文件复制到src目录下(位置和名称都固定的)

【第2步】创建c3p0连接池对象(ComboPooledDataSource)

【第3步】通过连接池对象获得连接对象

【第4步】同JDBC操作数据库执行步骤,获取SQL执行对象、执行SQL语句。。。。

注意:

如果连接对象是从连接池中获取的,那么连接的close方法就不是关闭而是归还。

c3p0-config.xml的配置文件的名称和位置是固定的,配置文件放在src目录中,配置文件中的属性名也是固定的,否则无法根据属性名或者对应的属性值。

将properties文件作为配置文件【补充】

要求:属性文件的名称必须是c3p0.properties,必须放在src路径下,文件中的key是固定的必须以c3p0.开头。

druid连接池的使用【重点】🚩

使用步骤:

【前提】导入的druid依赖jar包(druid-1.0.9.jar)

【第1步】在src目录下创建配置文件,名称可以任意,一般叫做druid.properties

【第2步】使用Properties对象加载配置文件

【第3步】使用DruidDataSourceFactory.createDataSource()工厂创建连接池对象

【第4步】通过连接池对象获得连接对象

【第5步】同JDBC操作数据库执行步骤,获取SQL执行对象、执行SQL语句。。。。

自定义配置文件的内容

  # 基础连接参数,名称是固定
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db14
username=root
password=root
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 等待超时时间
maxWait=3000
复制

代码实现

方式1:直接创建DruidDataSource连接池对象,硬编码,暂时不推荐使用

    //创建连接池对象
 DruidDataSource dataSource=new DruidDataSource();
 //设置参数
 dataSource.setDriverClassName("com.mysql.jdbc.Driver");
 dataSource.setUrl("jdbc:mysql://localhost:3306/db14");
 dataSource.setUsername("root");
 dataSource.setPassword("root");
复制

方式2:使用工厂类集合配置文件创建连接池,推荐使用

将连接池(druid)抽取到工具类中【重点】🚩

需要做的事:        1 加载properties属性文件        2 创建连接池对象        3 对外提供获取连接池的方法        4 对外提供获取连接的方法        5 对外提供释放资源的方法

自定义连接池

要求:所有的连接池都必须直接或者间接实现DataSource接口,该接口由sun公司制定,相当于连接池的规范,用于规范连接池必须提供哪些方法。

连接池本质:内部会有一个数组或者集合容器,用于存储Connection对象。

为了实现连接对象的归还而不是关闭,引入了以下四种设计模式,其中继承者模式无法实现归还连接对象的需求,另外三种设计模式如下:

装饰者设计模式:

解决的问题:在不改变原有类方法源代码的情况下给方法进行增强,在实现了中调用原有对象的对应方法,也可对原有方法进行增强。

要求:要实现接口的所有方法。

弊端:要重写的方法太多了,写起来麻烦。

理解:创建一个JDBC4connection的兄弟类,除了要进行重写增强的方法外,其他所有的方法同JDBC4connection一样去实现Connection接口里定义的方法

适配器模式:

解决的问题:解决重写的方法太多的问题,如果没有这样的模版了就需要自己写。

要求:需要提前定义一个类(适配器类/模版类)实现接口,重写所有方法,在重写的方法中调用原有对象的对应方法,不做增强。然后我么的类只需要继承该适配器类,重写需要增强的方法即可.

理解:在Connetion接口和要完成增强方法的类之间,创建一个实现类模板,去实现接口中所有的方法,那样在需要在实现增强方法的类里面就不需要实现所有的方法了。

步骤:

【第一步】创建适配器模板类

  public abstract class AbstractConnectionAdapter implements Connection {

   private Connection conn;//保存原有对象

   public AbstractConnectionAdapter(Connection conn) {
       this.conn = conn;
  }

   @Override
   public void close() throws SQLException {
       conn.close();
  }
   @Override
   public Statement createStatement() throws SQLException {
       return conn.createStatement();
  }
  //其他方法按照上面的写法自己补充。。。
}
复制

【第二步】自定义一个Connection类继承适配器类,重写需要增强的方法

  public class HeimaConnection extends AbstractConnectionAdapter {

 private List<Connection> sPool;  //连接池中的容器对象
 private Connection conn; //传递进来的conn是JDBC4Connection

 public HeimaConnection(Connection conn, List<Connection> sPool) {
   super(conn);
   this.conn=conn;
   this.sPool=sPool;
}

 //重新close方法
 @Override
 public void close() throws SQLException {
   //归还连接
   sPool.add(conn);
}
}
复制

【第三步】在自定义连接池的获取连接方法中,返回自定义的Connection对象

动态代理模式:

相关概念

解决的问题:在不改变原始对象方法源代码的情况下对原始方法进行增强,可以彻底解决装饰模式以及适配器模式要重写的方法过多问题。

要求:代理对象和目标对象要实现相同的接口

三个角色:目标对象、接口、代理对象(在内存中动态生成的对象)

具体做法:使用Proxy.newProxyInstance(...)方法动态生成并返回一个代理对象

动态代理代码演示

  【第一步】创建目标对象 --> JDBC4Connection
Connection conn = JDBCUtils.getConnection();
【第二步】使用Proxy.newProxyInstance(三个参数)创建代理对象
  ClassLoader loader:和目标对象使用相同类加载,用来动态生成代理对象,一般使用目标对象获取
  Class<?>[] interfaces:目标对象实现的接口们,告诉代理对象要和目标对象实现相同的接口
  InvocationHandler h:是一个接口,用来处理代理对象要做的事
 ClassLoader classLoader = conn.getClass().getClassLoader();
 //Class<?>[] interfaces = conn.getClass().getInterfaces(); //在此次无法获取接口们,因为JDBC4Connection没有直接实现Connection接口
 Class<?>[] interfaces={Connection.class}; //静态初始化

Connection conn_proxy= (Connection) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
   /**
    * 调用代理对象的方法,InvocationHandler的invoke方法就执行了
    * @param proxy 代理对象
    * @param method 调用代理对象的方法对象
    * @param args 调用代理对象方法传递的参数们
    * @return 该返回值最终会返回到调用代理对象的地方
    * @throws Throwable
    */
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     System.out.println("invoke执行了...");
     /*System.out.println(method);
       System.out.println(Arrays.toString(args));*/
     //调用目标对象的方法
     //如果是close方法,我们就需要归还连接
     if("close".equals(method.getName())){
       System.out.println("归还连接...");
       return null;
    }
     //反射调用目标对象的方法
     return method.invoke(conn,args); //等价于之前适配器模式中的conn.prepareStatement(sql)之类的操作
  }
});
 //3 调用代理对象的方法
 //conn_proxy.getCatalog();
 //String value = conn_proxy.getClientInfo("hello");
 //System.out.println(value);
 PreparedStatement pstmt = conn_proxy.prepareStatement("select ...", 2);
 System.out.println("pstmt = " + pstmt);

 conn_proxy.close();
}
复制

自定义连接池的getConnection方法中使用动态代理

  @Override
public Connection getConnection() throws SQLException {
 if(sPool.size()<=0){
   throw new SQLException("连接池空空如也!😣");
}
 Connection conn = sPool.remove(0); //原始JDBC4Connection,没有归还连接的功能
 //创建自定义的连接对象:HeimaConnection_back
 //HeimaConnection connection=new HeimaConnection(conn,sPool);//包装一下

 //换成使用动态代理得到Connection的代理对象
 ClassLoader classLoader = conn.getClass().getClassLoader();
 Class<?>[] interfaces={Connection.class}; //静态初始化
//创建代理对象
 Connection proxyObj= (Connection) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     //如果是close方法就归还连接
     if(method.getName().equals("close")){
       sPool.add(conn);
    }
     //如果不是close方法,就反射调用目的对象的方法
     return method.invoke(conn,args);
  }
});
 //
 return proxyObj; //一定是放回代理对象
}
复制

DbUtils工具类【扩展】🚩

介绍

DbUtils是apache组织提供的一个封装了jdbc操作的工具类,该工具类提供了简单的CRUD操作,只需要两步就可以轻松操作数据库。

常用API

  QueryRunner核心类:负责CRUD操作
BeanListHandler类:用来将查询结果封装成Bean对象,并且将Bean对象存到List集合中返回List<Bean对象>
BeanHandler类:用来将查询结果封装成Bean对象并返回。
复制

使用步骤

  增删改操作:
【第一步】创建QueryRunner核心对象,需要传递连接池
【第二步】调用update(根据具体情况选参数),返回值为int类型的影响的行数
查询操作:
【第一步】创建QueryRunner核心对象,需要传递连接池
【第二步】调用query(根据具体情况选参数),返回值为查询得到的结果集,结果集的类型视传参的类型而定
事务管理操作(批量添加):
在创建QueryRunner的对象,不传递连接池
手动创建连接对象,开启事务
在批量添加语句中每次都传入手动创建的连接对象
添加成功则提交事务
出现异常则回滚事务
复制



文章转载自dunk的日志,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论