01_数据库连接池,数据源,ResultSetMetaData,jdbc优化

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介:  一、数据库连接池 1. 什么是连接池 传统的开发模式下,Servlet处理用户的请求,找Dao查询数据,dao会创建与数据库之间的连接,完成数据查询后会关闭数据库的链接。 这样的方式会导致用户每次请求都要向数据库建立链接而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的


一、数据库连接池

1. 什么是连接池

传统的开发模式下,Servlet处理用户的请求,找Dao查询数据,dao会创建与数据库之间的连接,完成数据查询后会关闭数据库的链接。

这样的方式会导致用户每次请求都要向数据库建立链接而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、宕机。

 

解决方案就是数据库连接池

连接池就是数据库连接对象的一个缓冲池

我们可以先创建10个数据库连接缓存在连接池中,当用户有请求过来的时候,dao不必创建数据库连接,而是从数据库连接池中获取一个,用完了也不必关闭连接,而是将连接换回池子当中,继续缓存

 

使用数据库连接池可以极大地提高系统的性能

 

2. 实现数据库连接池

jdbc统一了数据库的操作  定义了规范

jdbc针对数据库连接池也定义的接口java.sql.DataSource,所有的数据库连接池实现都要实现该接口

该接口中定义了两个重载的方法

Connection getConnection()

Connection getConnection(String username,String password)

 

数据库连接池实现思路

1定义一个类实现java.sql.DataSource接口

2定义一个集合用于保存Connection对象,由于频繁地增删操作,用LinkedList比较好

3实现getConnection方法,在方法中取出LinkedList集合中的一个连接对象返回

注意:

    返回的Connection对象不是从集合中获得,而是删除

    用户用完Connection,会调用close方法释放资源,此时要保证连接换回连接池,而不是关闭连接

    重写close方法是难点,解决方案:装饰设计模式、动态代理

 

二、 数据源

通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。

 

一些开源组织提供了数据源的独立实现,常用的有:

DBCP 数据库连接池

C3P0 数据库连接池

1.  DBCP 数据源

介绍

DBCP Apache 软件基金组织下的开源连接池实现

tomcat服务器就是使用DBCP作为数据库连接池

使用DBCP数据源,需要导入两个jar

Commons-dbcp.jar:连接池的实现

Commons-pool.jar:连接池实现的依赖库

 

DBCP核心 API

BasicDataSource

数据源实现

BasicDataSourceFactory

用于创建数据源的工厂类

 

dbcp 创建连接池

方法1 直接创建对象,设置参数

BasicDataSource bds = new BasicDataSource();

 

// 设置连接数据库需要的配置信息

bds.setDriverClassName("com.mysql.jdbc.Driver");

bds.setUrl("jdbc:mysql://localhost:3306/jdbc3");

bds.setUsername("root");

bds.setPassword("root");

 

// 设置连接池的参数

bds.setInitialSize(5);

bds.setMaxActive(10);

 

ds = bds

 

方法2 通过工厂类创建对象,读取配置文件

try {

   Properties prop =new Properties();

   // 读配置文件

   InputStream in =

   JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");

   prop.load(in);

   ds =BasicDataSourceFactory.createDataSource(prop);

}catch (Exception e) {

   throw newExceptionInInitializerError(e);

}

 

配置文件为dbcpconfig.properties

#连接设置

driverClassName=com.mysql.jdbc.Driver

url=jdbc:mysql://localhost:3306/jdbc3

username=root

password=root

 

#<!-- 初始化连接 -->

initialSize=5

 

#最大连接数量

maxActive=10

 

#<!-- 最大空闲连接 -->

maxIdle=10

 

 

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60 -->

maxWait=60000

 

 

#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]

#注意:"user" "password" 两个属性会被明确地传递,因此这里不需要包含他们。

connectionProperties=useUnicode=true;characterEncoding=gbk

 

#指定由连接池所创建的连接的自动提交(auto-commit)状态。

defaultAutoCommit=true

 

#driver default 指定由连接池所创建的连接的只读(read-only)状态。

#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix

defaultReadOnly=

 

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。

#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED,REPEATABLE_READ, SERIALIZABLE

defaultTransactionIsolation=READ_UNCOMMITTED

2.  C3P0 数据源

介绍

c3p0是一个开源的jdbc连接池,我们熟悉的Hibernate Spring 框架使用的都是该数据源

 

创建连接池对象

方法1:直接创建对象,设置参数

ComboPooledDataSource cpds = new ComboPooledDataSource();

cpds.setDriverClass("com.mysql.jdbc.Driver");

cpds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc3");

cpds.setUser("root");

cpds.setPassword("root");

cpds.setInitialPoolSize(5);

cpds.setMaxPoolSize(15);

方法2:读取配置文件

ComboPooledDataSource cpds = newComboPooledDataSource("itcast");

配置文件为c3p0-config.xml 该文件需要放在类路径下

