Tomcat JNDI 连接池部署:三步从零拿到数据库连接
【免费下载链接】tomcatApache Tomcat项目地址: https://gitcode.com/gh_mirrors/tom/tomcat
如果你接手 Tomcat 应用需要连数据库,这篇文章帮你完成 Tomcat JNDI 连接池部署:只在服务器端配置一次数据源,应用通过 JNDI 名称从连接池取连接。不用在代码里硬编码账号密码,也不用每个请求都新建连接。
🎯 连接池解决哪三个真实问题
线上跑 Tomcat 应用,数据库相关痛点基本逃不出这三类:
| 常见现象 | 背后的代价 |
|---|---|
| 每个请求新建一条 Connection | 每次握手、认证都耗时,并发一上来吞吐骤降 |
| 用户名密码硬编码在 Java 代码里 | 换密码要改代码、重新发布 |
| 并发下报"等待连接超时" | 连接被耗尽,用户直接看到 500 |
解决思路一致:把数据源定义交给容器配置,应用只用 JNDI 名称(java:comp/env/jdbc/TestDB)查找。池的容量、生命周期、监控全部由 Tomcat 统一管理。
📦 准备环境:把两个 jar 放对位置
认清目录结构
Tomcat 安装后只需熟悉两个目录:lib/是服务器级类路径,放进去的 jar 对所有应用可见;webapps/放应用。本地还没有 Tomcat 的话,先克隆源码并按 BUILDING.txt 构建:git clone https://gitcode.com/gh_mirrors/tom/tomcat。
放置连接池实现与数据库驱动
需要放进lib/的是两个东西:tomcat-jdbc 连接池(本仓库 modules/jdbc-pool/ 下就是它的源码,直接用发行版打好的 jar 即可)和数据库驱动 jar(如 MySQL Connector/J)。注意 lib 下的 jar 在启动时加载,放完必须重启 Tomcat 才生效。
⚙️ 在 context.xml 里声明 数据源
该改哪个文件
全局默认配置在conf/context.xml,对每个应用生效;也可以给单个应用建独立 context 文件放在conf/Catalina/localhost/下。二选一,两处重名时以单应用文件为准,混用容易互相覆盖。
写入数据源声明
下面是最小可用的一段声明,放在<Context>节点内,重启后生效:
<Context> <Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource" driverClassName="com.mysql.cj.jdbc.Driver" url="jdbc:mysql://localhost:3306/javatest" username="javauser" password="javadude" maxTotal="50" maxIdle="10" maxWaitMillis="10000"/> </Context>池参数最常调的是这三个:
| 参数 | 作用 |
|---|---|
| maxTotal | 池中连接数上限,不要超过数据库 max_connections |
| maxIdle | 保留的空闲连接上限 |
| maxWaitMillis | 拿不到连接时的最长等待毫秒数,超时抛异常 |
Oracle、PostgreSQL 等其他数据库的差异写法,可查仓库内 webapps/docs/jndi-datasource-examples-howto.xml。
✅ 跑一个 JSP 页面验证连接池通了
在 web.xml 声明资源引用
应用侧必须声明资源引用,这一步漏掉,后面的 JNDI 查找必定失败:
<resource-ref> <res-ref-name>jdbc/TestDB</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>写验证页并访问
把下面的页面放进应用根目录,重启 Tomcat,浏览器访问该 JSP:
<%@ page import="java.sql.*, javax.naming.*, javax.sql.*" %> <% Context env = new InitialContext().getContext("java:comp/env"); DataSource ds = (DataSource) env.lookup("jdbc/TestDB"); try (Connection con = ds.getConnection(); Statement st = con.createStatement(); ResultSet rs = st.executeQuery("select 1")) { out.println(rs.next() ? "Pool OK" : "No result"); } %>跑通后的样子
页面输出Pool OK,catalina.out 日志里能看到应用 context 正常启动。此时这条连接来自池,借出和归还都由容器完成,应用代码不再关心连接的创建成本。
🛠️ 三个常见报错与修复
NameNotFoundException:先查两处名字是否一致
症状:lookup 抛 NameNotFoundException,提示找不到 jdbc/TestDB。 原因:web.xml 里 res-ref-name 与 context.xml 里 Resource 的 name 不一致,或者根本没写 resource-ref。 修复:分别打开两个文件核对名字是否逐字符一致,缺声明的补上再重启。
Communications link failure:jar 与类名要配齐
症状:获取连接失败,日志出现 Communications link failure 或 No suitable driver。 原因:驱动 jar 没放进 lib/,或 driverClassName 写错——旧版驱动是 com.mysql.jdbc.Driver,现行 Connector/J 应为 com.mysql.cj.jdbc.Driver。 修复:确认驱动 jar 已复制到 lib/ 并重启,把类名改对。
等待连接超时:多半是泄漏不是池小
症状:系统平时正常,跑一段时间后突然报 timeout waiting for connection。 原因:某条代码路径没关闭 Connection 或 ResultSet,池子被慢慢耗尽。 修复:排查代码确认用 try-with-resources;必要时在 Resource 上加removeAbandonedOnBorrow="true"让容器强制回收泄漏连接并打印堆栈,定位是哪段代码没还连接。
🔧 接下来可以做什么
- 监控池状态:tomcat-jdbc 把池暴露为 MBean,通过 JMX 工具可实时看空闲与在用连接数。
- 拦截慢查询:modules/jdbc-pool/ 内置 SlowQueryReport 拦截器,可记录慢 SQL 输出到日志。
- 多应用共享:多个应用连同一个库时,把资源挪到conf/server.xml的
<GlobalNamingResources>下定义成全局资源,避免多个池重复占连接。
验证页输出 Pool OK 之后,把select 1换成你的真实查询,这套 Tomcat JNDI 连接池就算完整落地了。
【免费下载链接】tomcatApache Tomcat项目地址: https://gitcode.com/gh_mirrors/tom/tomcat
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考