本教程向您展示如何在 Tomcat 中创建表示 JDBC 数据源的 JNDI 资源,然后如何配置 Java Web 应用程序以访问 JNDI 数据源。使用 JNDI 数据源的好处是:

  • 利用容器提供的数据库连接池服务,即Tomcat使用tomcat-jdbc作为实现。
  • 外部化数据库连接并使其独立于 Web 应用程序本身。
  • 在容器中部署的应用程序之间共享数据库连接。

以下示例在 Tomcat 9 和 MySQL Database 8 中进行了测试。

1. MySQL 数据库示例

首先,我们需要创建一个示例数据库。让我们执行以下 MySQL 脚本:

create database usersdb;

use usersdb;

CREATE TABLE `users` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(45) NOT NULL,
  `password` varchar(45) NOT NULL,
  `email` varchar(45) NOT NULL,
  PRIMARY KEY (`user_id`)
);

这将创建一个名为usersdb的数据库和一个名为users的表。请记住在此表中插入一些虚拟数据。

要从 Java 应用程序与 MySQL 数据库交互,MySQL Connector/J库必须存在于类路径中。在这里,我们需要将mysql-connector-java-VERSION-bin.jar文件复制到$CATALINA_BASE/lib目录下。如果您的计算机上只有一个 Tomcat 实例,则$CATALINA_BASE是 Tomcat 的安装目录,例如Windows 平台上的c:\Program Files\Apache Software Foundation\Tomcat 9.0。这样做有助于 Tomcat 在发现 JNDI 数据源配置时加载 MySQL JDBC 驱动程序。

2.配置上下文

要为上面的 MySQL 数据库声明 JNDI 数据源,请创建一个具有以下内容的Resource XML 元素:

<?xml version='1.0' encoding='utf-8'?>
<Context path="/JNDIDataSourceExample">
    <Resource
        name="jdbc/UsersDB"
        auth="Container"
        type="javax.sql.DataSource"
        factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
        maxActive="100"
        maxIdle="30"
        maxWait="10000"
        driverClassName="com.mysql.cj.jdbc.Driver"
        url="jdbc:mysql://localhost:3306/usersDB"
        username="root"
        password="root"
    />
</Context>

context.xml文件的根元素<Context>中添加此元素。context.xml文件可以驻留在两个位置(如果不存在,则创建一个):

  • 在Web 应用程序的/META-INF目录中:JNDI 数据源仅对应用程序本身可用,因此不能与其他应用程序共享。此外,这使得配置依赖于应用程序。
  • $CATALINA_BASE/conf目录中:这是首选位置,因为 JNDI 数据源将可用于所有 Web 应用程序并且它独立于任何应用程序。

因此,我们在$CATALINA_BASE/conf目录下的context.xml文件中声明上述Resource元素。下表描述了上述配置中指定的属性:

属性名称

描述

姓名

资源的名称。

授权

指定应用代码的认证机制,可以是Application或Container。

类型

Web 应用程序在查找此资源时所期望的完全限定的 Java 类名。

最大活动

池中的最大数据库连接数。设置为 -1 表示没有限制。

最大空闲

保留在池中的最大空闲数据库连接数。设置为 -1 表示没有限制。

最大等待

等待数据库连接可用的最长时间(以毫秒为单位),在本示例中为 10 秒。如果超过此超时,则会引发异常。设置为 -1 以无限期等待。

驱动程序类名

数据库驱动程序的完全限定 Java 类名。对于 MySQL Connector/J,它是com.mysql.jdbc.Driver。

网址

JDBC 连接 URL。

用户名

MySQL 数据库用户名。

密码

MySQL 数据库用户密码。

有关属性的更多信息,请访问本教程末尾提到的参考链接。

笔记:

  • 如果您在 Eclipse IDE 中使用 Tomcat,则需要修改Servers项目下的context.xml文件。那是因为 Eclipse 复制了 Tomcat 配置:

  • 如果在 web 应用程序的META-INF目录和$CATALINA_BASE/conf目录下的context.xml文件中都声明了两个具有相同名称的资源,则内部版本优先。

3.配置web.xml

将以下声明添加到web.xml文件中:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
    <display-name>JNDIDataSourceExample</display-name>
    <resource-ref>
        <description>DB Connection</description>
        <res-ref-name>jdbc/UsersDB</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>
</web-app>

