1.数据库连接池

什么是数据库连接池

简单来说:数据库连接池就是提供连接的。。。

为什么我们要使用数据库连接池

  • 数据库的连接的建立和关闭是非常消耗资源的
  • 频繁地打开、关闭连接造成系统性能低下

编写连接池

  1. 编写连接池需实现java.sql.DataSource接口
  2. 创建批量的Connection用LinkedList保存【既然是个池,当然用集合保存、、LinkedList底层是链表,对增删性能较好】
  3. 实现getConnetion(),让getConnection()每次调用,都是在LinkedList中取一个Connection返回给用户
  4. 调用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的

解决思路:

  1. 写一个Connection子类,覆盖close()方法
  2. 写一个Connection包装类,增强close()方法
  3. 用动态代理,返回一个代理对象出去,拦截close()方法的调用,对close()增强

分析第一个思路:

  • Connection是通过数据库驱动加载的,保存了数据的信息。写一个子类Connection,new出对象,子类的Connction无法直接继承父类的数据信息,也就是说子类的Connection是无法连接数据库的,更别谈覆盖close()方法了。

分析第二个思路:

  • 写一个Connection包装类。

    1. 写一个类,实现与被增强对象的相同接口【Connection接口】
    2. 定义一个变量,指向被增强的对象
    3. 定义构造方法,接收被增强对象
    4. 覆盖想增强的方法
    5. 对于不想增强的方法,直接调用被增强对象的方法
  • 这个思路本身是没什么毛病的,就是实现接口时,方法太多了!,所以我们也不使用此方法

分析第三个思路代码实现:

@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数据源的步骤:

  1. 导入两个jar包【Commons-dbcp.jar和Commons-pool.jar】
  2. 读取配置文件
  3. 获取BasicDataSourceFactory对象
  4. 创建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配置文件配置信息!

步骤:

  1. 导入开发包【c3p0-0.9.2-pre1.jar】和【mchange-commons-0.2.jar】
  2. 导入XML配置文件【可以在程序中自己一个一个配,C3P0的doc中的Configuration有XML文件的事例】
  3. 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

步骤:

  1. 在META-INF目录下配置context.xml文件【文件内容可以在tomcat默认页面的 JNDI Resources下Configure Tomcat’s Resource Factory找到】
  2. 导入Mysql或oracle开发包到tomcat的lib目录下
  3. 初始化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连接数据库实现分页

下面是常见的分页图片


配合图片,看下我们的需求是什么:

  1. 算出有多少页的数据,显示在页面上
  2. 根据页码,从数据库显示相对应的数据。

分析:

  1. 算出有多少页数据这是非常简单的【在数据库中查询有多少条记录,你每页显示多少条记录,就可以算出有多少页数据了】
  2. 使用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框架、分页】相关推荐

  1. Spring JDBC数据库连接池设置

    对于任何Java应用程序而言, 在Spring框架中设置JDBC数据库连接池都是很容易的,仅需更改spring配置文件中的一些配置即可.使用Apache Commons DBCP和Commons Po ...

  2. python 数据库连接池DBUtils

    python 数据库连接池DBUtils 0x00 简述 0x01 摘要 0x02 模块 0x03 下载及安装 1.下载 2.安装 0x04 SimplePooledDB 0x05 SteadyDB ...

  3. JDBC数据库连接池练习题

    <JDBC数据库连接池练习题> 文章目录 单选题 多选题 判断题 填空题 单选题 1. 下面选项中,能够将游标从当前位置向下移一行的方法是( ). A.next() B.absolute( ...

  4. java jdbc init_Java 的JDBC 数据库连接池实现方法

    虽然 J2EE 程序员一般都有现成的应用服务器所带的JDBC 数据库连接池,不过对于开发一般的 Java Application . Applet 或者 JSP.velocity 时,我们可用的JDB ...

  5. Java jdbc数据库连接池

    1. 引言 近年来,随着Internet/Intranet建网技术的飞速发展和在世界范围内的迅速普及,计算机 应用程序已从传统的桌面应用转到Web应用.基于B/S(Browser/Server)架构的 ...

  6. JDBC 数据库连接池(JDBCUtils工具类)

    文章目录 前言 一.JDBC作用? 二.获取数据的连接方式 三.Statement/PreStatement执行sql 四.ResultSet结果集 五.JDBC相关API 六.事务 七.批处理 八. ...

  7. python数据库连接池工具类_Python数据库连接池DBUtils

    DBUtils简介 DBUtils是Python的一个用于实现数据库连接池的模块. 此连接池有两种连接模式: 模式一:为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放 ...

  8. Python数据库连接池DBUtils

    DBUtils是Python的一个用于实现数据库连接池的模块 此连接池有两种连接模式: DBUtils提供两种外部接口:     PersistentDB :提供线程专用的数据库连接,并自动管理连接. ...

  9. 【JAVA】JDBC数据库连接池

    目录 [JDBC] [JDBC--项目示例] [JDBC-API] [DriverManager]--驱动管理类 [Connection]--数据库连接对象 [Statement]--执行 [Resu ...

  10. JDBC——数据库连接池

    目录 前言 一.为什么要使用数据库连接池 二.数据库连接池 2.1 优点 三.多种开源数据库连接池 3.1 C3P0数据库连接池 3.2 DBCP连接池 3.3 Druid(德鲁伊)数据库连接池 总结 ...

最新文章

  1. 解析equals(Object obj)和compareTo(T obj)
  2. hibernate 逆向工程
  3. Luhn算法验证信用卡的合法性
  4. cpu线程_记w3wp占用CPU过高解决过程Dictionary线程安全
  5. HDU -2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)
  6. php多个表中查找数据_HeidiSQL 免费的可视化数据库管理工具
  7. 【Java】浅显理解 hashcode 和 hash 算法
  8. 20191022:(leetcode习题)山脉数组的峰顶索引
  9. linux执行shell过程日志,Android之在linux终端执行shell脚本直接打印当前运行app的日志...
  10. 5.Linux 高性能服务器编程 --- Linux 网络编程基础 API
  11. GNS3+GNS3 VM
  12. 服务器网卡多路径配置文件,IPSAN(五)IPSAN多路径设置(客户端)
  13. 阿里云服务器 安全组 防火墙 开放端口流程
  14. hive 求两个月之间的时间间隔(月留存)
  15. u盘安装系统win2019服务器系统,U盘启动装WIN10系统教程,U盘安装WIN2019方法,UltraISO将Windows server 2016/2019安装盘ISO写入U盘进行安装...
  16. 一加6手机可以把PDF文件转Word吗?
  17. 誉天华为数通认证技术指南之BGP的前世今生
  18. IDEA项目初次上传到git(超简单)
  19. VREP教程--**BubbleRob tutorial**
  20. java实现约瑟夫环完整算法_Java简单实现约瑟夫环算法示例

热门文章

  1. java 发卡平台支付_ZFAKA一款免费开源的发卡系统搭建教程 (支持多种支付接口)...
  2. GPS nmealib学习笔记 .
  3. [转载]用树莓派3配置成无线路由器
  4. 【PMAC】Chapter3:COM类工厂中CLSD为{XXX}的组件失败
  5. [转发]知识图谱 (Knowledge Graph) 专知 荟萃
  6. SSOP48的定位尺寸
  7. 利用MOS管和稳压二极管来做保护电路
  8. 形容计算机专业的诗句,描写技术精湛的诗句
  9. java基础多线程抢红包_java多线程模拟抢红包功能
  10. 国密浏览器介绍与下载