JavaWeb学习总结(十三)--数据库连接池
一、数据库连接池的概念#
用池来管理Connection,这可以重复使用Connection。有了池,所以我们就不用自己来创建Connection,而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。池就可以再利用这个Connection对象了。
JDBC数据库连接池接口(DataSource)#
Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商可以让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
开源数据库连接池#
现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:
- DBCP 数据库连接池
- C3P0 数据库连接池
在使用了数据库连接池之后,在项目的实际开发中就不需要编写连接数据库的代码了,直接从数据源获得数据库的连接。
JDBC数据库连接池接口(DataSource)
Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商可以让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
二、DBCP#
DBCP是Apache提供的一款开源免费的数据库连接池!Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。Hibernate3.0之后不再对DBCP提供支持!因为Hibernate声明DBCP有致命的缺欠!
需要应用程序应在系统中增加如下两个 jar 文件:
- Commons-dbcp.jar:连接池的实现
- Commons-pool.jar:连接池实现的依赖库
实现DBCP#
1. 加入jar包#
2.编写dbcpconfig.properties#
#连接设置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/testdb username=root password=123456 #<!-- 初始化连接 --> initialSize=10 #最大连接数量 maxActive=50 #<!-- 最大空闲连接 --> maxIdle=20 #<!-- 最小空闲连接 --> minIdle=5 #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --> maxWait=60000 #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。 connectionProperties=useUnicode=true;characterEncoding=UTF8 #指定由连接池所创建的连接的自动提交(auto-commit)状态。 defaultAutoCommit=true #driver default 指定由连接池所创建的连接的只读(read-only)状态。 #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix) defaultReadOnly=false #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。 #可用值为下列之一:NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE defaultTransactionIsolation=REPEATABLE_READ
如下图所示:
3、在获取数据库连接的工具类(如jdbcUtils)的静态代码块中创建池#
package cn.zy.utils; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; /* * 数据库连接工具类DBCP */ public class JdbcUtils_DBCP { /* * 在java中,编写数据库连接池需实现java.sql.DataSource接口,每一种数据库连接池都是DataSource接口的实现 * DBCP连接池就是javax.sql.DataSource接口的一个具体实现 */ private static DataSource ds =null; //在静态代码块中创建数据库连接池 static { try { //加载配置文件 InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"); Properties prop = new Properties(); prop.load(in); //创建数据源 ds = BasicDataSourceFactory.createDataSource(prop); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } /* * 从数据源中获取数据库连接 */ public static Connection getConnection() throws SQLException{ return ds.getConnection(); }
/* * 释放资源 */ public static void release(Connection conn,Statement st,ResultSet rs){ if(rs!=null){ try{ rs.close(); }catch (Exception e){ e.printStackTrace(); } if(st!=null){ try { st.close(); } catch (Exception e) { e.printStackTrace(); } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } }
测试连接
package cn.zy.test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import org.junit.Test; import cn.zy.utils.JdbcUtils_DBCP; /* * 测试DBCP数据源 */ public class DbcpTest { @Test public void dbcpDataSourceTest(){ Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { //获取数据库连接 conn = JdbcUtils_DBCP.getConnection(); String sql = "insert into account(name,money) values(?,?)"; st = conn.prepareStatement(sql); st.setString(1, "D"); st.setFloat(2, 2000); st.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally{ //释放资源 JdbcUtils_DBCP.release(conn, st, rs); } } }
三、C3P0数据源#
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。C3P0数据源在项目开发中使用得比较多。
- dbcp没有自动回收空闲连接的功能
- c3p0有自动回收空闲连接功能
在应用程序中加入C3P0连接池#
1.导入相关jar包#
c3p0-0.9.2-pre1.jar、mchange-commons-0.2.jar,如果操作的是Oracle数据库,那么还需要导入c3p0-oracle-thin-extras-0.9.2-pre1.jar
2、在src下加入C3P0的配置文件:c3p0-config.xml,注意:
配置文件要求:
l 文件名称:必须叫c3p0-config.xml
l 文件位置:必须在src下
c3p0-config.xml的配置信息如下:
<?xml version="1.0" encoding="UTF-8"?> <!-- c3p0-config.xml必须位于类路径下面 private static ComboPooledDataSource ds; static{ try { ds = new ComboPooledDataSource("MySQL"); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } --> <c3p0-config> <!-- C3P0的缺省(默认)配置, 如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource();”这样写就表示使用的是C3P0的缺省(默认)配置信息来创建数据源 --> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/testdb</property> <property name="user">root</property> <property name="password">123456</property> <property name="acquireIncrement">5</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property> </default-config> <!-- C3P0的命名配置, 如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource("MySQL");”这样写就表示使用的是name是MySQL的配置信息来创建数据源 --> <named-config name="MySQL"> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/testdb</property> <property name="user">root</property> <property name="password">123456</property> <property name="acquireIncrement">5</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property> </named-config> </c3p0-config>
如图:
3、在获取数据库连接的工具类(如jdbcUtils)的静态代码块中创建池
1 package cn.zy.utils; 2 3 import java.sql.Connection; 4 import java.sql.ResultSet; 5 import java.sql.SQLException; 6 import java.sql.Statement; 7 8 import javax.sql.DataSource; 9 10 11 import com.mchange.v2.c3p0.ComboPooledDataSource; 12 13 /* 14 * 数据库连接工具类 15 */ 16 public class JdbcUtils_C3P0 { 17 private static ComboPooledDataSource dataSource = null; 18 //在静态块中创建数据库连接池 19 static{ 20 try { 21 //通过代码创建数据库连接池 22 /*ds = new ComboPooledDataSource(); 23 ds.setDriverClass("com.mysql.jdbc.Driver"); 24 ds.setJdbcUrl("jdbc:mysql://localhost:3306/testdb"); 25 ds.setUser("root"); 26 ds.setPassword("123456"); 27 ds.setInitialPoolSize(10); 28 ds.setMinPoolSize(5); 29 ds.setMinPoolSize(20);*/ 30 31 //通过读取xml来获取数据源 32 //ds = new ComboPooledDataSource();//使用C3P0的默认配置来创建数据源 33 dataSource = new ComboPooledDataSource("MySQL");//使用C3P0的命名配置来创建数据源 34 } catch (Exception e) { 35 throw new ExceptionInInitializerError(e); 36 } 37 } 38 39 /* 40 * 从数据源中获取数据库连接 41 */ 42 public static Connection getConnection() throws SQLException { 43 return dataSource.getConnection(); 44 } 45 46 /* 47 * 返回连接池 48 */ 49 public static DataSource getDataSource() { 50 return dataSource; 51 } 52 /* 53 * 释放资源 54 */ 55 public static void release(Connection conn,Statement st,ResultSet rs){ 56 if(rs!=null){ 57 try{ 58 //关闭存储查询结果的ResultSet对象 59 rs.close(); 60 }catch (Exception e) { 61 e.printStackTrace(); 62 } 63 rs = null; 64 } 65 if(st!=null){ 66 try{ 67 //关闭负责执行SQL命令的Statement对象 68 st.close(); 69 }catch (Exception e) { 70 e.printStackTrace(); 71 } 72 } 73 74 if(conn!=null){ 75 try{ 76 //将Connection连接对象还给数据库连接池 77 conn.close(); 78 }catch (Exception e) { 79 e.printStackTrace(); 80 } 81 } 82 } 83 } 84
编写测试类:
package cn.zy.test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import org.junit.Test; import cn.zy.utils.JdbcUtils_C3P0; public class C3p0Test { @Test public void fun(){ Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils_C3P0.getConnection(); String sql = "insert into account(name,money) values(?,?)"; st = conn.prepareStatement(sql); st.setString(1, "F"); st.setFloat(2,3000); st.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } } }
四、配置Tomcat数据源#
在实际开发中,我们有时候还会使用服务器提供给我们的数据库连接池,比如我们希望Tomcat服务器在启动的时候可以帮我们创建一个数据库连接池,那么我们在应用程序中就不需要手动去创建数据库连接池,直接使用Tomcat服务器创建好的数据库连接池即可。要想让Tomcat服务器在启动的时候帮我们创建一个数据库连接池,那么需要简单配置一下Tomcat服务器。
4.1 Tomcat配置JNDI资源#
JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包,
这 套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需 通过名称检索即可。其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。
Tomcat服务器创建的数据源是以JNDI资源的形式发布的,所以说在Tomat服务器中配置一个数据源实际上就是在配置一个JNDI资源,通过查看Tomcat文档,我们知道使用如下的方式配置tomcat服务器的数据源:
<Context> <Resource name="jdbc/datasource" auth="Container" type="javax.sql.DataSource" username="root" password="123456" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/testdb" maxActive="8" maxIdle="4"/> </Context>
服务器创建好数据源之后,我们的应用程序又该怎么样得到这个数据源呢,Tomcat服务器创建好数据源之后是以JNDI的形式绑定到一个JNDI容器中的,我们可以把JNDI想象成一个大大的容器,我们可以往这个容器中存放一些对象,一些资源,JNDI容器中存放的对象和资源都会有一个独一无二的名称,应用程序想从JNDI容器中获取资源时,只需要告诉JNDI容器要获取的资源的名称,JNDI根据名称去找到对应的资源后返回给应用程序。我们平时做javaEE开发时,服务器会为我们的应用程序创建很多资源,比如request对象,response对象,服务器创建的这些资源有两种方式提供给我们的应用程序使用:第一种是通过方法参数的形式传递进来,比如我们在Servlet中写的doPost和doGet方法中使用到的request对象和response对象就是服务器以参数的形式传递给我们的。第二种就是JNDI的方式,服务器把创建好的资源绑定到JNDI容器中去,应用程序想要使用资源时,就直接从JNDI容器中获取相应的资源即可。
对于上面的name="jdbc/datasource"数据源资源,在应用程序中可以用如下的代码去获取
1 Context initCtx = new InitialContext(); 2 Context envCtx = (Context) initCtx.lookup("java:comp/env"); 3 dataSource = (DataSource)envCtx.lookup("jdbc/datasource");
此种配置下,数据库的驱动jar文件需放置在tomcat的lib下
4.2、配置Tomcat数据源#
1、在Web项目的WebRoot目录下的META-INF目录创建一个context.xml文件
2、在Web项目的WebRoot目录下的META-INF目录创建一个context.xml文件
<?xml version="1.0" encoding="UTF-8"?> <Context> <Resource name="jdbc/datasource" auth="Container" type="javax.sql.DataSource" username="root" password="123456" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/testdb" maxActive="8" maxIdle="4"/> </Context>
3、将数据库的驱动jar文件需放置在tomcat的lib下
4、在获取数据库连接的工具类(如jdbcUtils)的静态代码块中获取JNDI容器中的数据源
package cn.zy.utils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.naming.Context; import javax.naming.InitialContext; import javax.sql.DataSource; public class JdbcUtils_JNDI { private static DataSource ds = null; //在静态代码块中创建数据库连接池 static{ try { //初始化JNDI Context initCtx = new InitialContext(); //得到JNDI容器 Context envCtx = (Context) initCtx.lookup("java:comp/env"); //从JNDI容器中检索name为jdbc/datasource的数据源 ds = (DataSource)envCtx.lookup("jdbc/datasource"); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } /* * 从数据源中获取连接 */ public static Connection getConnection() throws SQLException{ return ds.getConnection(); } /* * 释放资源 */ public static void release(Connection conn,Statement st,ResultSet rs){ if(rs!=null){ try{ rs.close(); }catch (Exception e){ e.printStackTrace(); } if(st!=null){ try { st.close(); } catch (Exception e) { e.printStackTrace(); } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } }
写一个Servlet进行测试:
package cn.zy.test; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.zy.utils.JdbcUtils_JNDI; public class JNDITest extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { //获取连接 conn = JdbcUtils_JNDI.getConnection(); String sql = "insert into account(name,money) values(?,?)"; st = conn.prepareStatement(sql); st.setString(1, "F"); st.setFloat(2, 2000); st.executeUpdate(); response.getWriter().print("插入成功"); } catch (Exception e) { e.printStackTrace(); }finally{ JdbcUtils_JNDI.release(conn, st, rs); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了