דוגמת קוד Spring DataSource JNDI עם Tomcat

שלום לך! ברוך הבא לשלב הדוגמאות של Spring DataSource JNDI ב-Tomcat. בקטע הקודם ראינו איך לממש פעולות מסד נתונים באמצעות אינטגרציה של Spring JDBC. עם זאת, ברוב המקרים, יישומים עסקיים מוטבעים בסביבת סרבל כמו Tomcat או JBoss.

Spring DataSource

אנו יודעים ששימוש ב־DataSource עם JNDI הוא הדרך המועדפת להשיג גישה לבריכת חיבורים וליהנות מיישום התכנית. היום נראה איך ניתן להגדיר אפליקציית רשת של Spring לשימוש בחיבורי JNDI שמסופקים על ידי Tomcat. לדוגמה שלי, אשתמש בשרת מסד נתונים MySQL ואיצור טבלה פשוטה עם כמה שורות. ניצור שירות רשת של Spring שיחזיר את התגובה בפורמט 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 MVC

צור פרויקט Spring MVC ב-Spring Tool Suite כך שקוד המסגרת של היישום שלנו יהיה מוכן. לאחר שנסיים את המימוש, מבנה הפרויקט שלנו יראה דומה לתמונה למטה.

תלותי Spring JDBC ו-Jackson

יהיה עלינו להוסיף תלותי Spring JDBC, Jackson ונהג מסד נתונים של MySQL בקובץ pom.xml. הקובץ 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>

אם אינך מכיר את Rest ב-Spring, אנא קרא דוגמה לשירות Restful של Spring.

כיתת מודל

העסקה שלנו שמודלת את טבלת העובדים, נראית כמו שלמטה.

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

המחלקת הפשוטה של הקונטרולר שלנו נראית כך.

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

}

נקודות חשובות בנוגע למחלקת הקונטרולר הן:

  • מקור נתונים יתווסף על ידי תצורת Spring Bean עם השם dbDataSource.
  • אנו משתמשים ב־JdbcTemplate כדי למנוע שגיאות נפוצות כמו נזילת משאבים והסרת קוד boiler plate של JDBC.
  • ה־URI לקבלת רשימת העובדים יהיה https://{host}:{port}/SpringDataSource/rest/emps
  • אנו משתמשים ב־@ResponseBody כדי לשלוח את רשימת אובייקטי העובדים כתגובה, Spring יטפל בהמרתה ל־JSON.

תצורת Spring Bean

ישנם שני דרכים לביצוע חיפוש JNDI וחיבורו למקור הנתונים של הקונטרולר, קובץ תצורת החלקה של הפעלתי כולל את שתיהם אך אחת מהן מוסתרת. ניתן להחליף ביניהם והתגובה תישאר זהה.

  1. שימוש בתגית שם ה־jee namespace כדי לבצע את החיפוש JNDI ולהגדיר אותו כ־Spring Bean. עלינו גם לכלול את ה־jee namespace ואת הגדרת הסכימה במקרה זה.
  2. יצירת אובייקט מסוג org.springframework.jndi.JndiObjectFactoryBean על ידי מעבר בשם ההקשר של JNDI. 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>

תצורת JNDI של מקור הנתונים של Tomcat

עכשיו שסיימנו עם הפרויקט שלנו, החלק הסופי הוא לבצע את התצורה של JNDI בתוך מקונטיינר ה-Tomcat כדי ליצור את המשאב של 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"/>

הוסף את התצורה שלמעלה בקטע GlobalNamingResources של קובץ server.xml.

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

אנו צריכים גם ליצור את הקישור למשאב כדי להשתמש בתצורת JNDI ביישום שלנו, הדרך הטובה ביותר להוסיף אותו היא בקובץ context.xml של השרת. שים לב ששם המשאב ResourceLink צריך להתאים לשם ההקשר של JNDI שאנו משתמשים בו ביישום שלנו. וודא גם שקובץ ה-JAR של MySQL נמצא בתיקיית הספרייה של tomcat, אחרת לא תוכל ליצור את מאגר החיבורים למסד הנתונים של MySQL.

הרצת פרויקט הדוגמה של חלון משאבי הנתונים של Spring DataSource

שלום,
הפרויקט שלנו והקבוצה של השרת הוגדרו ואנו מוכנים לבדוק אותו. יש לייצא את הפרויקט כקובץ WAR ולשים אותו בתיקיית ההפצה של טומקט. התגובה ב-JSON לקריאה על פי REST מוצגת בתמונה למטה. זו הכול לגבי השגרה של Spring עם הקשר JNDI של מכונת השרת, תוכל להוריד את הפרויקט הדוגמא מהקישור למטה ולשחק עם זה כדי ללמוד עוד.

הורד פרויקט מקורי ל-JNDI של מקורות נתונים של Spring

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