c3p0连接池的使用

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错误,看来单连十分恐怖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值