一、JDBC连接数据库。
1.加载JDBC驱动。
连接数据库之前,首先加载想要连接的数据库驱动到JVM,通过java.lang.Class类的静态forName方法实现
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new DbException("com.mysql.jdbc.Driver not found");
}
2.JDBC连接的URL。
url需要定义连接数据库时的协议、子协议、数据源标识。协议:在JDBC总是以jdbc开始。子协议:桥连接的驱动程序或是数据库管理系统名称。数据源标识:标识找到数据库来源的地址与连接端口。
jdbc:mysql://" + dbConnInfo.host + "/" + dbConnInfo.name + "?useUnicode=true&characterEncoding=UTF-8
3.创建数据库连接。
向java.sql.DriverManager请求并获得Connection对象,代表一个数据库的连接。
Connection c = DriverManager.getConnection(dbConnInfoURL, dbConnInfo.user, dbConnInfo.pass);
4.创建一个Statement。
执行sql语句必须获得java.sql.Statement实例,有三种类型。
执行静态sql语句。(这里增加了一个数据库连接池)
ConnectionPool pool = ConnectionManager.getConnectionPool();
DBConnection c = pool.get();
Statement stmt = c.createStatement();
ResultSet = stmt.executeQuery("select * from users");
执行动态sql
PreparedStatement ps = c.prepareStatement(sql);
int idx = 1;
ps.setInt(idx++, userId);
rs = ps.executeQuery();
执行数据库存储过程Connection c.prepareCall(sql);
5.执行sql语句。
Statement提供三种执行sql语句的方法,executeQuery、executeUpdate、execute。executeQuery 执行插叙数据库sql,返回结果集 ResultSet。executeUpdate 执行insert update delete语句以及sql DDL语句,create drop等。execute执行返回多个boolean结果集,多个更新计数或二者组合的语句。
6.处理结果。执行更新返回的是本次操作影响的记录数。执行查询返回的ResultSet对象。
ResultSet set.get(i);比较高效的
7.关闭JDBC对象。
操作完成要关闭JDBC,关闭顺序与声明顺序相反:关闭记录集,关闭声明,关闭连接对象。
if(rs != null){ // 关闭记录集
try{
rs.close() ;
}catch(SQLException e){
e.printStackTrace() ;
}
}
if(stmt != null){ // 关闭声明
try{
stmt.close() ;
}catch(SQLException e){
e.printStackTrace() ;
}
}
if(conn != null){ // 关闭连接对象
try{
conn.close() ;
}catch(SQLException e){
e.printStackTrace() ;
}
}
二、DBCP连接数据库。
dbcp为apache开源项目:http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi
DBCP连接池配置参数讲解
-----------------------------
一、Apache官方DBCP文档给出的配置示例:
可参见:http://tomcat.apache.org/tomcat-6.0-doc/jndi-datasource-examples-howto.html
<Context>
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="javauser" password="javadude" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javatest"/>
</Context>
二、常用参数说明:
可参见:http://elf8848.iteye.com/blog/337981
<Resource
name="jdbc/TestDB" JNDI数据源的name
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver" JDBC驱动类
url=""
username="" 访问数据库用户名
password="" 访问数据库的密码
maxActive="80" 最大活动连接
initialSize="10" 初始化连接
maxIdle="60" 最大空闲连接
minIdle="10" 最小空闲连接
maxWait="3000" 从池中取连接的最大等待时间,单位ms.
validationQuery = "SELECT 1" 验证使用的SQL语句
testWhileIdle = "true" 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.
testOnBorrow = "false" 借出连接时不要测试,否则很影响性能
timeBetweenEvictionRunsMillis = "30000" 每30秒运行一次空闲连接回收器
minEvictableIdleTimeMillis = "1800000" 池中的连接空闲30分钟后被回收
numTestsPerEvictionRun="3" 在每次空闲连接回收器线程(如果有)运行时检查的连接数量
removeAbandoned="true" 连接泄漏回收参数,当可用连接数少于3个时才执行
removeAbandonedTimeout="180" 连接泄漏回收参数,180秒,泄露的连接可以被删除的超时值
/>
DBCP连接池的自我检测
-----------------------------
默认配置的DBCP连接池,是不对池中的连接做测试的,有时连接已断开了,但DBCP连接池不知道,还以为连接是好的呢。
应用从池中取出这样的连接访问数据库一定会报错。这也是好多人不喜欢DBCP的原因。
问题例一:
MySQL8小时问题,Mysql服务器默认连接的“wait_timeout”是8小时,也就是说一个connection空闲超过8个小时,Mysql将自动断开该 connection。
但是DBCP连接池并不知道连接已经断开了,如果程序正巧使用到这个已经断开的连接,程序就会报错误。
问题例二:
以前还使用Sybase数据库,由于某种原因,数据库死了后重启、或断网后恢复。
等了约10分钟后,DBCP连接池中的连接还都是不能使用的(断开的),访问数据应用一直报错,最后只能重启Tomcat问题才解决 。
解决方案:
方案1、定时对连接做测试,测试失败就关闭连接。
方案2、控制连接的空闲时间达到N分钟,就关闭连接,(然后可再新建连接)。
以上两个方案使用任意一个就可以解决以述两类问题。如果只使用方案2,建议 N <= 5分钟。连接断开后最多5分钟后可恢复。
也可混合使用两个方案,建议 N = 30分钟。
下面就是DBCP连接池,同时使用了以上两个方案的配置配置
validationQuery = "SELECT 1" 验证连接是否可用,使用的SQL语句
testWhileIdle = "true" 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.
testOnBorrow = "false" 借出连接时不要测试,否则很影响性能
timeBetweenEvictionRunsMillis = "30000" 每30秒运行一次空闲连接回收器
minEvictableIdleTimeMillis = "1800000" 池中的连接空闲30分钟后被回收,默认值就是30分钟。
numTestsPerEvictionRun="3" 在每次空闲连接回收器线程(如果有)运行时检查的连接数量,默认值就是3.
解释:
配置timeBetweenEvictionRunsMillis = "30000"后,每30秒运行一次空闲连接回收器(独立纯种)。并每次检查3个连接,如果连接空闲时间超过30分钟就销毁。销毁连接后,连接数量就少了,如果小于minIdle数量,就新建连接,维护数量不少于minIdle,过行了新老更替。
testWhileIdle = "true" 表示每30秒,取出3条连接,使用validationQuery = "SELECT 1" 中的SQL进行测试 ,测试不成功就销毁连接。销毁连接后,连接数量就少了,如果小于minIdle数量,就新建连接。
testOnBorrow = "false" 一定要配置,因为它的默认值是true。false表示每次从连接池中取出连接时,不需要执行validationQuery = "SELECT 1" 中的SQL进行测试。若配置为true,对性能有非常大的影响,性能会下降7-10倍。所在一定要配置为false.
每30秒,取出numTestsPerEvictionRun条连接(本例是3,也是默认值),发出"SELECT 1" SQL语句进行测试 ,测试过的连接不算是“被使用”了,还算是空闲的。连接空闲30分钟后会被销毁。
DBCP连接池配置参数注意事项
-----------------------------
maxIdle值与maxActive值应配置的接近。
因为,当连接数超过maxIdle值后,刚刚使用完的连接(刚刚空闲下来)会立即被销毁。而不是我想要的空闲M秒后再销毁起一个缓冲作用。这一点DBCP做的可能与你想像的不一样。
若maxIdle应与maxActive相差较大,在高负载的系统中会导致频繁的创建、销毁连接,连接数在maxIdle与maxActive间快速频繁波动,这不是我想要的。
高负载的系统的maxIdle值可以设置为与maxActive相同或设置为-1(-1表示不限制),让连接数量在minIdle与maxIdle间缓冲慢速波动。
timeBetweenEvictionRunsMillis建议设置值
initialSize="5",会在tomcat一启动时,创建5条连接,效果很理想。
但同时我们还配置了minIdle="10",也就是说,最少要保持10条连接,那现在只有5条连接,哪什么时候再创建少的5条连接呢?
1、等业务压力上来了, DBCP就会创建新的连接。
2、配置timeBetweenEvictionRunsMillis=“时间”,DBCP会启用独立的工作线程定时检查,补上少的5条连接。销毁多余的连接也是同理。
连接销毁的逻辑
------------------------------
DBCP的连接数会在 0 - minIdle - maxIdle - maxActive 之间变化。变化的逻辑描述如下:
默认未配置initialSize(默认值是0)和timeBetweenEvictionRunsMillis参数时,刚启动tomcat时,连接数是0。当应用有一个并发访问数据库时DBCP创建一个连接。
目前连接数量还未达到minIdle,但DBCP也不自动创建新连接已使数量达到minIdle数量(没有一个独立的工作线程来检查和创建)。
随着应用并发访问数据库的增多,连接数也增多,但都与minIdle值无关,很快minIdle被超越,minIdle值一点用都没有。
直到连接的数量达到maxIdle值,这时的连接都是只增不减的。 再继续发展,连接数再增多并超过maxIdle时,使用完的连接(刚刚空闲下来的)会立即关闭,总体连接的数量稳定在maxIdle但不会超过maxIdle。
但活动连接(在使用中的连接)可能数量上瞬间超过maxIdle,但永远不会超过maxActive。
这时如果应用业务压力小了,访问数据库的并发少了,连接数也不会减少(没有一个独立的线程来检查和销毁),将保持在maxIdle的数量。
默认未配置initialSize(默认值是0),但配置了timeBetweenEvictionRunsMillis=“30000”(30秒)参数时,刚启动tomcat时,连接数是0。马上应用有一个并发访问数据库时DBCP创建一个连接。
目前连接数量还未达到minIdle,每30秒DBCP的工作线程检查连接数是否少于minIdle数量,若少于就创建新连接直到达到minIdle数量。
随着应用并发访问数据库的增多,连接数也增多,直到达到maxIdle值。这期间每30秒DBCP的工作线程检查连接是否空闲了30分钟,若是就销毁。但此时是业务的高峰期,是不会有长达30分钟的空闲连接的,工作线程查了也是白查,但它在工作。到这里连接数量一直是呈现增长的趋势。
当连接数再增多超过maxIdle时,使用完的连接(刚刚空闲下来)会立即关闭,总体连接的数量稳定在maxIdle。停止了增长的趋势。但活动连接(在使用中的连接)可能数量上瞬间超过maxIdle,但永远不会超过maxActive。
这时如果应用业务压力小了,访问数据库的并发少了,每30秒DBCP的工作线程检查连接(默认每次查3条)是否空闲达到30分钟(这是默认值),若连接空闲达到30分钟,就销毁连接。这时连接数减少了,呈下降趋势,将从maxIdle走向minIdle。当小于minIdle值时,则DBCP创建新连接已使数量稳定在minIdle,并进行着新老更替。
配置initialSize=“10”时,tomcat一启动就创建10条连接。其它同上。
minIdle要与timeBetweenEvictionRunsMillis配合使用才有用,单独使用minIdle不会起作用。
Tomcat中配置DBCP连接池
-----------------------------
Tomcat自带DBCP的包,是$CATALINA_HOME/lib/tomcat-dbcp.jar。
tomcat-dbcp.jar含有commons pool、commons DBCP两个包的内容。但只含有与连接池有关的类。
数据源配置在context.xml文件中, 要在tomcat的lib目录中放jdbc 驱动包
数据源配置在server.xml的host中,不需要在tomcat的lib目录中放jdbc 驱动包,只使用工程中的jdbc驱动包
JNDI配置:更改tomcat的server.xml或context.xml
全局的数据源:
如果需要配置全局的 Resource,则在server.xml的GlobalNamingResources节点里加入Resource,再在Context节点里加入ResourceLink的配置。
全局的resource只是为了重用,方便所有该tomcat下的web工程的数据源管理,但如果你的tomcat不会同时加载多个web工程,也就是说一个tomcat只加载一个web工程时,是没有必要配置全局的resource的。
每个web工程一个数据源:
在$CATALINA_HOME/conf/context.xml的根节点Context里加入Resource配置。这种配置方法,你在context.xml配置了一个数据源,但Tomcat中有同时运行着5个工程,那了就坏事儿了,这个在Tomcat启动时数据源被创建了5份,每个工程1份数据源。连接数会是你配置的参数的5倍。
只有在你的Tomcat只加载一个web工程时,才可以直接以context.xml配置数据源。
<Resource name="jdbc/testDB" //指定的jndi名称,会用于spring数据源bean的配置和ResourceLink的配置
type="javax.sql.DataSource" //数据源床型,使用标准的javax.sql.DataSource
driverClassName="com.mysql.jdbc.Driver" //JDBC驱动器
url="jdbc:mysql://localhost:3306/test" //数据库URL地址
username="test" //数据库用户名
password="test" //数据库密码
maxIdle="40" //最大的空闲连接数
maxWait="4000" //当池的数据库连接已经被占用的时候,最大等待时间
maxActive="40" //连接池当中最大的数据库连接
removeAbandoned="true"
removeAbandonedTimeout="180"
logAbandoned="true" //被丢弃的数据库连接是否做记录,以便跟踪
factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory" />
这里的factory指的是该Resource 配置使用的是哪个数据源配置类,这里使用的是tomcat自带的标准数据源Resource配置类,这个类也可以自己写,实现javax.naming.spi.ObjectFactory 接口即可。某些地方使用的commons-dbcp.jar中的org.apache.commons.dbcp.BasicDataSourceFactory,如果使用这个就需把commons-dbcp.jar及其依赖的jar包,都放在tomcat的lib下,光放在工程的WEB-INF/lib下是不够的。
ResourceLink 的配置有多种:
1)tomcat安装目录下的conf/context.xml,把全局的resource直接公开给该tomcat下的所有web工程,在Context节点中加入:
<ResourceLink global="jdbc/testMDB" name="jdbc/testMDB" type="javax.sql.DataSource"/>
不建议在此文件中,不使用<ResourceLink/>,而使用<Resource/>直接配置数据源,原因上面已说明了。
2)tomcat安装目录下的conf/server.xml,该方法可以指定把哪些source绑定到哪个web工程下。
<!-- 新增,第一行为加载的工程配置,第二行是该工程需要的ResourceLink配置 -->
<context docBase="/web/webapps/phoenix" path="" reloadable="false">
<ResourceLink global="jdbc/testMDB" name="jdbc/testMDB" type="javax.sql.DataSource"/>
</context>
也可在此文件中,不使用<ResourceLink/>,而使用<Resource/>直接配置数据源。
3)安装目录下的conf/localhost/下建立一个xml文件,文件名是<yourAppName>.xml。比如工程名为test,则该xml名为test.xml。
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<ResourceLink global="jdbc/testMDB" name="jdbc/testMDB" type="javax.sql.DataSource"/>
</context>
也可在此文件中,不使用<ResourceLink/>,而使用<Resource/>直接配置数据源。
4)tomcat安装目录下的\webapps\test\META-INF\context.xml的Context节点中增加:
<ResourceLink global="jdbc/testMDB" name="jdbc/testMDB" type="javax.sql.DataSource"/>
也可在此文件中,不使用<ResourceLink/>,而使用<Resource/>直接配置数据源。
三、C3P0连接池一、实现方式:
C3P0有三种方式实现:
1.自己动手写代码,实现数据源
例如:在类路径下配置一个属性文件,config.properties,内容如下:
driverClass=xxx
jdbcUrl=xxx
user=xxx
password=xxx
...
然后代码中实现
Properties props = new Properties();
InputStream in = Thread.class.getResourceAsStream("config.properties");
props.load(in);
in.close();
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass(props.getProperty("driverClass"));
cpds.setJdbcUrl(props.getProperty("jdbcUrl"));
cpds.setUser(props.getProperty("user"));
cpds.setPassword(props.getProperty("password"));
...
这里实现了一个数据源。
也可以这样配置,在类路径下配置一个xml文件,config.xml
<config>
<source name="source1">
<property name="user">root</property>
<property name="password">xxx</property>
<property name="url">xxx</property>
<property name="driverClass">xxx</property>
</source>
<source name="source2">
...
</source>
</config>
然后自己解析xml文件,这样可以实现多个数据源的配置
2.配置默认的熟悉文件
类路径下提供一个c3p0.properties文件(不能改名)
配置如下:
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/jdbc
c3p0.user=root
c3p0.password=java
这种方式使用方式与第二种差不多,但是有更多的优点
(1).更直观明显,很类似hibernate和spring的配置
(2).可以为多个数据源服务,提供default-config和named-config两种配置方式
<c3p0-config>
<default-config>
<property name="user">root</property>
<property name="password">java</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
</default-config>
<named-config name="mySource">
<property name="user">root</property>
<property name="password">java</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
</named-config>
</c3p0-config>
...
DataSource ds = new ComboPooledDataSource("mySource");
return ds;
...
这样就可以使用数据源了。
二、部分参数配置说明:
1.最常用配置
initialPoolSize:连接池初始化时创建的连接数,default : 3(建议使用)
minPoolSize:连接池保持的最小连接数,default : 3(建议使用)
maxPoolSize:连接池中拥有的最大连接数,如果获得新连接时会使连接总数超过这个值则不会再获取新连接,而是等待其他连接释放,所以这个值有可能会设计地很大,default : 15(建议使用)
acquireIncrement:连接池在无空闲连接可用时一次性创建的新数据库连接数,default : 3(建议使用)
2.管理连接池的大小和连接的生存时间
maxConnectionAge:配置连接的生存时间,超过这个时间的连接将由连接池自动断开丢弃掉。当然正在使用的连接不会马上断开,而是等待它close再断开。配置为0的时候则不会对连接的生存时间进行限制。default : 0 单位 s(不建议使用)
maxIdleTime:连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接。如果为0,则永远不会断开连接,即回收此连接。default : 0 单位 s(建议使用)
maxIdleTimeExcessConnections:这个配置主要是为了快速减轻连接池的负载,比如连接池中连接数因为某次数据访问高峰导致创建了很多数据连接,但是后面的时间段需要的数据库连接数很少,需要快速释放,必须小于maxIdleTime。其实这个没必要配置,maxIdleTime已经配置了。default : 0 单位 s(不建议使用)
3.配置连接测试:
automaticTestTable:配置一个表名,连接池根据这个表名用自己的测试sql语句在这个空表上测试数据库连接,这个表只能由c3p0来使用,用户不能操作。default : null(不建议使用)
preferredTestQuery:与上面的automaticTestTable二者只能选一。自己实现一条SQL检测语句。default : null(建议使用)
idleConnectionTestPeriod:用来配置测试空闲连接的间隔时间。测试方式还是上面的两种之一,可以用来解决MySQL8小时断开连接的问题。因为它保证连接池会每隔一定时间对空闲连接进行一次测试,从而保证有效的空闲连接能每隔一定时间访问一次数据库,将于MySQL8小时无会话的状态打破。为0则不测试。default : 0(建议使用)
testConnectionOnCheckin:如果为true,则在close的时候测试连接的有效性。default : false(不建议使用)
testConnectionOnCheckout:性能消耗大。如果为true,在每次getConnection的时候都会测试,为了提高性能,尽量不要用。default : false(不建议使用)
4.配置PreparedStatement缓存:
maxStatements:连接池为数据源缓存的PreparedStatement的总数。由于PreparedStatement属于单个Connection,所以这个数量应该根据应用中平均连接数乘以每个连接的平均PreparedStatement来计算。同时maxStatementsPerConnection的配置无效。default : 0(不建议使用)
maxStatementsPerConnection:连接池为数据源单个Connection缓存的PreparedStatement数,这个配置比maxStatements更有意义,因为它缓存的服务对象是单个数据连接,如果设置的好,肯定是可以提高性能的。为0的时候不缓存。default : 0(看情况而论)
5.重连相关配置
acquireRetryAttempts:连接池在获得新连接失败时重试的次数,如果小于等于0则无限重试直至连接获得成功。default : 30(建议使用)
acquireRetryDelay:连接池在获得新连接时的间隔时间。default : 1000 单位ms(建议使用)
breakAfterAcquireFailure:如果为true,则当连接获取失败时自动关闭数据源,除非重新启动应用程序。所以一般不用。default : false(不建议使用)
checkoutTimeout:配置当连接池所有连接用完时应用程序getConnection的等待时间。为0则无限等待直至有其他连接释放或者创建新的连接,不为0则当时间到的时候如果仍没有获得连接,则会抛出SQLException。其实就是acquireRetryAttempts*acquireRetryDelay。default : 0(与上面两个,有重复,选择其中两个都行)
6.定制管理Connection的生命周期
connectionCustomizerClassName:用来定制Connection的管理,比如在Connection acquire 的时候设定Connection的隔离级别,或者在Connection丢弃的时候进行资源关闭,
就可以通过继承一个AbstractConnectionCustomizer来实现相关方法,配置的时候使用全类名。有点类似监听器的作用。default : null(不建议使用)
7.配置未提交的事务处理
autoCommitOnClose:连接池在回收数据库连接时是否自动提交事务。如果为false,则会回滚未提交的事务,如果为true,则会自动提交事务。default : false(不建议使用)
forceIgnoreUnresolvedTransactions:这个配置强烈不建议为true。default : false(不建议使用)
一般来说事务当然由自己关闭了,为什么要让连接池来处理这种不细心问题呢?
8.配置debug和回收Connection
unreturnedConnectionTimeout:为0的时候要求所有的Connection在应用程序中必须关闭。如果不为0,则强制在设定的时间到达后回收Connection,所以必须小心设置,保证在回收之前所有数据库操作都能够完成。这种限制减少Connection未关闭情况的不是很适用。建议手动关闭。default : 0 单位 s(不建议使用)
debugUnreturnedConnectionStackTraces:如果为true并且unreturnedConnectionTimeout设为大于0的值,当所有被getConnection出去的连接unreturnedConnectionTimeout时间到的时候,就会打印出堆栈信息。只能在debug模式下适用,因为打印堆栈信息会减慢getConnection的速度default : false(不建议使用)
其他配置项:因为有些配置项几乎没有自己配置的必要,使用默认值就好,所以没有再写出来。
三、示例:
示例采用第二种方式:
1.c3p0.properties:
- #驱动
- c3p0.driverClass=com.mysql.jdbc.Driver
- #地址
- c3p0.jdbcUrl=jdbc:mysql://localhost:3306/jdbc
- #用户名
- c3p0.user=root
- #密码
- c3p0.password=lovejava
- #-------------------------------
- #连接池初始化时创建的连接数
- c3p0.initialPoolSize=3
- #连接池保持的最小连接数
- c3p0.minPoolSize=3
- #连接池在无空闲连接可用时一次性创建的新数据库连接数,default:3
- c3p0.acquireIncrement=3
- #连接池中拥有的最大连接数,如果获得新连接时会使连接总数超过这个值则不会再获取新连接,而是等待其他连接释放,所以这个值有可能会设计地很大,default : 15
- c3p0.maxPoolSize=15
- #连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接,单位秒
- c3p0.maxIdleTime=100
- #连接池在获得新连接失败时重试的次数,如果小于等于0则无限重试直至连接获得成功
- c3p0.acquireRetryAttempts=30
- #连接池在获得新连接时的间隔时间
- c3p0.acquireRetryDelay=1000
2.ConnectionPool
- package com.study.pool;
- import java.sql.Connection;
- import java.sql.SQLException;
- import javax.sql.DataSource;
- import com.mchange.v2.c3p0.ComboPooledDataSource;
- public class ConnectionPool {
- private DataSource ds;
- private static ConnectionPool pool;
- private ConnectionPool(){
- ds = new ComboPooledDataSource();
- }
- public static final ConnectionPool getInstance(){
- if(pool==null){
- try{
- pool = new ConnectionPool();
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
- return pool;
- }
- public synchronized final Connection getConnection() {
- try {
- return ds.getConnection();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return null;
- }
- }
3.PoolThread
- package com.study.pool;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- public class PoolThread extends Thread {
- @Override
- public void run(){
- ConnectionPool pool = ConnectionPool.getInstance();
- Connection con = null;
- PreparedStatement stmt= null;
- ResultSet rs = null;
- try{
- con = pool.getConnection();
- stmt = con.prepareStatement("select sysdate as nowtime from dual");
- rs = stmt.executeQuery();
- while(rs.next()){
- System.out.println(Thread.currentThread().getId()+"---------------开始"+rs.getString("nowtime"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- }finally{
- try {
- rs.close();
- stmt.close();
- con.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- System.out.println(Thread.currentThread().getId()+"--------结束");
- }
- }
4.PoolMain
- package com.study.pool;
- public class PoolMain {
- /**
- * 数据源缓冲池 实例练习
- */
- public static void main(String[] args) {
- System.out.println("缓冲池模拟开始");
- PoolThread[] threads = new PoolThread[50];
- for(int i=0;i<threads.length;i++){
- threads[i] = new PoolThread();
- }
- for(int i=0;i<threads.length;i++){
- threads[i].start();
- }
- }
- }
Druid是阿里巴巴的一个开源项目,据该网站称:Druid首先是一个数据库连接池。Druid是目前最好的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。
上述内容难免有水分,但是如果真如他所说Druid已经在阿里巴巴和淘宝等大型网站及系统上被充分使用及测试的话,那么Druid还是值得信赖的,毕竟任何有信誉公司不会拿这个去开玩笑。
Druid官网:http://code.alibabatech.com/wiki/display/Druid/Home
Druid有以下几点优势或好处:
- 替换DBCP和C3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。
- 可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。
- 数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。
- SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-Logging、Log4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。
- 扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter机制,很方便编写JDBC层的扩展插件。
看到上面的这些Druid的好处你是否已经心动了?下面就让我们一起来实验一下。
1.下载Druid(http://code.alibabatech.com/mvn/releases/com/alibaba/druid/),目前Druid最新版本为0.2.23,可以选择源码,javadoc,jar等。
2.还是在之前举例的项目基础上添加Druid代码,代码其实很简单,把之前的c3p0改改就能用:
- /**
- * Druid
- */
- public void getUsrInfoWithDruid(Integer usrId) {
- String sql="SELECT * FROM USER u WHERE u.USR_ID=" + usrId;
- try {
- //通过Map方式设置Druid参数
- Map<String, String> druidMap=new HashMap<String, String>();
- druidMap.put(DruidDataSourceFactory.PROP_USERNAME, user);
- druidMap.put(DruidDataSourceFactory.PROP_PASSWORD, passwd);
- druidMap.put(DruidDataSourceFactory.PROP_URL, jdbcUrl);
- druidMap.put(DruidDataSourceFactory.PROP_DRIVERCLASSNAME, driver);
- //通过DruidDataSourceFactory获取DataSource实例
- dataSource=DruidDataSourceFactory.createDataSource(druidMap);
- conn=dataSource.getConnection();
- Statement st=conn.createStatement();
- ResultSet result=st.executeQuery(sql);
- while(result.next()) {
- System.out.println("Druid:begin");
- System.out.println("Name:" + result.getString("NAME"));
- System.out.println("Druid:end");
- }
- result.close();
- st.close();
- conn.close();
- } catch(Exception e) {
- e.printStackTrace();
- }
- }
以上是利用Map方式向Druid传递参数,这种方式基本上用不到。
3.利用属性文件传递参数:
- #MySQL驱动
- druid.driverClassName=com.mysql.jdbc.Driver
- #MySQL连接地址
- druid.url=jdbc\:mysql\://192.168.0.8\:3306/test
- #数据库用户
- druid.username=moster
- #该用户密码
- druid.password=shzygjrmdwg
- #自动提交
- druid.defaultAutoCommit=false
- #只读设置
- druid.defaultReadOnly=false
- druiddruid.defaultTransactionIsolation=
- druid.defaultCatalog=
- #连接池最大使用连接数量
- druid.maxActive=25
- #连接池最大空闲
- druid.maxIdle=25
- #连接池最小空闲
- druid.minIdle=1
- #初始化连接池大小
- druid.initialSize=1
- #连接等待时间,默认:-1
- druid.maxWait=3000
- druiddruid.testOnBorrow=
- druid.testOnReturn=
- #检测间隔,检测需要关闭的空闲连接,单位:毫秒
- druiddruid.timeBetweenEvictionRunsMillis=
- druid.numTestsPerEvictionRun=
- #一个连接在池中最小生存的时间,单位:毫秒
- druiddruid.minEvictableIdleTimeMillis=
- druid.testWhileIdle=
- #测试SQL
- druid.validationQuery=
- #测试超时时间
- druid.validationQueryTimeout=
- #初始化连接SQL
- druiddruid.initConnectionSqls=
- druid.accessToUnderlyingConnectionAllowed=
- #移除被废弃连接
- druid.removeAbandoned=
- #超时时间
- druid.removeAbandonedTimeout=
- #日志记录
- druid.logAbandoned=
- #PreparedStatements
- druid.poolPreparedStatements=
- #PreparedStatement最大数量
- druid.maxOpenPreparedStatements=
- #属性配置文件
- druid.connectionProperties=
- #filters配置
- druid.filters=
- #Exception处理
- druid.exceptionSorter=
- #Exception处理类名
- druid.exception-sorter-class-name=
- #初始化
- druid.init=
官网居然连这些属性的解释都没有,以上是我自己翻译的,如有偏差请及时指正。
4.将原创建DataSource方法修改为:
- Properties p=new Properties();
- p.load(getClass().getResourceAsStream("/druid.properties"));
- //通过属性文件设置Druid参数
- dataSource=DruidDataSourceFactory.createDataSource(p);
Druid的参数可以参考DruidDataSourceFactory类的源码,其中参数的命名规则与c3p0,DBCP非常像,也比较容易理解,以下是具体参数:
- String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit";
- String PROP_DEFAULTREADONLY = "defaultReadOnly";
- String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation";
- String PROP_DEFAULTCATALOG = "defaultCatalog";
- String PROP_DRIVERCLASSNAME = "driverClassName";
- String PROP_MAXACTIVE = "maxActive";
- String PROP_MAXIDLE = "maxIdle";
- String PROP_MINIDLE = "minIdle";
- String PROP_INITIALSIZE = "initialSize";
- String PROP_MAXWAIT = "maxWait";
- String PROP_TESTONBORROW = "testOnBorrow";
- String PROP_TESTONRETURN = "testOnReturn";
- String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis";
- String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun";
- String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis";
- String PROP_TESTWHILEIDLE = "testWhileIdle";
- String PROP_PASSWORD = "password";
- String PROP_URL = "url";
- String PROP_USERNAME = "username";
- String PROP_VALIDATIONQUERY = "validationQuery";
- String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout";
- String PROP_INITCONNECTIONSQLS = "initConnectionSqls";
- String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed";
- String PROP_REMOVEABANDONED = "removeAbandoned";
- String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout";
- String PROP_LOGABANDONED = "logAbandoned";
- String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements";
- String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements";
- String PROP_CONNECTIONPROPERTIES = "connectionProperties";
- String PROP_FILTERS = "filters";
- String PROP_EXCEPTION_SORTER = "exceptionSorter";
- String PROP_EXCEPTION_SORTER_CLASS_NAME = "exception-sorter-class-name";
- String PROP_INIT = "init";
Druid监控:
同Proxool一样Druid也提供了监控功能,而且更强大!
Druid的监控配置起来跟Proxool类似,其实我的感觉就是Druid把现在主流的连接池优点结合起来了,很有中国人做东西的“风格”。
- <!-- Druid监控Servlet -->
- <servlet>
- <servlet-name>DruidStatView</servlet-name>
- <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>DruidStatView</servlet-name>
- <url-pattern>/druid/*</url-pattern>
- </servlet-mapping>
打开浏览器就可以看到监控页面了。
下图是Druid官网给出的几种连接池的对比表格,从数据上来看Druid在各方面都占有很大的优势,实际情况还需要实践去检验。
LRU
LRU是一个性能关键指标,特别Oracle,每个Connection对应数据库端的一个进程,如果数据库连接池遵从LRU,有助于数据库服务器优化,这是重要的指标。在测试中,Druid、DBCP、Proxool、JBoss是遵守LRU的。BoneCP、C3P0则不是。BoneCP在mock环境下性能可能好,但在真实环境中则就不好了。
PSCache
PSCache是数据库连接池的关键指标。在Oracle中,类似SELECT NAME FROM USER WHERE ID = ?这样的SQL,启用PSCache和不启用PSCache的性能可能是相差一个数量级的。Proxool是不支持PSCache的数据库连接池,如果你使用Oracle、SQL Server、DB2、Sybase这样支持游标的数据库,那你就完全不用考虑Proxool。
PSCache-Oracle-Optimized
Oracle 10系列的Driver,如果开启PSCache,会占用大量的内存,必须做特别的处理,启用内部的EnterImplicitCache等方法优化才能够减少内存的占用。这个功能只有DruidDataSource有。如果你使用的是Oracle Jdbc,你应该毫不犹豫采用DruidDataSource。
ExceptionSorter
ExceptionSorter是一个很重要的容错特性,如果一个连接产生了一个不可恢复的错误,必须立刻从连接池中去掉,否则会连续产生大量错误。这个特性,目前只有JBossDataSource和Druid实现。Druid的实现参考自JBossDataSource。
监控
DruidDataSource自身提供有NotEmptyWaitCount、PSCahcheHitCount等有用的监控属性,通过配置StatFilter能够监控SQL的执行情况。
扩展
DruidDataSource提供基于Filter-Chain模式的模式的扩展。