<c3p0-config>

 

   <default-config>

      <!—- 默认配置 –->

      <propertyname="initialPoolSize">5</property>

      <propertyname="maxPoolSize">15</property>

      <propertyname="driverClass">com.mysql.jdbc.Driver</property>

      <propertyname="jdbcUrl">jdbc:mysql://localhost:3306/jdbc3</property>

      <propertyname="user">root</property>

      <propertyname="password">root</property>

   </default-config>

   <named-configname="xwh">

      <propertyname="initialPoolSize">5</property>

      <propertyname="maxPoolSize">15</property>

      <propertyname="driverClass">com.mysql.jdbc.Driver</property>

      <propertyname="jdbcUrl">jdbc:mysql://localhost:3306/jdbc3</property>

      <propertyname="user">root</property>

      <propertyname="password">root</property>

   </named-config>

</c3p0-config>

 

三、ResultSetMetaData对象

元数据,可以理解为描述数据的数据

jdbc中的元数据是指数据库、表、列的定义信息

 

ResultSetMetaData对象表示结果集 ResultSet对象的元数据

获得该对象:

ResultSetMetaDatametaData = rs.getMetaData();

 

常用方法:

getColumnCount()  返回resultset对象的列数

getColumnName(int column)  获得指定列的名称

getColumnTypeName(int column) 获得指定列的类型

 

四、jdbc优化

使用jdbc对数据库进行crud操作时,会有很多重复的代码,仔细分析不难发现其实变化的只是其中几行代码

 

对于 cud(增删改) 操作,代码几乎完全一样, 唯一的区别就是sql语句不同,我们完全可以把相同的代码抽取出来定义在一个工具方法中,然后定义一个参数来接收sql语句

 

对于 r(查询) 操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet结果集的处理也有所不相同,因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中

 

优化后的工具类 JdbcUtils

// 通用的增删改方法

public static int update(String sql, Object[] params) throws SQLException {

   Connection conn =null;

   PreparedStatementpstmt = null;

   ResultSet rs = null;

   try {

      // 获得连接

      conn =getConnection();

      // 预编译sql

      pstmt =conn.prepareStatement(sql);

      // 将参数设置进去

      for(int i=0; params!=null&&i<params.length; i++) {

         pstmt.setObject(i+1,params[i]);

      }

      // 发送sql

      int num = pstmt.executeUpdate();

      return num;

   } finally {

      // 释放资源

      release(conn,pstmt, rs);

   }

}

 

// 优化查询

public static Object query(String sql, Object[] params,ResultSetHandler rsh) throws SQLException {

   Connection conn =null;

   PreparedStatementpstmt = null;

   ResultSet rs = null;

   try {

      // 获得连接

      conn =getConnection();

      // 预编译sql

      pstmt =conn.prepareStatement(sql);

      // 将参数设置进去

      for(int i=0; params!=null&&i<params.length;i++) {

         pstmt.setObject(i+1,params[i]);

      }

      // 发送sql

      rs =pstmt.executeQuery();

      // 不知道别人想如何处理结果集

      // 干脆想别人所要一个结果集的处理器

      // 为了让当前代码继续,定义一个结果集处理器接口

      // 策略模式, 规定算法,具体的算法留给将来的调用者实现

      Object obj =rsh.handle(rs);

      return obj;

   } finally {

      // 释放资源

      release(conn,pstmt, rs);

   }

}

 

 

结果集处理器接口

public interface ResultSetHandler {

   // 处理结果集的方法

   public Objecthandle(ResultSet rs);

}

 

实现类:

BeanListHandler

public class BeanListHandler implements ResultSetHandler{

 

   private Classclazz;

   publicBeanListHandler(Class clazz) {

      this.clazz =clazz;

   }

   public Objecthandle(ResultSet rs) {

      try {

         // 取出结果集所有的记录,封装到bean,存入list返回

         List list =new ArrayList();

         while(rs.next()) {

            Objectbean = clazz.newInstance();

            // 获得元数据

            ResultSetMetaDatametaData = rs.getMetaData();

            // 获得列的数量

            intcount = metaData.getColumnCount();

            // 遍历列

            for(inti=1; i<=count; i++) {

                // 取列名

                StringcolumnName = metaData.getColumnName(i);

                // 取这列的值

                Objectvalue = rs.getObject(columnName);

                // 反射出属性

                Fieldfield = clazz.getDeclaredField(columnName);

                // 设置属性

                field.setAccessible(true);

                field.set(bean,value);

            }

            // 加入list

            list.add(bean);

         }

         returnlist;

      } catch(Exception e) {

         throw newRuntimeException(e);

      }

   }

 

}

 

BeanHandler

public class BeanHandler implements ResultSetHandler {

   private Classclazz;

   publicBeanHandler(Class clazz) {

      this.clazz =clazz;

   }

