数据库连接池
数据库连接池是什么呢?
连接池就是我们创建了一个池子,这个池子里面放的都是一些JDBC的连接,也就是Connection对象
为什么要有连接池呢?
因为假如没有连接池,那么我们在使用JDBC程序的时候,就会反复的创建连接,销毁连接,这样做比较消耗资源,并不能做到连接的反复利用,这样做如果这个程序的用户人数很多,那么对性能的影响就会很大,所以我们为了优化性能,我们就需要自己去创建一个连接池,然后我们每次从连接池里面获取连接,使用完了连接,放回连接池,做到连接的反复使用
Demo1 自己手动实现一个连接池
第一个版本
// 连接池对象
public class MyConnectionPool1 {
// 得用一个数据结构去存放连接
static LinkedList<Connection> connectionPool;
static {
init(10);
}
// 往连接池里面放连接的方法
private static void init(int num) {
if (connectionPool == null) {
connectionPool = new LinkedList<>();
}
for (int i = 0; i < num; i++) {
// 添加一个连接
Connection connection = JDBCUtils.getConnection();
connectionPool.addFirst(connection);
}
}
// 获取连接 从头部存,从尾部取
public static Connection getConnection(){
// 动态扩容 要经过测试才能得出数值为多少的时候性能最好
if (connectionPool == null || connectionPool.size() < 5) {
init(10);
}
// 这里是取出连接
Connection connection = connectionPool.removeLast();
return connection;
}
// 返回连接,放回连接池
public static void releaseConnection(Connection connection){
connectionPool.addFirst(connection);
}
}
如何使用?
@Test
public void testMyConnectionPoolV1() throws SQLException {
//获取连接
Connection connection = MyConnectionPool1.getConnection();
Statement statement = connection.createStatement();
// 执行sql
ResultSet resultSet = statement.executeQuery("select * from account");
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
BigDecimal money = resultSet.getBigDecimal("money");
System.out.println("id:" + id +", name:" + name + ",money:" + money);
}
// 返回连接
MyConnectionPool1.releaseConnection(connection);
}
不足之处:
功能不完整、动态扩容的方法不够智能
最重要的一点,是我们这个连接池使我们自己创建的,我们自己去获取连接的方法叫做 getConnection(), 别人创建一个连接池,这个方法可能叫别的名字,例如acquireConnection(),那么我们在使用的时候就没有一个统一的规范和标准。SUN公司就制定了连接池的标准,来定义这些方法,各个连接池的实现只需要实现这个标准即可
javax.sql.Datasource
第二个版本 实现DataSource接口
public class MyConnectionPool2 implements DataSource {
// 得用一个数据结构去存放连接
static LinkedList<Connection> connectionPool;
static {
init(10);
}
// 往连接池里面放连接的方法
private static void init(int num) {
if (connectionPool == null) {
connectionPool = new LinkedList<>();
}
for (int i = 0; i < num; i++) {
// 添加一个连接
Connection connection = JDBCUtils.getConnection();
connectionPool.addFirst(connection);
}
}
@Override
public Connection getConnection() throws SQLException {
Connection connection = connectionPool.removeLast();
return connection;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
// 返回连接,放回连接池
public void releaseConnection(Connection connection){
connectionPool.addFirst(connection);
}
}
// 返回连接
// 为什么Datasource这个接口里面没有给我们定义返回连接的方法呢?
// 因为即使你定义了,还是阻止不了很多开发者去执行完任务去关闭连接
// 假如我们在Datasource接口里面定义了一个放回连接的方法,就会出现,我们使用的时候,可能先把连接关闭了,然后
// 再把连接放回去,那么这个时候放回连接池里面的连接就成了死连接,下次就不可复用了,所以Datasource接口并没有
// 给我们定义这样的一个放回连接的方法
// 那么这个时候我们应该怎么办才能去把这个连接放回去呢?如何去阻止用户手动关闭连接呢?
// 思路:
// 重写Close方法 我们是使用继承还是使用实现接口呢?
// 1. 继承Connection的之类 我们需要知道Connection具体是哪个实现类 ,我们发现目前我们的实现类是JDBC4Connection
// 我们发现JDBC4Connection 是在Mysql的驱动包里面,假如我们换了一个驱动包的类型或者版本,那么这个对象的
// 实例类型会不会变呢?例如在 mysql-connector-java 8.X的版本里面,Connection的实现类叫JDBC42Connection
// 所以我们采用继承JDBC4Connection的方法不太通用,不太合适
// 2. 实现Connection接口 可以做到这样的事情
第三个版本
自己实现Connection接口,然后自己创建一个连接池来维护
1. 实现Connection接口
// 这个是自己实现了一个连接对象
public class MyWrapperConnection implements Connection {
// 在这个类里面去维护一个Connection对象,我们就直接调用Connection对象的方法
private Connection connection;
private LinkedList<Connection> linkedList;
// 无参构造
public MyWrapperConnection() {
}
// 有参构造
public MyWrapperConnection(Connection connection, LinkedList<Connection> linkedList) {
this.connection = connection;
this.linkedList = linkedList;
}
@Override
public Statement createStatement() throws SQLException {
return connection.createStatement();
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return connection.prepareStatement(sql);
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
return connection.prepareCall(sql);
}
@Override
public String nativeSQL(String sql) throws SQLException {
return connection.nativeSQL(sql);
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
connection.setAutoCommit(autoCommit);
}
@Override
public boolean getAutoCommit() throws SQLException {
return false;
}
@Override
public void commit() throws SQLException {
}
@Override
public void rollback() throws SQLException {
}
// 这个方法要进行重写,自己实现
// 目的是要把这个关闭的方法里面变成放回连接池
@Override
public void close() throws SQLException {
// 这里传什么值呢?
linkedList.addFirst(this);
}
@Override
public boolean isClosed() throws SQLException {
return false;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
return null;
}
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
}
@Override
public boolean isReadOnly() throws SQLException {
return false;
}
@Override
public void setCatalog(String catalog) throws SQLException {
}
@Override
public String getCatalog() throws SQLException {
return null;
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
}
@Override
public int getTransactionIsolation() throws SQLException {
return 0;
}
@Override
public SQLWarning getWarnings() throws SQLException {
return null;
}
@Override
public void clearWarnings() throws SQLException {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return null;
}
@Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
}
@Override
public void setHoldability(int holdability) throws SQLException {
}
@Override
public int getHoldability() throws SQLException {
return 0;
}
@Override
public Savepoint setSavepoint() throws SQLException {
return null;
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
return null;
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return null;
}
@Override
public Clob createClob() throws SQLException {
return null;
}
@Override
public Blob createBlob() throws SQLException {
return null;
}
@Override
public NClob createNClob() throws SQLException {
return null;
}
@Override
public SQLXML createSQLXML() throws SQLException {
return null;
}
@Override
public boolean isValid(int timeout) throws SQLException {
return false;
}
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
}
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
}
@Override
public String getClientInfo(String name) throws SQLException {
return null;
}
@Override
public Properties getClientInfo() throws SQLException {
return null;
}
@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return null;
}
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return null;
}
@Override
public void setSchema(String schema) throws SQLException {
}
@Override
public String getSchema() throws SQLException {
return null;
}
@Override
public void abort(Executor executor) throws SQLException {
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
}
@Override
public int getNetworkTimeout() throws SQLException {
return 0;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
2.构建连接池
public class MyConnectionPool3 implements DataSource {
// 得用一个数据结构去存放连接
static LinkedList<Connection> connectionPool;
static {
init(10);
}
// 往连接池里面放连接的方法
private static void init(int num) {
if (connectionPool == null) {
connectionPool = new LinkedList<>();
}
for (int i = 0; i < num; i++) {
// 添加一个连接
Connection connection = JDBCUtils.getConnection();
MyWrapperConnection myWrapperConnection = new MyWrapperConnection(connection, connectionPool);
connectionPool.addFirst(myWrapperConnection);
}
}
@Override
public Connection getConnection() throws SQLException {
Connection connection = connectionPool.removeLast();
return connection;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
3.使用
@Test
public void testMyConnectionPoolV3() throws SQLException {
//获取连接
MyConnectionPool3 myConnectionPool3 = new MyConnectionPool3();
// 这里获取到的连接其实是我们自己实现的Connection对象,也就是MyWrapperConnection这个类的实例对象
Connection connection = myConnectionPool3.getConnection();
Statement statement = connection.createStatement();
// 执行sql
ResultSet resultSet = statement.executeQuery("select * from account");
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
BigDecimal money = resultSet.getBigDecimal("money");
System.out.println("id:" + id + ", name:" + name + ",money:" + money);
}
// 我们在这里调用close方法,其实是调用我们自己实现的close方法();
connection.close();
}
总结
我们发现自己手动实现这一系列连接池比较麻烦,并且很多功能自己都没有去实现,但是别人或者是别的组织已经考虑到了这个情况,所以网上有很多第三方的开源的数据库连接池供我们在实际工作中使用,有哪些呢?DBCP、C3p0、Druid有这三个可供我们选择
以前在工作中,C3p0是比较受大家欢迎的数据库连接池,因为配质简单,性能不错。
但是C3p0有两年时间没人去维护,Druid数据库连接池目前被大家广泛使用
DBCP
如何使用呢?
第一步,导包
<!--DBCP-->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
第二步 配置
建立一个dbcp.properties文件,放在我们的resources目录下
文件里面的内容
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=utf-8;useSSL=false
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=REPEATABLE_READ
第三步,创建DBCPUtils对象
这个工具类里面主要是提供我们获取连接的方法
public class DBCPUtils {
// 声明一个数据源对象
private static DataSource dataSource;
static {
// 加载配置文件
Properties properties = new Properties();
ClassLoader classLoader = DBCPUtils.class.getClassLoader();
InputStream stream = classLoader.getResourceAsStream("dbcp.properties");
try {
properties.load(stream);
} catch (IOException e) {
e.printStackTrace();
}
// 涉及到了一个设计模式:工厂模式
// 创建一个数据源工厂对象
BasicDataSourceFactory basicDataSourceFactory = new BasicDataSourceFactory();
try {
// 给Datasource赋值
dataSource = basicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
// 写一个方法 获取连接
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
C3p0
第一步 导包
<!--c3p0-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.4</version>
</dependency>
第二步 配置
需要说明的是,这个配置文件的名字必须是 c3p0-config.xml,并且必须放在Resources目录下
配置文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<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">123456</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
</c3p0-config>
第三步 创建C3p0Utils对象
public class C3p0Utils {
// 首先声明一个数据源对象
private static DataSource dataSource;
static {
// 给Datasource对象去赋值
dataSource = new ComboPooledDataSource();
}
// 获取连接
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
Druid
介绍一下:这个是阿里巴巴开源的一个数据库连接池,在国内被广泛使用
第一步 导包
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.19</version>
</dependency>
第二步 配置
- 首先在resouces里面创建一个配置文件
-
然后在配置文件里面去配置
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/28_jdbc username=root password=123456
第三步 创建DruidUtils对象
public class DruidUtils {
private static DataSource dataSource;
static {
// 加载配置文件
Properties properties = new Properties();
ClassLoader classLoader = DruidUtils.class.getClassLoader();
InputStream stream = classLoader.getResourceAsStream("druid.properties");
try {
properties.load(stream);
} catch (IOException e) {
e.printStackTrace();
}
// 创建一个Druid数据源工厂对象
DruidDataSourceFactory dataSourceFactory = new DruidDataSourceFactory();
try {
// 通过工厂创建一个数据源并且去赋值
dataSource = dataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取连接
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/181122.html