Tomcat 예제를 사용한 Spring DataSource JNDI

스프링 DataSource JNDI Tomcat 예제 튜토리얼에 오신 것을 환영합니다. 이전에는 스프링 JDBC 통합을 사용하여 데이터베이스 작업을 구현하는 방법을 살펴 보았습니다. 그러나 대부분의 경우 기업 애플리케이션은 Tomcat, JBoss 등과 같은 서블릿 컨테이너에 배포됩니다.

스프링 DataSource

우리는 JNDI와 DataSource가 연결 풀링을 구현하고 컨테이너를 통해 이점을 얻는 우선적인 방법임을 알고 있습니다. 오늘은 스프링 웹 애플리케이션을 구성하여 Tomcat이 제공하는 JNDI 연결을 사용하는 방법을 살펴보겠습니다. 예시로 MySQL 데이터베이스 서버를 사용하고 몇 개의 행이 있는 간단한 테이블을 생성합니다. 테이블의 모든 데이터 목록을 JSON 응답으로 반환하는 스프링 Rest 웹 서비스를 만들 것입니다.

데이터베이스 설정

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 MVC 프로젝트를 생성하여 스프링 애플리케이션의 기본 코드를 준비합니다. 구현이 완료되면 프로젝트 구조는 아래 이미지와 같아집니다.

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 예제를 읽어보세요.

모델 클래스

Employee 테이블을 모델로 한 Employee 빈은 아래와 같습니다.

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;
	}
	
	
}

스프링 컨트롤러 클래스

우리 간단한 컨트롤러 클래스는 아래와 같습니다.

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 getAllEmployees() {
		logger.info("Start getAllEmployees.");
		List empList = new ArrayList();
		//JDBC 코드 - 시작
		String query = "select id, name, role from Employee";
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

		List> empRows = jdbcTemplate.queryForList(query);
		
		for(Map 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;
	}

}

컨트롤러 클래스에 대한 중요한 포인트는 다음과 같습니다.

  • 데이터 소스는 이름이 dbDataSource인 스프링 빈 구성으로 연결됩니다.
  • JdbcTemplate을 사용하여 리소스 누출 및 JDBC 보일러 플레이트 코드와 같은 일반적인 오류를 피합니다.
  • 직원 목록을 검색하기 위한 URI는 https://{호스트}:{포트}/SpringDataSource/rest/emps입니다.
  • 우리는 @ResponseBody를 사용하여 직원 개체 목록을 응답으로 보내며, 스프링이 이를 JSON으로 변환하는 것을 처리합니다.

스프링 빈 구성

JNDI 조회 및 컨트롤러 데이터 소스에 연결하는 두 가지 방법이 있습니다. 스프링 빈 구성 파일에는 두 가지 방법이 모두 포함되어 있지만 하나는 주석 처리되어 있습니다. 이를 전환하면 응답은 동일합니다.

  1. jee 네임스페이스 태그를 사용하여 JNDI 조회를 수행하고 스프링 빈으로 구성합니다. 또한 이 경우에는 jee 네임스페이스와 스키마 정의를 포함해야 합니다.
  2. JNDI 컨텍스트 이름을 전달하여 org.springframework.jndi.JndiObjectFactoryBean 유형의 빈을 생성합니다. 이 구성에는 jndiName이 필수 매개변수입니다.

제 스프링 빈 구성 파일은 다음과 같습니다.

<?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 DataSource 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 구성을 사용하려면 리소스 링크를 생성해야 합니다. 이를 위해 server context.xml 파일에 추가하는 것이 가장 좋습니다. ResourceLink 이름이 응용 프로그램에서 사용하는 JNDI 컨텍스트 이름과 일치하는지 확인하세요. 또한 MySQL jar 파일이 tomcat lib 디렉토리에 있는지 확인하세요. 그렇지 않으면 tomcat은 MySQL 데이터베이스 연결 풀을 생성할 수 없습니다.

Spring DataSource JNDI 샘플 프로젝트 실행

우리의 프로젝트와 서버 구성이 완료되어 테스트할 준비가 되었습니다. 프로젝트를 WAR 파일로 내보내고 톰캣 배포 디렉토리에 배치하십시오. Rest 호출에 대한 JSON 응답은 아래 이미지에 표시되어 있습니다. 이것이 Spring 통합 및 서블릿 컨테이너 JNDI 컨텍스트에 대한 모든 내용입니다. 아래 링크에서 샘플 프로젝트를 다운로드하여 더 많이 배울 수 있도록 사용해 보십시오.

Spring DataSource JNDI 프로젝트 다운로드

Source:
https://www.digitalocean.com/community/tutorials/spring-datasource-jndi-with-tomcat-example