   public Objecthandle(ResultSet rs) {

      // 不知道有几列数据,不知道列名,不知道封装到什么样的bean

      // 表的列明和javabean的字段名一致

      try {

         if(rs.next()){

            // 创建bean

            Objectbean = clazz.newInstance();

            // 封装数据

            // 获得结果集的元数据

            ResultSetMetaDatametaData = rs.getMetaData();

            intcount = metaData.getColumnCount();

            // 迭代取每一列的数据

            for(inti=1; i<=count; i++) {

                // 获得列名  username

                StringcolumnName = metaData.getColumnName(i);

                // 获得数据ddd

                Objectvalue = rs.getObject(columnName);

                // 根据列名反射出映射的属性 username

                Fieldfield = clazz.getDeclaredField(columnName);

                // 为属性赋值

                field.setAccessible(true);

                field.set(bean,value);

            }

            returnbean;

         }

         return null;

      } catch(Exception e) {

         throw newRuntimeException(e);

      }

   }

 

}

 

ArrayHandler

// 取出第一行的所有记录存入一个Object数组

public class ArrayHandler implements ResultSetHandler {

 

   public Objecthandle(ResultSet rs) {

      try {

         if(rs.next()) {

            // 指向了第一行的记录

            // 获得元数据

            ResultSetMetaDatametaData = rs.getMetaData();

            // 获得列数

            intcount = metaData.getColumnCount();

            // 创建数组

            Object[]arr = new Object[count];

            // 迭代所有列的值,存入数组

            for(inti=1; i<=count; i++) {

                Objectvalue = rs.getObject(i); // 获得指定列的值

                arr[i-1]= value;

            }

            returnarr;

         }

         return null;

      } catch(Exception e) {

         throw newRuntimeException(e);

      }

   }

}

 

批处理

 

处理大数据

Clob Character large Object

text

 

Blob binary large object

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
2月前
|
SQL Java 数据库连接
除了JDBC,还有哪些常见的数据库访问技术?
除了JDBC,还有哪些常见的数据库访问技术?
219 2
|
2月前
|
存储 人工智能 NoSQL
AI大模型应用实践 八:如何通过RAG数据库实现大模型的私有化定制与优化
RAG技术通过融合外部知识库与大模型,实现知识动态更新与私有化定制,解决大模型知识固化、幻觉及数据安全难题。本文详解RAG原理、数据库选型(向量库、图库、知识图谱、混合架构)及应用场景,助力企业高效构建安全、可解释的智能系统。
|
6月前
|
关系型数据库 MySQL 数据库连接
Django数据库配置避坑指南:从初始化到生产环境的实战优化
本文介绍了Django数据库配置与初始化实战,涵盖MySQL等主流数据库的配置方法及常见问题处理。内容包括数据库连接设置、驱动安装、配置检查、数据表生成、初始数据导入导出,并提供真实项目部署场景的操作步骤与示例代码,适用于开发、测试及生产环境搭建。
245 1
|
2月前
|
SQL 存储 监控
SQL日志优化策略:提升数据库日志记录效率
通过以上方法结合起来运行调整方案, 可以显著地提升SQL环境下面向各种搜索引擎服务平台所需要满足标准条件下之数据库登记作业流程综合表现; 同时还能确保系统稳健运行并满越用户体验预期目标.
183 6
|
3月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
545 5
|
5月前
|
机器学习/深度学习 SQL 运维
数据库出问题还靠猜?教你一招用机器学习优化运维,稳得一批!
数据库出问题还靠猜?教你一招用机器学习优化运维,稳得一批!
162 4
|
9月前
|
SQL 关系型数据库 MySQL
如何优化SQL查询以提高数据库性能?
这篇文章以生动的比喻介绍了优化SQL查询的重要性及方法。它首先将未优化的SQL查询比作在自助餐厅贪多嚼不烂的行为,强调了只获取必要数据的必要性。接着,文章详细讲解了四种优化策略:**精简选择**(避免使用`SELECT *`)、**专业筛选**(利用`WHERE`缩小范围)、**高效联接**(索引和限制数据量)以及**使用索引**(加速搜索)。此外,还探讨了如何避免N+1查询问题、使用分页限制结果、理解执行计划以及定期维护数据库健康。通过这些技巧,可以显著提升数据库性能,让查询更高效流畅。
|
10月前
|
关系型数据库 数据库 数据安全/隐私保护
云数据库实战:基于阿里云RDS的Python应用开发与优化
在互联网时代,数据驱动的应用已成为企业竞争力的核心。阿里云RDS为开发者提供稳定高效的数据库托管服务,支持多种数据库引擎,具备自动化管理、高可用性和弹性扩展等优势。本文通过Python应用案例,从零开始搭建基于阿里云RDS的数据库应用,详细演示连接、CRUD操作及性能优化与安全管理实践,帮助读者快速上手并提升应用性能。
|
10月前
|
JSON Java 关系型数据库
Hutool创建数据源工厂动态查询不同数据库不同数据表的数据
Hutool创建数据源工厂动态查询不同数据库不同数据表的数据
228 2

热门文章

最新文章