一、数据库连接池
1.1 传统JDBC存在的问题
曾经获取数据库连接的代码还需要使用 DriverManager,DriverManager.getConnection() 是通过数据库驱动与数据库建立连接。建立数据库连接以及关闭连接属于耗费时间的事情,如果业务层每次进行 SQL 查询都使用此方式,将会产生较大的系统开销。随着系统的发展迭代,出于系统的复杂度以及对性能的要求,这种获取连接的方式是难以接受的。数据库连接池正是针对这个问题提出来的。
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
1.2 DataSource 介绍
连接池虽然牛 ,但是独木难支。因为连接池没有产生连接的能力,所以还需要配合类似 DriverManager 组件与数据库驱动配合创建连接。如果这样放到业务代码里,那岂不是还得封装一层?
这个时候,不约而同的想到一个公司,sun 公司是干啥的?制定规范的对不对,他们在 jdbc 2.0 版本推出一个 DataSource 的东西,用来进行规范约束访问数据库的流程。
相当于把 DriverManager 和连接池概念揉合在一起,如果你想获取数据库连接,你通过我 DataSource 获取,你不用关心连接池和数据库连接怎么创建的,用就完了,使用完也不用关闭连接。其实,DataSource 获取的连接来自于连接池,而连接池的连接其实还是从 DriverManager或类似组件中创建的。
DriverManager 只是 jdbc 1.0 版本用来调用数据库驱动的的工具包,jdbc 2.0 版本推出 DataSource 之后,典型的像(Druid数据库连接池)DruidDataSource 就没有依赖DriverManager,而是在自己实现类中调用了数据库驱动。这里只是重点强调,数据库连接不一定是使用 DriverManager 创建。
/*
*数据源(DataSource)是 sun 公司指定用于获取数据库连接的规范接口,应用程序于数据库连接抽象的中间层,它存在于 javax.sql 包,用来代替 DriverManager 的方式获取数据库连接.
*有了 DataSource 之后,数据库连接、用户名、密码都进行了统一的管理,作为 DataSource 属性的一部分,并且将数据库驱动名称填充,底层自动加载
*/
public interface DataSource extends CommonDataSource, Wrapper {
//两个方法,是一个重载的关系,用于建立 DataSource 所代表数据源的数据库连接
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password) throws SQLException;
}
这里应该注意的是 CommonDataSource 接口,公共数据源接口用来定义三个数据源接口的公共方法:
javax.sql.DataSource:定义基础获取数据库连接的接口
javax.sql.ConnectionPoolDataSource:定义从数据库连接池中获取连接的接口
javax.sql.XADataSource:定义获取分布式事务连接的接口。
对于前两种,sun 公司定义规范时,就是希望你普通获取数据库连接使用 DataSource,数据源底层如果是连接池那么使用 ConnectionPoolDataSource。
后面发展逐渐脱离了原本的轨道预期,比如 DruidDataSource 就同时实现了两者。
1.3 连接池
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
1.4 连接池的工作原理
连接池的核心思想是连接的复用,通过建立一个数据库连接池以及一套连接使用、分配和管理策略,使得该连接池中的连接可以得到高效,安全的复用,避免了数据库连接频繁建立和关闭的开销。
连接池的工作原理主要由三部分组成,分别为连接池的建立,连接池中连接的使用管理,连接池的关闭。
第一:连接池的建立。一般在系统初始化时,连接池会根据系统配置建立,并在池中建立几个连接对象,以便使用时能从连接池中获取,连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。java中提供了很多容器类,可以方便的构建连接池.
第二:连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其策略是:当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有控线连接,则查看当前所开的连接数是否已经达到最大连接数,例如如果没有达到就重新创建一个请求的客户;如果达到,就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过了就从连接池中删除该连接,否则就保留为其他客户服务。该策略保证了数据库连接的有效复用,避免了频繁建立释放连接所带来的系统资源的开销。
第三:连接池的关闭。当应用程序退出时,关闭连接池中所有的链接,释放连接池相关资源。
1.5 Druid
Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。
使用:
配置文件:
常见参数
url=jdbc:mysql://IP:端口/db_student?serverTimezone=UTC&characterEncoding=UTF8
#这个可以缺省的,会根据url自动识别
driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=123456
##初始连接数,默认0
initialSize=10
#最大连接数,默认8
maxActive=30
#最小闲置数
minIdle=10
#获取连接的最大等待时间,单位毫秒
maxWait=2000
代码:
public static void main(String[] args) {
//数据源配置文件
Properties properties=new Properties();
//通过当前类的class对象获取资源文件
InputStream is = Test.class.getResourceAsStream("druid.properties");
properties.load(is);
//返回的是DataSource,不是DruidDataSource
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//获取连接
Connection connection = dataSource.getConnection();
//PreparedStatement接口
String sql = "insert into tb_student (name,age) values ('chy',22)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.execute();
//关闭连接
connection.close();
}
//当使用循环来获取连接,发现每个连接不一样,确实使用了连接池
com.mysql.cj.jdbc.ConnectionImpl@5e4c8041
com.mysql.cj.jdbc.ConnectionImpl@71c8becc
com.mysql.cj.jdbc.ConnectionImpl@19d37183
com.mysql.cj.jdbc.ConnectionImpl@1a0dcaa
com.mysql.cj.jdbc.ConnectionImpl@3bd40a57
com.mysql.cj.jdbc.ConnectionImpl@fdefd3f
com.mysql.cj.jdbc.ConnectionImpl@d83da2e
com.mysql.cj.jdbc.ConnectionImpl@a4102b8
二、JDBCTemplate
用了连接池之后,我们会发现连接对象的复用性更高了,程序整体运行的性能也更高了。但是我们在做JDBC操作的时候还是比较麻烦,要定义sql,执行sql,设置参数,处理结果。
尤其是当我们要做查询操作的时候,处理结果会变得相当麻烦。我们要手动把数据封装成对象,然后从结果集里获取数据,然后再创建对象,给对象属性赋值。这个过程很麻烦,都是体力活。我们只希望定义sql,然后把sql执行了,封装都交给别人。JDBCtemplate的作用就是干这个的。
2.1 druid+ JDBCTemplate 使用
在使用JDBCTemplate我们会将数据库连接池交给JDBCTemplate来管理。使用时还需要druid相关的jar包或者是其他连接池jar包。
druid+jdbcTemplate ,需要导入JDBCTemplate jar包5个+druid jar包1个 + 数据库驱动包
随着项目规模的扩大,我们发现仅仅使用数据库就需要导入7个第三方jar包,很不方便,因此我们使用maven项目管理工具。
<!--日志记录-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<!--JAVAbeen管理-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.13</version>
</dependency>
<!--核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.13</version>
</dependency>
<!--jdbc支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.13</version>
</dependency>
<!--事务支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.13</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!-- druid连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
2.2 JDBCUtils工具类的设计
public class JDBCUtils {
//声明静态数据源成员变量
private static DataSource ds;
//加载配置信息,初始化连接池
static{
try {
Properties pro=new Properties();
InputStream is= JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接对象
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//获取数据库连接池
public static DataSource getDataSource() {
return ds;
}
}
2.3 获取JDBCTemplate对象,执行sql语句
创建JDBCTemplate对象。依赖于数据源DataSource
JdbcTemplate template = new JdbcTemplate(datasource)
调用JdbcTemplate的方法来完成CRUD的操作
update():执行DML语句。增删改语句
queryForMap():查询结果将结果集封装为map集合
queryForList():查询结果将结果集封装为list集合
query():查询结果,将结果封装为JavaBean对象
queryForObject:查询结果,将结果封装为对象
示例代码
public static void main(String[] args) {
//创建JDBCTemplate对象
JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDateSource());
//3.调用方法
String sql = "update user set name =''黎明 where id = ?";
int count = template.update(sql, 3);
System.out.println(count);
}
以上内容参考自下面文章:
DataSource,一个被严重低估的接口
JDBCTemplate的基本使用