c3p0连接池
起因:想使用Mysql进行多线程操作,不行,然后找到了连接池的方式
如何进行多线程的操作,使用的是连接池,
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(index);
}
});
}
然后一开始使用的是单点连接的情况,。。。刚刚又去实验了一下,结果又可以了。。。可能是因为刚连接上没占用到那么多的连接,所以mysql还能坚持得住,但是不具备再现行性,因为第二次刷新又不出来了,,灵异事件。。。刚刚又看了一遍代码,加了打印语句后。。。原来一直能执行?我佛了?再展示一下我写的jdbc
public class MysqlJdbc {
public static final String DRIVER = "com.mysql.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&useSSL=false";
public static final String USER = "root";
public static final String PASSWORD = "123456";
public static Connection ct = null;
public static PreparedStatement ps = null;
private static ResultSet rs = null;
static {
try {
Class.forName(DRIVER);
}catch(ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
try {
ct = (Connection)DriverManager.getConnection(URL,USER,PASSWORD);
}catch(SQLException e) {
e.printStackTrace();
}
return ct;
}
public static void close() throws Exception{
if(rs != null) {
rs.close();
}
else if(ps != null) {
ps.close();
}
else if(ct != null) {
ct.close();
}
}
public static User selectID(int id) throws Exception{
User user = new User();
String sql = "select a.id,a.user_name,a.mobile_phone from usertable AS a WHERE a.id = ?";
Connection ct = getConnection();
PreparedStatement ps = (PreparedStatement) ct.prepareStatement(sql);
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
user.setId(Integer.valueOf(rs.getString(1)));
user.setUserName(rs.getString(2));
user.setMobilePhone(Long.valueOf(rs.getString(3)));
}
return user;
}
}
结果:
神奇
c3p0
通常有两种实现方式
第一种方式,就是通过配置文件来实现,这个配置文件可以是xml文件,就像下面的文件一样(并且可以在里面进行配置默认的配置项和带名字的配置项,带了名字的配置想使用上的话,要在get资源池的时候,把那个名字带上),properties应该也可以,不过不知道怎么导入。
第二种方式,就是硬编码实现,就是不使用配置文件,直接set属性值吧,这是我的理解。
第一种
jar包
c3p0-config.xml
<c3p0-config>
<!-- 默认配置,如果没有指定则使用这个配置 -->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">
<![CDATA[jdbc:mysql://127.0.0.1:3306/mydb?useUnicode=true&characterEncoding=UTF-8&useSSL=false]]>
</property>
<property name="user">root</property>
<property name="password">123456</property>
<!-- 初始化池大小 -->
<property name="initialPoolSize">2</property>
<!-- 最大空闲时间 -->
<property name="maxIdleTime">30</property>
<!-- 最多有多少个连接 -->
<property name="maxPoolSize">10</property>
<!-- 最少几个连接 -->
<property name="minPoolSize">2</property>
<!-- 每次最多可以执行多少个批处理语句 -->
<property name="maxStatements">50</property>
</default-config>
<!-- 命名的配置 -->
<named-config name="BarryLee">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/mydb</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="acquireIncrement">5</property><!-- 如果池中数据连接不够时一次增长多少个 -->
<property name="initialPoolSize">100</property>
<property name="minPoolSize">50</property>
<property name="maxPoolSize">1000</property>
<property name="maxStatements">0</property>
<property name="maxStatementsPerConnection">5</property> <!-- he's important, but there's only one of him -->
</named-config>
</c3p0-config>
Utils工具类,并且带上测试
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class MyC3P0Utils{
private static DataSource ds;
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
static {
ds = new ComboPooledDataSource();//直接使用即可,不用显示的配置,其会自动识别配置文件
}
public static DataSource getDataSource() {
return ds;
}
public static Connection getConnection() {
try {
// 得到当前线程上绑定的连接
Connection conn = tl.get();
if (conn == null) { // 代表线程上没有绑定连接
conn = ds.getConnection();
tl.set(conn);
}
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void startTransaction() {
try {
// 得到当前线程上绑定连接开启事务
Connection conn=getConnection();
conn.setAutoCommit(false);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void commitTransaction() {
try {
Connection conn = tl.get();
if (conn != null) {
conn.commit();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void closeConnection() {
try {
Connection conn = tl.get();
if (conn != null) {
conn.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
tl.remove(); // 千万注意,解除当前线程上绑定的链接(从threadlocal容器中移除对应当前线程的链接)
}
}
public static void main(String[] args) throws SQLException {
// Connection cn = getConnection();
// java.sql.PreparedStatement ps = cn.prepareStatement("select * from usertable");
// ResultSet rs = ps.executeQuery();
// while(rs.next()) {
// System.out.println(rs.getString(1));
// }
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
// System.out.println(index);
Random rd = new Random();
int id = rd.nextInt(10000);
Connection cn = getConnection();
java.sql.PreparedStatement ps = cn.prepareStatement("select * from usertable where id = ?");
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
System.out.println(rs.getString(2));
}
closeConnection();
System.out.println("");
Thread.sleep(2000);
} catch (InterruptedException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
}
运行结果
第二种
package Test01;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Test01 {
@Test
public void method() throws SQLException, PropertyVetoException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
comboPooledDataSource.setJdbcUrl("jdbc:mysql://Bessi:3306/db01");
comboPooledDataSource.setUser("jy");
comboPooledDataSource.setPassword("jy");
Connection conn = comboPooledDataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM emp");
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
System.out.println(rs.getInt("id") + " " + rs.getString("ename") + " " + rs.getDouble("sal") + " " + rs.getDate("birth"));
}
rs.close();
pstmt.close();
conn.close();
}
}
/*
c3p0硬编码方式:意义不大
*/
说起来,线程池的连接也是要关闭的,像第一种一样,要把线程退回给线程池。
再验证一下原来的原生是否是创建连接还是是使用了默认连接池,看能不能复现一下昨天出错的情况,捡查昨天为什么不行
Java中DriverManager跟DataSource获取getConnection有什么不同
当使用JDK提供的java(x).sql包中的类访问数据库时候,基本上用到的就是drivermanager,connection,statement,resultset。其中drivermanger是类,他调用相应的驱动(即各个数据库厂商提供的驱动)中的方法生成connection对象。Connection是接口,在各个数据库厂商提供的数据库驱动中,都实现了该接口。例如:当使用com.mysql.jdbc.driver时候,生成的connection即为com.mysql.jdbc.Connection对象。
Javax.sql包中定义了接口datasource,统一规定了作为数据源连接池必须提供的方法和属性等。各个数据源组件中提供的datasource都实现了该接口。当通过数据源连接池的方式获取connnection的时候,同样的,各个数据源组件也都提供(实现了java.sql.connection)接口的类。
更为具体的细节,可以参考jdk文档中关于java(x).sql包中相关类和接口的描述;参考开源数据源连接池组件的相关源码(例如C3P0);参考相关的数据库驱动。
综上:
DriverManager是获取一个connection,用完就进行关闭,需要又重新建立连接;
Datasource获取多个connection并管理起来,作为数据库连接池;很多第三方连接池都通过实现该接口来做连接池;
DriverManager在Java中的位置,java.sql包下
Datasource在Java中的位置,javax.sql包下
由此可知,昨天使用的的确是单点的连接,并且属于连完一次就释放一次那种。
测试性能,如果用的是新建连接的方式,那么性能上肯定很差,有待参考
可还行
连接池:
10条数据的时候是400ms左右:
100条数据的时候是480左右:
1000条数据的时候是1000ms左右:
10000条数据是3800ms左右:
100000次尝试一下,暴毙,没有时间显示,但数据库没崩,还是比较快的
单连接:
在10条的时候是430ms左右,
在100条数据的时候是830ms左右:
在1000条数据的时候就已经到4000ms了,并且暴毙了,
10000条我就不试了,刚刚才重启的电脑,mysql直接报too many connections错误
,看来单连十分恐怖。