Spring DataSource JNDI 与 Tomcat 示例
欢迎阅读 Spring DataSource JNDI Tomcat 示例教程。之前我们了解了如何使用Spring JDBC集成实现数据库操作。然而,大多数时候企业应用程序都部署在 servlet 容器中,例如 Tomcat、JBoss 等。
春季数据源
我们知道,使用 JNDI 的 DataSource是实现连接池和获得容器实现好处的首选方式。今天,我们将了解如何配置 Spring Web 应用程序以使用 Tomcat 提供的 JNDI 连接。作为示例,我将使用 MySQL 数据库服务器并创建一个包含一些行的简单表。我们将创建一个 Spring Rest Web 服务,它将返回包含表中所有数据列表的 JSON 响应。
数据库设置
CREATE TABLE `Employee` (
`id` int(11) unsigned NOT NULL,
`name` varchar(20) DEFAULT NULL,
`role` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `Employee` (`id`, `name`, `role`)
VALUES
(1, 'Pankaj', 'CEO'),
(2, 'David', 'Manager');
commit;
Spring DataSource MVC 项目
在 Spring Tool Suite 中创建一个 Spring MVC 项目,以便我们的 Spring 应用程序骨架代码准备就绪。一旦我们完成实施,我们的项目结构将如下图所示。
Spring JDBC 和 Jackson 依赖项
我们必须在 pom.xml 文件中添加 Spring JDBC、Jackson 和 MySQL 数据库驱动程序作为依赖项。我的最终 pom.xml 文件如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.spring</groupId>
<artifactId>SpringDataSource</artifactId>
<name>SpringDataSource</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.6</java-version>
<org.springframework-version>4.0.2.RELEASE</org.springframework-version>
<org.aspectj-version>1.7.4</org.aspectj-version>
<org.slf4j-version>1.7.5</org.slf4j-version>
<jackson.databind-version>2.2.3</jackson.databind-version>
</properties>
<dependencies>
<!-- Spring JDBC Support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- MySQL Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.5</version>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.databind-version}</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
如果您不熟悉 Spring 中的 Rest,请阅读Spring Restful Webservice 示例。
模型类
我们的员工 bean 是以员工表为模型的,如下所示。
package com.journaldev.spring.jdbc.model;
import java.io.Serializable;
public class Employee implements Serializable{
private static final long serialVersionUID = -7788619177798333712L;
private int id;
private String name;
private String role;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
Spring 控制器类
我们的简单控制器类如下所示。
package com.journaldev.spring.jdbc.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.journaldev.spring.jdbc.model.Employee;
/**
* Handles requests for the Employee JDBC Service.
*/
@Controller
public class EmployeeController {
private static final Logger logger = LoggerFactory.getLogger(EmployeeController.class);
@Autowired
@Qualifier("dbDataSource")
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@RequestMapping(value = "/rest/emps", method = RequestMethod.GET)
public @ResponseBody List<Employee> getAllEmployees() {
logger.info("Start getAllEmployees.");
List<Employee> empList = new ArrayList<Employee>();
//JDBC Code - Start
String query = "select id, name, role from Employee";
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
List<Map<String,Object>> empRows = jdbcTemplate.queryForList(query);
for(Map<String,Object> empRow : empRows){
Employee emp = new Employee();
emp.setId(Integer.parseInt(String.valueOf(empRow.get("id"))));
emp.setName(String.valueOf(empRow.get("name")));
emp.setRole(String.valueOf(empRow.get("role")));
empList.add(emp);
}
return empList;
}
}
关于 Controller 类的要点如下:
- DataSource 将通过名为 dbDataSource 的 Spring Bean 配置进行连接。
- 我们使用 JdbcTemplate 来避免常见错误(例如资源泄漏)并删除 JDBC 样板代码。
- 检索员工列表的 URI 为 https://{host}:{port}/SpringDataSource/rest/emps
- 我们使用@ResponseBody将员工对象列表作为响应发送,Spring 将负责将其转换为 JSON。
Spring Bean 配置
我们可以通过两种方式进行 JNDI 查找并将其连接到 Controller DataSource,我的 spring bean 配置文件包含这两种方式,但其中一种方式被注释掉了。您可以在这两种方式之间切换,响应将是相同的。
- 使用 jee 命名空间标签执行 JNDI 查找并将其配置为 Spring Bean。在这种情况下,我们还需要包含 jee 命名空间和模式定义。
- 通过传递 JNDI 上下文名称来创建类型的 bean。jndiName
org.springframework.jndi.JndiObjectFactoryBean
是此配置的必需参数。
我的 spring bean 配置文件如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="https://www.springframework.org/schema/mvc"
xmlns:jee="https://www.springframework.org/schema/jee"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:beans="https://www.springframework.org/schema/beans"
xmlns:context="https://www.springframework.org/schema/context"
xsi:schemaLocation="https://www.springframework.org/schema/jee https://www.springframework.org/schema/jee/spring-jee.xsd
https://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources
in the /WEB-INF/views directory -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<!-- Configure to plugin JSON as request and response in method handler -->
<beans:bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<beans:property name="messageConverters">
<beans:list>
<beans:ref bean="jsonMessageConverter" />
</beans:list>
</beans:property>
</beans:bean>
<!-- Configure bean to convert JSON to POJO and vice versa -->
<beans:bean id="jsonMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</beans:bean>
<!-- Create DataSource Bean -->
<beans:bean id="dbDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<beans:property name="jndiName" value="java:comp/env/jdbc/MyLocalDB"/>
</beans:bean>
<!-- using JEE namespace for lookup -->
<!--
<jee:jndi-lookup id="dbDataSource" jndi-name="jdbc/MyLocalDB"
expected-type="javax.sql.DataSource" />
-->
<context:component-scan base-package="com.journaldev.spring.jdbc.controller" />
</beans:beans>
Tomcat 数据源 JNDI 配置
现在我们已经完成了项目,最后部分是在 Tomcat 容器中进行 JNDI 配置以创建 JNDI 资源。
<Resource name="jdbc/TestDB"
global="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/TestDB"
username="pankaj"
password="pankaj123"
maxActive="100"
maxIdle="20"
minIdle="5"
maxWait="10000"/>
在 server.xml 文件的 GlobalNamingResources 部分添加上述配置。
<ResourceLink name="jdbc/MyLocalDB"
global="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource" />
我们还需要创建资源链接以在我们的应用程序中使用 JNDI 配置,最好的方法是将其添加到服务器 context.xml 文件中。请注意,ResourceLink 名称应与我们在应用程序中使用的 JNDI 上下文名称匹配。还要确保 MySQL jar 存在于 tomcat lib 目录中,否则 tomcat 将无法创建 MySQL 数据库连接池。
运行 Spring DataSource JNDI 示例项目
我们的项目和服务器配置已完成,我们准备测试它。将项目导出为 WAR 文件并将其放置在 tomcat 部署目录中。Rest 调用的 JSON 响应如下图所示。这就是 Spring 与 servlet 容器 JNDI 上下文集成的全部内容,请从以下链接下载示例项目并试用它以了解更多信息。
下载 Spring DataSource JNDI 项目