使用连接池的目的:重复利用Connection资源
连接池概述:
在Java中,我们使用javax.sql.DataSource来表示连接池对象。
DataSource:数据源,其实就是连接池,Connection Pool.
为什么要使用连接池:
普通的JDBC数据库连接使用 DriverManager来获取,每次向数据库建立连接的时候都要将Connection加载到内存中,再验证用户和密码
(得花费0.05s~1s的时间)。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源时间。
数据库的连接资源并没有得到很好的重复利用,若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将会占用很多的系统资源,
严重的甚至会造成服务器系统崩溃。对于每一次数据库连接,使用完后都得断开,否则如果程序出现异常而未能关闭,将会导致数据库系统
中的内存泄露,最终将导致重启数据库。这种开发不能控制被创建的连接对象数,系统资源会被毫无顾忌的分配出去,如连接过多,也可能
导致内存泄露,服务器奔溃。
常见的连接池技术:
dbcp :Spring推荐的连接池技术
c3p0 :HiBernate推荐的连接池技术
使用连接池和不使用连接池在代码上的区别:
使用连接池之前:使用DriverManager来获取Connection对象。
Connection conn = DriverManager.getConnection(url, username, password);
使用连接池之后:直接找连接池取出(Connection对象),取出Connection即可。
如何创建DataSource对象:
Connection conn = DataSource对象.getConnection();
接下来的代码和以前相同。
释放连接:
代码:connection对象.close();
使用连接池之前:直接和数据库服务器建立连接关系,而断开也是和数据库服务器断开连接。
使用连接池之后: 直接和连接池建立连接关系,而断开也是把connection对象还给连接池,供其他客服使用,没有真正的和数据库断开,
如此一来,一个connection对象就得到了充分的利用!!!
思考题:请你给我设计一个连接池。
dbcp连接池:
/**
* 1.拷贝jar包。
* Commons-dbcp-1.4.jar commons-pool-1.5.6.jar
* 2.阅读文档:commons-dbcp-1.3-src/doc/BasicDataSourceExample.java
*
* 步骤:
* 1.创建DataSource对象
* 2.从DataSource对象中获取Connection对象。
* 3.接下来使用Connection就和以前相同
*/
private DataSource getDS(){
//这些是阅读文档:commons-dbcp-1.3-src/doc/BasicDataSourceExample.java查到的。
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUsername("root");
ds.setPassword("111111");
ds.setUrl("jdbc:mysql:///jdbcdemo?useSSL=false");
//设置初始化连接数
ds.setInitialSize(5);
return ds;
}
@Test
public void testName() throws Exception {
//获取数据源对象
//DataSource ds = this.getDS();
//从数据源对象获取Connection对象
//Connection conn = ds.getConnection();
//=======================================
Connection conn = DBCPUtil.INSTANCE.getConn();//这是使用DBCPUtil工具包的操作!
String sql = "select * from product";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
while (rs.next()){
Long id = rs.getLong("id");
String name = rs.getString("productName");
System.out.println(id +"\t" + name);
}
JdbcUtil.INSTANCE.close(conn, ps, rs);
}
//jdbc的操作工具类
public enum DBCPUtil {
INSTANCE;
private static DataSource dataSource;
//只需要注册一次驱动即可,没必要每次都注册,放到jdbcutil类的静态代码块中(当字节码被加载进jvm,就会执行)
static {
Properties p = new Properties();
try {
//从classpath的根路径去加载db.properties文件
//加载dbcp。properties文件,并把该文件的信息读取到properties对象中。
InputStream inStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("dbcp.properties");
p.load(inStream);
//把装有连接信息的P对象传递给工厂类,-->DataSource对象
dataSource = BasicDataSourceFactory.createDataSource(p);
//根据Properties对象的信息,创建DataSource对象。
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建connection对象
* @return
*/
public Connection getConn() {
try {
//从dataSource中获取一个connection对象
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public void close(Connection conn, Statement st, ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (st != null) {
st.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
dbcp.properties文件内容:
url=jdbc:mysql://localhost:3306/jdbcdemo?useSSL=false
username=root
password=111111
driverClassName=com.mysql.jdbc.Driver
initialSize=1
maxActive=50
maxIdle=20
c3p0连接池:
/**
* 1.拷贝jar包。
* Commons-dbcp-1.4.jar commons-pool-1.5.6.jar
* 2.阅读文档:commons-dbcp-1.3-src/doc/BasicDataSourceExample.java
*
* 步骤:
* 1.创建DataSource对象
* 2.从DataSource对象中获取Connection对象。
* 3.接下来使用Connection就和以前相同
*/
private DataSource getDS() throws Exception {
//这些是阅读文档:commons-dbcp-1.3-src/doc/BasicDataSourceExample.java查到的。
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver"); //loads the jdbc driver
cpds.setJdbcUrl("jdbc:mysql:///jdbcdemo?userSSL=false");
cpds.setUser("root");
cpds.setPassword("111111");
//初始化连接数
cpds.setInitialPoolSize(5);
return cpds;
}
@Test
public void testName() throws Exception {
//获取数据源对象
//从数据源对象获取Connection对象
//Connection conn = this.getDS().getConnection();
//=======================================
Connection conn = C3P0Util.INSTANCE.getConn();
String sql = "select * from product";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
while (rs.next()){
Long id = rs.getLong("id");
String name = rs.getString("productName");
System.out.println(id +"\t" + name);
}
JdbcUtil.INSTANCE.close(conn, ps, rs);
}
代码中数据连接池的配置信息写死了,不利于维护。
解决方案:把c3p0的连接信息,抽离到配置文件中–>c3p0.properties
注意:必须按照以下规则:
1.文件名称必须叫做:c3p0.properties
2.c3p0.properties存放在classpath的根路径。就是放在sourcefolder文件中
3.c3p0.properties文件中的key必须以c3p0.作为前缀:如:c3p0.jdbcUrl
4.c3p0.properties文件中的key,必须ComboPooledDataSource的属性
因为c3p0在配置的时候有很多约束:
约束存放的位置:CLASSPATH根路径
约束文件的名称:c3p0.properties
就决定了c3p0内部会自动的去找classpath的跟路径去寻找c3p0.properties文件。
//jdbc的操作工具类
public enum C3P0Util {
INSTANCE;
private static DataSource dataSource;
//只需要注册一次驱动即可,没必要每次都注册,放到jdbcutil类的静态代码块中(当字节码被加载进jvm,就会执行)
static {
Properties p = new Properties();
try {
DataSource ds_unpooled = DataSources.unpooledDataSource();
dataSource = DataSources.pooledDataSource( ds_unpooled );
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建connection对象
* @return
*/
public Connection getConn() {
try {
//从dataSource中获取一个connection对象
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public void close(Connection conn, Statement st, ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (st != null) {
st.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
c3p0.properties文件内容:
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql:///jdbcdemo?userSSL=false
c3p0.user=root
c3p0.password=111111