本人小白一枚,欢迎大家一起讨论学习,如有错误,还望大家指教。
简述
我们通过JDBC编程知道,最原始的数据库使用的就是打开一个连接使用,使用过后一定要关闭连接释放资源。由于频繁的打开和关闭连接对jvm包括数据库都有一定的资源负担,尤其是应用压力较大时资源占用比较多容易产生性能问题。数据库连接池正是针对这个问题提出来的。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
Java中常用的数据库连接池有:DBCP 、C3P0、Druid、BoneCP、Proxool、DDConnectionBroker、DBPool、XAPool、Primrose、SmartPool、MiniConnectionPoolManager等。
概念: 数据库连接池其实就是一个用来存放数据库连接对象的容器。当系统初始化好后,容器被创建后会申请一些连接对象,当用户来访问数据库时,会从容器中获取连接对象,当用户使用完,会将对象归还给容器,而不是释放。
好处:
1、节约资源。
2、提高访问数据库的效率。
实现:
1、标准接口:DataSource javax.sql包下的
- 获取连接:getConnection()
- 归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接
2、 一般我们不去实现它,由数据库厂商来实现。
在这里我们只简单的介绍C3P0、Druid数据库连接池。
案例一: C3P0数据库连接池技术
使用步骤:
1、导入jar包 (两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar ,还有不要忘记导入数据库驱动jar包。
2、定义配置文件:
- 名称: c3p0.properties 或者 c3p0-config.xml
- 路径:直接将文件放在src目录下即可。
3、创建核心对象,数据库连接池对象 ComboPooledDataSource
4、获取连接: getConnection
5、获取执行sql对象:Statement
6、定义sql并执行操作
C3P0连接池配置文件(c3p0-config.xml)
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc?characterEncoding=UTF-8</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<!--初始化申请的连接数量-->
<property name="initialPoolSize">5</property>
<!--最大的连接数量-->
<property name="maxPoolSize">10</property>
<!--超时时间-->
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="otherc3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">8</property>
<property name="checkoutTimeout">1000</property>
</named-config>
</c3p0-config>
我们得了解一下此配置文件中的几个概念:
最小连接–应用启动后随即打开的连接数以及后续最小维持的连接数。
最大连接数–应用能够使用的最多的连接数
连接增长数–应用每次新打开的连接个数
举个例子说明连接池的运作:
假设设置了最小和最大的连接为10,20,那么应用一旦启动则首先打开10个数据库连接,但注意此时数据库连接池的正在使用数字为0–因为你并没有使用这些连接,而空闲的数量则是10。然后你开始登录,假设登录代码使用了一个连接进行查询,那么此时数据库连接池的正在使用数字为1、空闲数为9,这并不需要从数据库打开连接–因为连接池已经准备好了10个给你留着呢。登录结束了,当前连接池的连接数量是多少?当然是10,因为那个连接随着事务的结束已经返还给连接池了。然后同时有11个人在同一秒进行登录,会发生什么:连接池从数据库新申请(打开)了一个连接,连同另外的10个一并送出,这个瞬间连接池的使用数是11个,不过没关系正常情况下过一会儿又会变成10。如果同时有21个人登录呢?那第21个人就只能等前面的某个人登录完毕后释放连接给他。这时连接池开启了20个数据库连接–虽然很可能正在使用的数量已经降为0,那么20个连接会一直保持吗?当然不,连接池会在一定时间内关闭一定量的连接还给数据库,在这个例子里数字是20-10=10,因为只需要保持最小连接数就好了,而这个时间周期也是连接池里配置的。
项目结构及表记录
public static void main(String[] args) throws SQLException {
// 获取数据源,使用默认配置
DataSource dataSource = new ComboPooledDataSource();
// 获取连接对象
Connection connection = dataSource.getConnection();
// 定义sql
String sql = "UPDATE user SET salary = 5000 WHERE username = ?";
PreparedStatement statement = connection.prepareStatement(sql);
// 设置参数
statement.setString(1, "admin");
// 执行sql
statement.executeUpdate();
// 归还连接对象
connection.close();
}
操作成功,记录以成功修改
我们也可以使用指定名称配置,例如上面的c3p0-config.xml中存在otherc3p0的连接池。
DataSource ds = new ComboPooledDataSource("otherc3p0");
案例二: Druid数据库连接池技术
简介:Druid是目前最好的数据库连接池,由阿里巴巴提供,在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。同时Druid不仅仅是一个数据库连接池,它包括其他功能:
- 替换DBCP和C3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。
- 可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。
- 数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。
- SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-Logging、Log4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。
- 扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter机制,很方便编写JDBC层的扩展插件。
我们这里只简单使用下Druid提供的连接池,其他的功能在我们以后的学习中会继续深入:
步骤:
- 导入jar包 druid-1.0.9.jar,还有不要忘记导入数据库驱动jar包
- 定义配置文件:
- 是properties形式的
- 可以叫任意名称,可以放在任意目录下
- 加载配置文件(我们可以把加载配置文件的这个操作封装成一个工具类,便于我们操作)
- 获取数据库连接池对象:通过工厂来来获取 (DruidDataSourceFactory)
- 获取连接,执行sql。
项目目录结构及数据表结构
配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///jdbc?characterEncoding=UTF-8
username=root
password=root
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000
工具类
public class DruidUtils {
private static DataSource dataSource = null;
static {
try {
Properties properties = new Properties();
ClassLoader loader = DruidUtils.class.getClassLoader();
URL resource = loader.getResource("druid.properties");
properties.load(new FileReader(resource.getPath()));
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接对象
* @return
*/
public static Connection getConnection() {
try {
Connection connection = dataSource.getConnection();
return connection == null ? null : connection;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
/**
* 释放资源
* @param statement 执行sql对象
* @param connection 连接对象
*/
public static void close(Statement statement, Connection connection) {
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
// 归还连接,而不是关闭连接
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 释放资源
* @param resultSet 结果集对象
* @param statement 执行sql对象
* @param connection 连接对象
*/
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
}
close(statement, connection);
}
}
测试案例
public static void main(String[] args) {
try {
// 获取连接
Connection connection = DruidUtils.getConnection();
// 像表中添加记录
String sql = "INSERT INTO user VALUES('张三', 10000)";
// 执行sql
PreparedStatement statement = connection.prepareStatement(sql);
statement.executeUpdate();
// 释放资源
DruidUtils.close(statement, connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
查看数据库