为了使 JNDI DataSource 可用于指定命名空间jdbc/UsersDB下的应用程序,这是必需的。

4.编写一个测试JSP页面

现在,创建一个 JSP 页面 ( UsersList.jsp ) 来测试我们所做的配置:

<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<sql:query var="listUsers" dataSource="jdbc/UsersDB">
	select username, email from users;
</sql:query>
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
	"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Users List</title>
</head>
<body>
	<div align="center">
		<table border="1" cellpadding="5">
			<caption><h2>List of users</h2></caption>
			<tr>
				<th>Name</th>
				<th>Email</th>
			</tr>
			<c:forEach var="user" items="${listUsers.rows}">
				<tr>
					<td><c:out value="${user.username}" /></td>
					<td><c:out value="${user.email}" /></td>
				</tr>
			</c:forEach>
		</table>
	</div>
</body>
</html>

在这里,我们使用JSTL 的 SQL 标记查询对数据库进行 SELECT 查询。请注意,dataSource属性是指在web.xml文件中声明的 JNDI 资源名称:

<sql:query var="listUsers" dataSource="jdbc/UsersDB">
	select username, email from users;
</sql:query>
这是加载 JSP 页面 (  http://localhost:8080/JNDIDataSourceExample/UsersList.jsp ) 时的示例输出:

5. 编写一个测试 Java servlet

我们可以使用 Java 代码查找配置的 JNDI 数据源,如下所示:

			Context initContext = new InitialContext();
			Context envContext = (Context) initContext.lookup("java:comp/env");
			DataSource ds = (DataSource) envContext.lookup("jdbc/UsersDB");
			Connection conn = ds.getConnection();

获取到连接后,我们可以将其作为简单的 JDBC 代码使用:

			Statement statement = conn.createStatement();
			String sql = "select username, email from users";
			ResultSet rs = statement.executeQuery(sql);
            // iterates over the result set...

这是示例 Java servlet 的源代码:

package net.codejava.jdbc;

import java.io.IOException;
import java.io.PrintWriter;
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.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

/**
 * This servlet class demonstrates how to access a JNDI DataSource that 
 * represents a JDBC connection.
 * @author www.codejava.net
 */
@WebServlet("/listUsers")
public class UsersListServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		PrintWriter writer = response.getWriter();
		try {
			Context initContext = new InitialContext();
			Context envContext = (Context) initContext.lookup("java:comp/env");
			DataSource ds = (DataSource) envContext.lookup("jdbc/UsersDB");
			Connection conn = ds.getConnection();
			
			Statement statement = conn.createStatement();
			String sql = "select username, email from users";
			ResultSet rs = statement.executeQuery(sql);
			
			int count = 1;
			while (rs.next()) {
				writer.println(String.format("User #%d: %-15s %s", count++, 
						rs.getString("username"), rs.getString("email")));
				
			}
		} catch (NamingException ex) {
			System.err.println(ex);
		} catch (SQLException ex) {
			System.err.println(ex);
		}
	}

}
以下是调用 servlet (  http://localhost:8080/JNDIDataSourceExample/listUsers ) 时的示例输出:

或者,我们可以使用@Resource注释 ( javax.annotation.Resource ) 而不是上面的查找代码。例如,在 servlet 中声明一个名为dataSource的字段,如下所示:

    @Resource(name = "jdbc/UsersDB")
    private DataSource dataSource;

Tomcat 会在发现这个注解时查找指定的资源名称并注入一个实际的实现。因此,servlet 代码如下所示:

package net.codejava.jdbc;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.annotation.Resource;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

/**
 * This servlet class demonstrates how to access a JNDI DataSource that
 * represents a JDBC connection.
 *
 * @author www.codejava.net
 */
@WebServlet("/listUsers")
public class UsersListServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Resource(name = "jdbc/UsersDB")
    private DataSource dataSource;

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer = response.getWriter();
        try {
            Connection conn = dataSource.getConnection();

            Statement statement = conn.createStatement();
            String sql = "select username, email from users";
            ResultSet rs = statement.executeQuery(sql);

            int count = 1;

            while (rs.next()) {
                writer.println(String.format("User #%d: %-15s %s", count++,
                        rs.getString("username"), rs.getString("email")));

            }
        } catch (SQLException ex) {
            System.err.println(ex);
        }
    }

}

参考:

附件:
JNDIDataSourceExample.zip【Eclipse项目】393 KB

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