Voorbeeld van Spring DataSource JNDI met Tomcat

Welkom bij de Spring DataSource JNDI Tomcat Voorbeeldhandleiding. Eerder zagen we hoe we databasebewerkingen konden implementeren met behulp van Spring JDBC-integratie. Maar meestal worden bedrijfstoepassingen ingezet in een servletcontainer zoals Tomcat, JBoss, enzovoort.

Spring DataSource

We weten dat DataSource met JNDI de voorkeursmethode is om verbindingen te poolen en te profiteren van containerimplementaties. Vandaag zullen we kijken hoe we een Spring-webtoepassing kunnen configureren om JNDI-verbindingen van Tomcat te gebruiken. Voor mijn voorbeeld zal ik de MySQL-database server gebruiken en een eenvoudige tabel met enkele rijen maken. We zullen een Spring Rest webservice maken die de JSON-respons retourneert met een lijst van alle gegevens in de tabel.

Database-instellingen

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 Project

Creëer een Spring MVC-project in de Spring Tool Suite, zodat onze lente-toepassingsskeletcode klaar is. Zodra we klaar zijn met onze implementatie, zal onze projectstructuur eruitzien zoals te zien is in de onderstaande afbeelding.

Spring JDBC- en Jackson-afhankelijkheden

We moeten Spring JDBC, Jackson en de MySQL-database-driver toevoegen als afhankelijkheden in het pom.xml-bestand. Mijn uiteindelijke pom.xml-bestand ziet er als volgt uit.

<?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>

Als je niet bekend bent met Rest in Spring, lees dan Voorbeeld van Spring Restful Webservice.

Modelklasse

Onze werknemersbean, gemodelleerd naar de werknemerstabel, ziet er als volgt uit.

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 Controller Klasse

Onze eenvoudige controllerklasse ziet er als volgt uit.

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

}

Belangrijke punten over de Controllerklasse zijn:

  • DataSource zal worden bedraad door Spring Bean-configuratie met de naam dbDataSource.
  • We gebruiken JdbcTemplate om veelvoorkomende fouten zoals resourcelek te vermijden en JDBC boilerplate code te verwijderen.
  • URI om de lijst met werknemers op te halen is https://{host}:{port}/SpringDataSource/rest/emps
  • We gebruiken @ResponseBody om de lijst met werknemersobjecten als antwoord te verzenden, Spring zal ervoor zorgen dat het naar JSON wordt geconverteerd.

Spring Bean-configuratie

Er zijn twee manieren waarop we JNDI kunnen opzoeken en het aan de Controller DataSource kunnen bedraden, mijn Spring Bean-configuratiebestand bevat beide maar een ervan is uitgeschakeld. Je kunt hier tussen schakelen en het antwoord blijft hetzelfde.

  1. Gebruik de jee namespace-tag om de JNDI-opzoeking uit te voeren en configureer deze als een Spring Bean. We moeten ook de jee namespace en schema-definitie in dit geval opnemen.
  2. Het maken van een bean van het type org.springframework.jndi.JndiObjectFactoryBean door de JNDI-contextnaam door te geven. jndiName is een vereiste parameter voor deze configuratie.

Mijn Spring-beanconfiguratiebestand ziet er als volgt uit.

<?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-configuratie

Nu we klaar zijn met ons project, is het laatste deel om de JNDI-configuratie in de Tomcat-container te doen om de JNDI-resource te maken.

<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"/>

Voeg bovenstaande configuratie toe in de GlobalNamingResources-sectie van het server.xml-bestand.

<ResourceLink name="jdbc/MyLocalDB"
                	global="jdbc/TestDB"
                    auth="Container"
                    type="javax.sql.DataSource" />

We moeten ook de Resource Link maken om de JNDI-configuratie in onze applicatie te gebruiken, de beste manier om deze toe te voegen is in het servercontext.xml-bestand. Let op dat de naam van de ResourceLink overeen moet komen met de JNDI-contextnaam die we in onze applicatie gebruiken. Zorg er ook voor dat het MySQL-jar-bestand aanwezig is in de tomcat lib-map, anders kan Tomcat geen MySQL-databaseverbindingenpool maken.

Het uitvoeren van het Spring DataSource JNDI Voorbeeldproject

Ons project- en serverconfiguratie is voltooid en we zijn klaar om het te testen. Exporteer het project als een WAR-bestand en plaats het in de tomcat-implementatiemap. De JSON-reactie voor de Rest-oproep wordt weergegeven in de onderstaande afbeelding. Dat is alles voor de Spring-integratie met servletcontainer JNDI-context, download het voorbeeldproject vanaf de onderstaande link en speel ermee om meer te leren.

Download Spring DataSource JNDI Project

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