JDBC【数据库连接池、DbUtils框架、分页】
1.数据库连接池
什么是数据库连接池
简单来说:数据库连接池就是提供连接的。。。
为什么我们要使用数据库连接池
- 数据库的连接的建立和关闭是非常消耗资源的
- 频繁地打开、关闭连接造成系统性能低下
编写连接池
- 编写连接池需实现java.sql.DataSource接口
- 创建批量的Connection用LinkedList保存【既然是个池,当然用集合保存、、LinkedList底层是链表,对增删性能较好】
- 实现getConnetion(),让getConnection()每次调用,都是在LinkedList中取一个Connection返回给用户
- 调用Connection.close()方法,Connction返回给LinkedList
private static LinkedList<Connection> 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() {@Overridepublic 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文件的配置:
<Context><Resource name="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"/>
</Context>
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 rnFROM 表名WHERE ROWNUM<=(currentPage*lineSize)) tempWHERE 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先获取currentPage*lineSize条数据,从(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<Person> list = getPageData2(currentPage, lineSize);for (Person person : list) {System.out.println(person);}}//使用JDBC连接Mysql数据库实现分页public static List<Person> 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<Person> persons = (List<Person>) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{start, lineSize});return persons;}//使用JDBC连接Oracle数据库实现分页public static List<Person> 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<Person> persons = (List<Person>) 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;}
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章的同学,可以关注微信公众号:Java3y。
JDBC【数据库连接池、DbUtils框架、分页】相关推荐
- Spring JDBC数据库连接池设置
对于任何Java应用程序而言, 在Spring框架中设置JDBC数据库连接池都是很容易的,仅需更改spring配置文件中的一些配置即可.使用Apache Commons DBCP和Commons Po ...
- python 数据库连接池DBUtils
python 数据库连接池DBUtils 0x00 简述 0x01 摘要 0x02 模块 0x03 下载及安装 1.下载 2.安装 0x04 SimplePooledDB 0x05 SteadyDB ...
- JDBC数据库连接池练习题
<JDBC数据库连接池练习题> 文章目录 单选题 多选题 判断题 填空题 单选题 1. 下面选项中,能够将游标从当前位置向下移一行的方法是( ). A.next() B.absolute( ...
- java jdbc init_Java 的JDBC 数据库连接池实现方法
虽然 J2EE 程序员一般都有现成的应用服务器所带的JDBC 数据库连接池,不过对于开发一般的 Java Application . Applet 或者 JSP.velocity 时,我们可用的JDB ...
- Java jdbc数据库连接池
1. 引言 近年来,随着Internet/Intranet建网技术的飞速发展和在世界范围内的迅速普及,计算机 应用程序已从传统的桌面应用转到Web应用.基于B/S(Browser/Server)架构的 ...
- JDBC 数据库连接池(JDBCUtils工具类)
文章目录 前言 一.JDBC作用? 二.获取数据的连接方式 三.Statement/PreStatement执行sql 四.ResultSet结果集 五.JDBC相关API 六.事务 七.批处理 八. ...
- python数据库连接池工具类_Python数据库连接池DBUtils
DBUtils简介 DBUtils是Python的一个用于实现数据库连接池的模块. 此连接池有两种连接模式: 模式一:为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放 ...
- Python数据库连接池DBUtils
DBUtils是Python的一个用于实现数据库连接池的模块 此连接池有两种连接模式: DBUtils提供两种外部接口: PersistentDB :提供线程专用的数据库连接,并自动管理连接. ...
- 【JAVA】JDBC数据库连接池
目录 [JDBC] [JDBC--项目示例] [JDBC-API] [DriverManager]--驱动管理类 [Connection]--数据库连接对象 [Statement]--执行 [Resu ...
- JDBC——数据库连接池
目录 前言 一.为什么要使用数据库连接池 二.数据库连接池 2.1 优点 三.多种开源数据库连接池 3.1 C3P0数据库连接池 3.2 DBCP连接池 3.3 Druid(德鲁伊)数据库连接池 总结 ...
最新文章
- 解析equals(Object obj)和compareTo(T obj)
- hibernate 逆向工程
- Luhn算法验证信用卡的合法性
- cpu线程_记w3wp占用CPU过高解决过程Dictionary线程安全
- HDU -2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)
- php多个表中查找数据_HeidiSQL 免费的可视化数据库管理工具
- 【Java】浅显理解 hashcode 和 hash 算法
- 20191022:(leetcode习题)山脉数组的峰顶索引
- linux执行shell过程日志,Android之在linux终端执行shell脚本直接打印当前运行app的日志...
- 5.Linux 高性能服务器编程 --- Linux 网络编程基础 API
- GNS3+GNS3 VM
- 服务器网卡多路径配置文件,IPSAN(五)IPSAN多路径设置(客户端)
- 阿里云服务器 安全组 防火墙 开放端口流程
- hive 求两个月之间的时间间隔(月留存)
- u盘安装系统win2019服务器系统,U盘启动装WIN10系统教程,U盘安装WIN2019方法,UltraISO将Windows server 2016/2019安装盘ISO写入U盘进行安装...
- 一加6手机可以把PDF文件转Word吗?
- 誉天华为数通认证技术指南之BGP的前世今生
- IDEA项目初次上传到git(超简单)
- VREP教程--**BubbleRob tutorial**
- java实现约瑟夫环完整算法_Java简单实现约瑟夫环算法示例
热门文章
- java 发卡平台支付_ZFAKA一款免费开源的发卡系统搭建教程 (支持多种支付接口)...
- GPS nmealib学习笔记 .
- [转载]用树莓派3配置成无线路由器
- 【PMAC】Chapter3:COM类工厂中CLSD为{XXX}的组件失败
- [转发]知识图谱 (Knowledge Graph) 专知 荟萃
- SSOP48的定位尺寸
- 利用MOS管和稳压二极管来做保护电路
- 形容计算机专业的诗句,描写技术精湛的诗句
- java基础多线程抢红包_java多线程模拟抢红包功能
- 国密浏览器介绍与下载