dbcp连接池配置详解_JDBC第四篇【数据库连接池、DbUtils框架、分页】(修订版)
前言
只有光头才能变强。
文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y
1.数据库连接池
什么是数据库连接池
简单来说:数据库连接池就是提供连接的。。。
为什么我们要使用数据库连接池
数据库的连接的建立和关闭是非常消耗资源的
频繁地打开、关闭连接造成系统性能低下
编写连接池
编写连接池需实现java.sql.DataSource接口
创建批量的Connection用LinkedList保存【既然是个池,当然用集合保存、、LinkedList底层是链表,对增删性能较好】
实现getConnetion(),让getConnection()每次调用,都是在LinkedList中取一个Connection返回给用户
调用Connection.close()方法,Connction返回给LinkedList
private static LinkedList list = new LinkedList<>();//获取连接只需要一次就够了,所以用static代码块static {//读取文件配置 InputStream inputStream = Demo1.class.getClassLoader().getResourceAsStream("db.properties"); Properties properties = new Properties();try { properties.load(inputStream); String url = properties.getProperty("url"); String username = properties.getProperty("username"); String driver = properties.getProperty("driver"); String password = properties.getProperty("password");//加载驱动 Class.forName(driver);//获取多个连接,保存在LinkedList集合中for (int i = 0; i 10; i++) { Connection connection = DriverManager.getConnection(url, username, password); list.add(connection); } } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }}//重写Connection方法,用户获取连接应该从LinkedList中给他@Overridepublic Connection getConnection() throws SQLException { System.out.println(list.size()); System.out.println(list);//先判断LinkedList是否存在连接return list.size() > 0 ? list.removeFirst() : null; }
我们已经完成前三步了,现在问题来了。我们调用Conncetion.close()方法,是把数据库的物理连接关掉,而不是返回给LinkedList的
解决思路:
写一个Connection子类,覆盖close()方法
写一个Connection包装类,增强close()方法
用动态代理,返回一个代理对象出去,拦截close()方法的调用,对close()增强
分析第一个思路:
Connection是通过数据库驱动加载的,保存了数据的信息。写一个子类Connection,new出对象,子类的Connction无法直接继承父类的数据信息,也就是说子类的Connection是无法连接数据库的,更别谈覆盖close()方法了。
分析第二个思路:
写一个Connection包装类。
写一个类,实现与被增强对象的相同接口【Connection接口】
定义一个变量,指向被增强的对象
定义构造方法,接收被增强对象
覆盖想增强的方法
对于不想增强的方法,直接调用被增强对象的方法
这个思路本身是没什么毛病的,就是实现接口时,方法太多了!,所以我们也不使用此方法
分析第三个思路代码实现:
@Overridepublic Connection getConnection() throws SQLException {
if (list.size() > 0) { final Connection connection = list.removeFirst();
//看看池的大小 System.out.println(list.size());
//返回一个动态代理对象 return (Connection) Proxy.newProxyInstance(Demo1.class.getClassLoader(), connection.getClass().getInterfaces(), new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//如果不是调用close方法,就按照正常的来调用 if (!method.getName().equals("close")) { method.invoke(connection, args); } else {
//进到这里来,说明调用的是close方法 list.add(connection);
//再看看池的大小 System.out.println(list.size());
} return null; }
}); } return null;}
我们上面已经能够简单编写一个线程池了。下面我们来使用一下开源数据库连接池
DBCP
使用DBCP数据源的步骤:
导入两个jar包【Commons-dbcp.jar和Commons-pool.jar】
读取配置文件
获取BasicDataSourceFactory对象
创建DataSource对象
private static DataSource dataSource = null;
static { try { //读取配置文件 InputStream inputStream = Demo3.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"); Properties properties = new Properties(); properties.load(inputStream);
//获取工厂对象 BasicDataSourceFactory basicDataSourceFactory = new BasicDataSourceFactory(); dataSource = basicDataSourceFactory.createDataSource(properties);
} catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); }}
public static Connection getConnection() throws SQLException { return dataSource.getConnection();
}
//这里释放资源不是把数据库的物理连接释放了,是把连接归还给连接池【连接池的Connection内部自己做好了】public static void release(Connection conn, Statement st, ResultSet rs) {
if (rs != null) { try { rs.close(); } catch (Exception e) { e.printStackTrace(); } rs = null; } if (st != null) { try { st.close(); } catch (Exception e) { e.printStackTrace(); }
} if (conn != null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); }
}}
C3P0
C3P0数据源的性能更胜一筹,并且它可以使用XML配置文件配置信息!
步骤:
导入开发包【c3p0-0.9.2-pre1.jar】和【mchange-commons-0.2.jar】
导入XML配置文件【可以在程序中自己一个一个配,C3P0的doc中的Configuration有XML文件的事例】
new出ComboPooledDataSource对象
private static ComboPooledDataSource comboPooledDataSource = null;
static { //如果我什么都不指定,就是使用XML默认的配置,这里我指定的是oracle的 comboPooledDataSource = new ComboPooledDataSource("oracle");}
public static Connection getConnection() throws SQLException { return comboPooledDataSource.getConnection();}
Tomcat数据源
Tomcat服务器也给我们提供了连接池,内部其实就是DBCP
步骤:
在META-INF目录下配置context.xml文件【文件内容可以在tomcat默认页面的 JNDI Resources下Configure Tomcat's Resource Factory找到】
导入Mysql或oracle开发包到tomcat的lib目录下
初始化JNDI->获取JNDI容器->检索以XXX为名字在JNDI容器存放的连接池
context.xml文件的配置:
"jdbc/EmployeeDB" auth="Container" type="javax.sql.DataSource" username="root" password="root" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/zhongfucheng" maxActive="8" maxIdle="4"/>
try {
//初始化JNDI容器 Context initCtx = new InitialContext();
//获取到JNDI容器 Context envCtx = (Context) initCtx.lookup("java:comp/env");
//扫描以jdbc/EmployeeDB名字绑定在JNDI容器下的连接池 DataSource ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB");
Connection conn = ds.getConnection(); System.out.println(conn);
}
使用dbutils框架
dbutils它是对JDBC的简单封装,极大简化jdbc编码的工作量
DbUtils类
提供了关闭连接,装载JDBC驱动,回滚提交事务等方法的工具类【比较少使用,因为我们学了连接池,就应该使用连接池连接数据库】
QueryRunner类
该类简化了SQL查询,配合ResultSetHandler使用,可以完成大部分的数据库操作,重载了许多的查询,更新,批处理方法。大大减少了代码量
ResultSetHandler接口
该接口规范了对ResultSet的操作,要对结果集进行什么操作,传入ResultSetHandler接口的实现类即可。
ArrayHandler:把结果集中的第一行数据转成对象数组。
ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
ColumnListHandler:将结果集中某一列的数据存放到List中。
KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
ScalarHandler 将ResultSet的一个列到一个对象中。
使用DbUtils框架对数据库的CRUD
/** 使用DbUtils框架对数据库的CRUD* 批处理** */public class Test {
@org.junit.Testpublic void add() throws SQLException {
//创建出QueryRunner对象 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "INSERT INTO student (id,name) VALUES(?,?)";
//我们发现query()方法有的需要传入Connection对象,有的不需要传入 //区别:你传入Connection对象是需要你来销毁该Connection,你不传入,由程序帮你把Connection放回到连接池中 queryRunner.update(sql, new Object[]{"100", "zhongfucheng"});
}
@org.junit.Testpublic void query()throws SQLException {
//创建出QueryRunner对象 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "SELECT * FROM student";
List list = (List) queryRunner.query(sql, new BeanListHandler(Student.class)); System.out.println(list.size());
}
@org.junit.Testpublic void delete() throws SQLException { //创建出QueryRunner对象 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "DELETE FROM student WHERE id='100'";
queryRunner.update(sql); }
@org.junit.Testpublic void update() throws SQLException { //创建出QueryRunner对象 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "UPDATE student SET name=? WHERE id=?";
queryRunner.update(sql, new Object[]{"zhongfuchengaaa", 1}); }
@org.junit.Testpublic void batch() throws SQLException { //创建出QueryRunner对象 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "INSERT INTO student (name,id) VALUES(?,?)";
Object[][] objects = new Object[10][]; for (int i = 0; i 10; i++) { objects[i] = new Object[]{"aaa", i + 300}; } queryRunner.batch(sql, objects); }
}
分页
分页技术是非常常见的,在搜索引擎下搜索页面,不可能把全部数据都显示在一个页面里边。所以我们用到了分页技术。
Oracle实现分页
/* Oracle分页语法: @lineSize---每页显示数据行数 @currentPage----当前所在页*/SELECT *FROM ( SELECT 列名,列名,ROWNUM rn FROM 表名 WHERE ROWNUM<=(currentPage*lineSize)) temp
WHERE temp.rn>(currentPage-1)*lineSize;
Oracle分页原理简单解释:
/* Oracle分页: Oracle的分页依赖于ROWNUM这个伪列,ROWNUM主要作用就是产生行号。 分页原理: 1:子查询查出前n行数据,ROWNUM产生前N行的行号 2:使用子查询产生ROWNUM的行号,通过外部的筛选出想要的数据 例子: 我现在规定每页显示5行数据【lineSize=5】,我要查询第2页的数据【currentPage=2】 注:【对照着语法来看】 实现: 1:子查询查出前10条数据【ROWNUM<=10】 2:外部筛选出后面5条数据【ROWNUM>5】 3:这样我们就取到了后面5条的数据*/
Mysql实现分页
/* Mysql分页语法: @start---偏移量,不设置就是从0开始【也就是(currentPage-1)*lineSize】 @length---长度,取多少行数据*/SELECT *FROM 表名LIMIT [START], length;
/* 例子: 我现在规定每页显示5行数据,我要查询第2页的数据 分析: 1:第2页的数据其实就是从第6条数据开始,取5条 实现: 1:start为5【偏移量从0开始】 2:length为5*/
总结:
Mysql从(currentPage-1)*lineSize开始取数据,取lineSize条数据
Oracle先获取currentPagelineSize条数据,从(currentPage-1)lineSize开始取数据
使用JDBC连接数据库实现分页
下面是常见的分页图片
配合图片,看下我们的需求是什么:
算出有多少页的数据,显示在页面上
根据页码,从数据库显示相对应的数据。
分析:
算出有多少页数据这是非常简单的【在数据库中查询有多少条记录,你每页显示多少条记录,就可以算出有多少页数据了】
使用Mysql或Oracle的分页语法即可
通过上面分析,我们会发现需要用到4个变量
currentPage--当前页【由用户决定的】
totalRecord--总数据数【查询表可知】
lineSize--每页显示数据的数量【由我们开发人员决定】
pageCount--页数【totalRecord和lineSize决定】
//每页显示3条数据 int lineSize = 3;
//总记录数 int totalRecord = getTotalRecord();
//假设用户指定的是第2页 int currentPage = 2;
//一共有多少页 int pageCount = getPageCount(totalRecord, lineSize);
//使用什么数据库进行分页,记得要在JdbcUtils中改配置 List list = getPageData2(currentPage, lineSize);for (Person person : list) { System.out.println(person); }//使用JDBC连接Mysql数据库实现分页public static List getPageData(int currentPage, int lineSize) throws SQLException {//从哪个位置开始取数据int start = (currentPage - 1) * lineSize; QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "SELECT name,address FROM person LIMIT ?,?"; List persons = (List) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{start, lineSize});return persons;}//使用JDBC连接Oracle数据库实现分页public static List getPageData2(int currentPage, int lineSize) throws SQLException {//从哪个位置开始取数据int start = (currentPage - 1) * lineSize;//读取前N条数据int end = currentPage * lineSize; QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "SELECT " +" name, " +" address " +"FROM ( " +" SELECT " +" name, " +" address , " +" ROWNUM rn " +" FROM person " +" WHERE ROWNUM <= ? " +")temp WHERE temp.rn>?"; List persons = (List) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{end, start});return persons;}public static int getPageCount(int totalRecord, int lineSize) {//简单算法//return (totalRecord - 1) / lineSize + 1;//此算法比较好理解,把数据代代进去就知道了。return totalRecord % lineSize == 0 ? (totalRecord / lineSize) : (totalRecord / lineSize) + 1;}public static int getTotalRecord() throws SQLException {//使用DbUtils框架查询数据库表中有多少条数据 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "SELECT COUNT(*) FROM person"; Object o = queryRunner.query(sql, new ScalarHandler()); String ss = o.toString();int s = Integer.parseInt(ss);return s;}
最后
乐于输出干货的Java技术公众号:Java3y。公众号内有200多篇原创技术文章、海量视频资源、精美脑图,不妨来关注一下!
有帮助?好看!转发!
dbcp连接池配置详解_JDBC第四篇【数据库连接池、DbUtils框架、分页】(修订版)相关推荐
- Spring Boot 使用 HikariCP 连接池配置详解
Spring Boot 使用 HikariCP 连接池配置详解 HikariCP 是一个高性能的 JDBC 连接池组件. Spring Boot 2.x 将其作为默认的连接池组件,项目中添加 spri ...
- Hikari连接池配置详解
Hikari连接池配置详解 ## 数据库配置 spring.datasource.type=com.zaxxer.hikari.HikariDataSource spring.datasource.d ...
- oracle通过dblink连接mysql配置详解(全Windows下)
oracle通过dblink连接mysql配置详解(全Windows下) 关于oracle通过dblink连接mysql,经过了两周的空闲时间研究学习,终于配置好了,真是不容易啊,仔细想想的话,其实也 ...
- dbcp连接池配置详解_重学MySQL:事务与连接池,一文详解带你搞懂
拼多多三面惨败,java中间件.数据库与spring框架,答不上... 吊打MySQL:21性能优化实践+学习导图+55面试+笔记+20高频知识点 Mysql事务 事务: 事务指逻辑上的一组操作,组成 ...
- Druid连接池配置详解
什么是Druid Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池.插件框架和SQL解析器组成.该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求,比如向密钥服务请 ...
- Redis连接池配置详解
连接池配置 文章目录 连接池配置 一.Redis连接池 二.jar包准备 三.编写代码配置,创建连接池,并调用处连接 总结 一.Redis连接池 与JDBC中在与数据库进行连接时耗时,从而需要引入连接 ...
- NETGEAR拒绝连接请求_详解 Tomcat 的连接数与线程池
点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 关注公众号后台回复pay或mall获取实战项目资料视频 点击此链接:多套SpringCloud/SpringBoot实 ...
- HAproxy指南之haproxy配置详解2(理论篇)
上一小节的从haproxy的配置文件我们知道haproxy相关参数基本介绍,但是在实际生产环境中,往往需要根据相关规则做请求匹配跳转,这时就需要用到Frontend:Backend这两个配置段,再结合 ...
- DBCP连接池配置详解
Spring DBCP配置 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource ...
最新文章
- FFmpeg的添加logo,去logo
- SD question:客户主数据中“合伙人功能”视图里面的数据怎么样才能自动带出来?...
- KMeans和KMedoid 的Matlab实现
- mysql5.7.12 my.ini文件_MySQL5.7缺少my.ini文件的解决方法
- (6)Zynq AXI_HP接口介绍
- 微软再次“封杀” Flash Player!
- 建造者模式(Java与Kotlin版)
- Name Disambiguaiton in Aminer论文解读
- 2020网络安全NISP一级题库
- 【山科OJ】Problem C: Matrix Problem (III) : Array Practice
- 第五周:Raptor:三色球问题
- pat乙级【数列的片段和 (20分)】测试样例修改(第二个测试点)
- 我爱淘冲刺阶段站立会议每天任务3
- Anima Toon:体素角色动画软件
- C语言里文字颜色色和背景颜色设置
- 互斥事件的概念和公式_高三数学辨析专题---概率模型中互斥、对立、独立事件...
- (译)网站加速最佳实践——雅虎35条
- 谷歌地图接口Google Maps APIs中地图样式设计配置调整与JSON或URL导出
- MSXML一般使用方法
- AD18设置菜单为中文
热门文章
- fitbit手表中文说明书_我如何分析FitBit中的数据以改善整体健康状况
- python函数应用(1)
- 安装低版本的R语言、和自行下载安装各个版本的R语言包、以及多环境运行R
- 您能解决这3个(看似)简单的Python问题吗?
- 《黑客帝国》中的代码雨让人身临其境!利用Python轻松实现!
- Vue——基础(对象、属性样式操作、条件、循环、事件、绑定)
- Linux不仅仅是开源
- Python的小整数对象池
- ExtJs2.0学习系列(12)--Ext.TreePanel之第一式
- CIF进口货物流程图_广州进口报关公司阿根廷红酒上海进口清关成本选择聚海