连接池
涉及概念:设计模式:资源池(Resource Pool)百度一下,你就知道
数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,从“缓冲池”中取出一个,使用完毕之后再放回去。
Tomcat连接池
Tomcat默认使用的是DBCP数据库连接池,使用的tomcat-dbcp.jar包。
数据源可以给项目单独配置,也可以全局配置:
单独配置:在项目META-INF下新建context.xml,内容如下Resource
全局设置:在tomcat中conf目录下的context.xml文件 context节点下添加resource
<Resource name="jdbc/Mobile"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
testWhileIdle="true"
testOnBorrow="true"
testOnReturn="false"
validationQuery="SELECT 1"
validationInterval="30000"
timeBetweenEvictionRunsMillis="30000"
maxActive="100"
minIdle="10"
maxWait="10000"
initialSize="10"
removeAbandonedTimeout="60"
removeAbandoned="true"
logAbandoned="false"
minEvictableIdleTimeMillis="30000"
jmxEnabled="true"
fairQueue="true"
jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
username="root"
password='root'
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://127.0.0.1:3306/hsdb?useUnicode=true&characterEncoding=utf-8&autoReconnect=true"/>
Spring框架常用的连接池C3P0
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定。c3p0所需jar:c3p0-0.9.2.1.jar mchange-commons-java-0.2.3.4.jar
基本使用:
1.引入jar包
2.编写配置文件
default-config:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="user">root</property>
<property name="password">root</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">
jdbc:mysql://localhost:3306/dbname?useSSL=true
</property>
<!--以上的user是数据库的用户,password是数据库的密码,driverClass是mysql的数据库驱动,jdbcUrl是连接数据库的url -->
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数 -->
<property name="acquireIncrement">5</property>
<!--初始化时获取十个连接,取值应在minPoolSize与maxPoolSize之间 -->
<property name="initialPoolSize">10</property>
<!--连接池中保留的最小连接数 -->
<property name="minPoolSize">10</property>
<!--连接池中保留的最大连接数 -->
<property name="maxPoolSize">50</property>
<property name="maxStatements">20</property>
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
<property name="maxStatementsPerConnection">5</property>
</default-config>
<named-config name="mysql">
<property name="user">root</property>
<property name="password">root</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/dbname?useSSL=true</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">10</property>
<property name="maxPoolSize">50</property>
<property name="maxStatements">20</property>
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>
3.实现操作
C3P0中连接池的实现类名ComboPooledDataSource 加载配置文件可以加载默认配置也可以加载带名字的配置
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//加载有名称的配置named-config
//ComboPooledDataSource dataSource =
new ComboPooledDataSource("mysql");
在Spring中的使用:
<!-- 配置c3p0数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!--连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize" value="100" />
<!--连接池中保留的最小连接数。-->
<property name="minPoolSize" value="1" />
<!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
<property name="initialPoolSize" value="10" />
<!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime" value="30" />
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement" value="5" />
<!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
<property name="maxStatements" value="0" />
<!--每60秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod" value="60" />
<!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
<property name="acquireRetryAttempts" value="30" />
<!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。Default: false-->
<property name="breakAfterAcquireFailure" value="true" />
<!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable等方法来提升连接测试的性能。Default: false -->
<property name="testConnectionOnCheckout" value="false" />
</bean>
JDBC连接池的一种原生写法:
Jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/dbname?useSSL=true
username=root
password=root
jdbcPoolInitSize=10
JdbcPool.java
package com.test0815;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.Properties;
import java.util.logging.Logger;
import javax.sql.DataSource;
/**
* 简单的的jdbc连接池实现
*/
public class JdbcPool implements DataSource {
/**
* 由于要频繁读写List集合,所以这里使用LinkedList存储数据库连接比较合适
* ps:LinkedList在性能上插入、删除数据速度快。
*/
private static LinkedList<Connection> listConnections = new LinkedList<Connection>();
private static Connection conn;
// 在静态代码块中加载jdbc.properties数据库配置文件
static {
InputStream in = JdbcPool.class.getResourceAsStream("/jdbc.properties");
Properties prop = new Properties();
try {
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
// 数据库连接池的初始化连接数大小
int jdbcPoolInitSize = Integer.parseInt(prop
.getProperty("jdbcPoolInitSize"));
// 加载数据库驱动
Class.forName(driver);
for (int i = 0; i < jdbcPoolInitSize; i++) {
conn = DriverManager.getConnection(url, username, password);
// System.out.println("获取到了连接" + conn);
// 将获取到的数据库连接加入到listConnections集合中,listConnections集合此时就是一个存放了数据库连接的连接池
listConnections.add(conn);
}
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
/*
*
* 获取数据库连接
*/
@Override
public Connection getConnection() throws SQLException {
// 如果数据库连接池中的连接对象的个数大于0
if (listConnections.size() > 0) {
// 从listConnections集合中获取一个数据库连接
System.out.println("listConnections数据库连接池大小是:"
+ listConnections.size());
// 返回Connection对象的代理对象
return (Connection) Proxy.newProxyInstance(JdbcPool.class
.getClassLoader(), conn.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (!method.getName().equals("close")) {
System.out.println("获取到连接池连接对象......" + conn);
return method.invoke(conn, args);
} else {
// 如果调用的是Connection对象的close方法,就把conn还给数据库连接池
listConnections.add(conn);
// System.out.println(conn+ ",还给数据库连接池了");
// System.out.println("listConnections数据库连接池大小为"+
// listConnections.size());
return null;
}
}
});
} else {
throw new RuntimeException("对不起,数据库忙");
}
}
//Override方法
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public Connection getConnection(String username, String password)
throws SQLException {
return null;
}
}
JDBCUtil.java
package com.test0815;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JDBCUtil {
private static JdbcPool pool = new JdbcPool();
/**
* 获取资源
*/
public static Connection getConnection() throws SQLException {
return pool.getConnection();
}
/**
* 关闭资源
*/
public static void close(ResultSet resultSet, Statement statement,
Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
resultSet = null;
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
statement=null;
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
connection = null;
}
}
//插入数据操作
public static void insertOrDeleteOrUpdate(String sql) {
try {
Connection connection = JDBCUtil.getConnection();
PreparedStatement pst = connection.prepareStatement(sql);
int execute = pst.executeUpdate();
System.out.println("执行语句:" + sql + "," + execute + "行数据受影响");
JDBCUtil.close(null, pst, connection);
} catch (SQLException e) {
System.out.println("异常提醒:" + e);
}
}
//插入数据操作
public static List<Map<String, Object>> select(String sql) {
List<Map<String, Object>> returnResultToList = null;
try {
Connection connection = JDBCUtil.getConnection();
PreparedStatement pst = connection.prepareStatement(sql);
ResultSet resultSet = pst.executeQuery();
returnResultToList = returnResultToList(resultSet);
JDBCUtil.close(resultSet, pst, connection);
} catch (SQLException e) {
System.out.println("异常提醒:" + e);
}
return returnResultToList;
}
/**
*
* 数据返回集合
* @param resultSet
* @return
* @throws SQLException
*/
public static List<Map<String, Object>> returnResultToList(
ResultSet resultSet) {
List<Map<String, Object>> values = null;
try {
// 键: 存放列的别名, 值: 存放列的值.
values = new ArrayList<>();
// 存放字段名
List<String> columnName = new ArrayList<>();
ResultSetMetaData rsmd = resultSet.getMetaData();
for (int i = 0; i < rsmd.getColumnCount(); i++) {
// 字段名
columnName.add(rsmd.getColumnLabel(i + 1));
}
System.out.println("表字段为:");
System.out.println(columnName);
System.out.println("表数据为:");
Map<String, Object> map = null;
// 处理 ResultSet, 使用 while 循环
while (resultSet.next()) {
map = new HashMap<>();
for (String column : columnName) {
Object value = resultSet.getObject(column);
map.put(column, value);
System.out.print(value + "\t");
}
// 把一条记录的 Map 对象放入准备的 List 中
values.add(map);
System.out.println();
}
} catch (SQLException e) {
System.out.println("异常提醒:" + e);
}
return values;
}
}