Из этого туториала Вы узнаете, как реализовать CRUD (создание, чтение, обновление, удаление) DataGrid с использованием ExtJS 4.2, Spring MVC 3.2.4 и Maven с IDE NetBeans 7.3.
Что мы обычно хотим делать с данными?
- Создать (вставить)
- Читать / Получить (Выбрать)
- Обновление (Update)
- Удалить / Уничтожить (Удалить)
В этом примере я собираюсь использовать JSON для обмена форматами данных между браузером и сервером.
1. Создайте новый проект в IDE NetBeans
- Перейдите в Файл> Новый проект или нажмите Ctrl + Shift + N
- Выберите maven> Project из архетипа и нажмите кнопку Далее
- В поле поиска напишите extjs, выберите extjs-springmvc-webapp и нажмите «Далее».
- Введите Имя проекта, Местоположение и Пакет и нажмите Готово
Наконец, вы получите каталог проекта, подобный этому:
2. Изменить проект
В этот момент вы можете запустить свой проект, и он покажет вам сетку, в которой вы можете редактировать 2 пользователей. Однако у этого архетипа есть небольшая проблема. Если вы видите файл UserService.java, который находится внутри пакета com.extjs.spring.services , он использует метод parseUserJson, который используется методом updateUser .
...
/**
* Update a user in the system
*/
@RequestMapping(value = "/user/update", method = RequestMethod.POST)
public Map updateUser( HttpServletRequest request, HttpServletResponse response, Principal principal
, @RequestBody String json ) throws Exception
{
//TODO replace this with your real code here.
Collection<User> parsedUsers = parseUserJson(json);
// Update all of the users (client is sending us array of users in json)
if ( parsedUsers != null )
{
for (User parsedUser : parsedUsers)
{
User localUser = users.get(parsedUser.getId());
if ( localUser == null )
{
throw new RuntimeException("Invalid User");
}
// save changes to local user
localUser.setName(parsedUser.getName());
localUser.setEmail(parsedUser.getEmail());
}
}
Map results = new HashMap();
results.put("succes", true);
return results;
}
/**
* Parse an json packet of user(s)
*/
private Collection<User> parseUserJson( String json ) throws Exception
{
try
{
if ( json.startsWith("[") && json.endsWith("]") )
{
// array of users
ObjectMapper mapper = new ObjectMapper();
TypeReference ref = new TypeReference<Collection<User>>(){};
Collection<User> user = (Collection<User>) mapper.readValue(json, ref);
return user;
}
else
{
// Single object
ObjectMapper mapper = new ObjectMapper();
Collection<User> users = new ArrayList<User>();
users.add( (User) mapper.readValue(json, User.class) );
return users;
}
}
catch (Exception ex)
{
throw new RuntimeException("Invalid USER Json");
}
}
...
Проблема этого подхода заключается в том, что если мы хотим создать больше объектов Models или POJO, нам нужно создать метод синтаксического анализатора для каждого контроллера.
Одним из вариантов решения этой проблемы было бы создание базового контроллера и расширение всех новых контроллеров из него. Однако нам по-прежнему необходимо вызывать метод синтаксического анализатора каждый раз, когда мы читаем объект, отправленный как параметр @RequestBody . Другим вариантом может быть создание класса util или helper, который реализует этот метод, и вызов его из контроллеров, но нам все равно нужно вызывать этот метод каждый раз, когда нам нужно десериализовать @RequestBody.
По умолчанию, в пружинных контроллерах, если мы проходим класс или List <Class> в @RequestBody параметра, он анализирует строку и преобразует объект непосредственно. Например:
@RequestMapping(value = "/update", method = RequestMethod.POST)
public void updateUser(@RequestBody List<User> inUsers) throws Exception {
for (User user : inUsers) {
User localUser = users.get(user.getId());
if (localUser == null) {
throw new RuntimeException("Invalid User");
}
// save changes to local user
localUser.setName(user.getName());
localUser.setEmail(user.getEmail());
}
}
Это может быть лучшим решением, но у нас есть другая проблема. Когда мы редактируем только одну строку в нашей сетке, она отправляет следующий json на сервер.
{"id":"1","name":"Eds","email":"ed@sencha.com"}
Как видите, он отправляет объект без квадратных скобок «[..]» , представляющий массив. то есть:
[{"id":"1","name":"Eds","email":"ed@sencha.com"}]
Это означает, что он будет работать только тогда, когда мы отредактируем две или более строки в сетке, поскольку он отправляет массив объектов, то есть:
[{"id":"1","name":"Edss","email":"ed@sencha.com"},{"id":"2","name":"Tommys","email":"tommy@sencha.com"}]
Есть два возможных решения:
- Модифицируйте ExtJS Store, чтобы он всегда отправлял массив вместо одного объекта. Поскольку я не знаю, как это сделать, и это может сломать другие части фреймворка, я отказался от этой опции
- Создайте CustomMappingJacksonHttpMessageConverter для Spring. Для меня это лучший вариант, так как не влияет на внутреннюю среду Spring Framework.
Чтобы сделать второй вариант, нам нужно сделать 3 вещи:
- Создайте Java-класс CustomMappingJacksonHttpMessageConverter.
- Создайте экземпляр этого класса в файле конфигурации Spring Servlet (в этом случае я собираюсь использовать XML)
- Добавьте некоторые библиотеки Apache commons в наш pom.xml, так как они потребуются для нашего пользовательского класса конвертера.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.springframework.http.converter.json;
import com.fasterxml.jackson.databind.JavaType;
import java.io.IOException;
import java.lang.reflect.Type;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageNotReadableException;
/**
*
* @author luis
*/
public class CustomMappingJacksonHttpMessageConverter extends MappingJackson2HttpMessageConverter {
@Override
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
JavaType javaType = getJavaType(type, contextClass);
return readJavaType(javaType, inputMessage);
}
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) {
try {
String inputJson = IOUtils.toString(inputMessage.getBody());
if(javaType.isCollectionLikeType()
&& !(inputJson.startsWith("[") && inputJson.endsWith("]")) ) {
inputJson = "[" + inputJson + "]";
}
return getObjectMapper().readValue(inputJson, javaType);
} catch (IOException ex) {
throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
}
}
}
2. Создайте экземпляр этого класса в файле конфигурации Spring Servlet (Веб-страницы> WEB-INF> services-servlet.xml).
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!--<mvc:annotation-driven/>-->
<bean class='org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter'>
<property name='messageConverters'>
<list>
<bean class='org.springframework.http.converter.json.CustomMappingJacksonHttpMessageConverter'/>
</list>
</property>
</bean>
<bean name='handlerMapping' class='org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping'>
<property name='useTrailingSlashMatch' value='false'></property>
</bean>
<!-- ****** REST API Service ****** -->
<bean id="userService" class="com.extjs.spring.services.UserService" />
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
</bean>
</list>
</property>
</bean>
</beans>
3. Добавьте некоторые библиотеки Apache commons в наш pom.xml, так как они потребуются для нашего пользовательского класса конвертера.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lvargas</groupId>
<artifactId>extjs-spring</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>extjs-spring-webapp</name>
<url>http://maven.apache.org</url>
<properties>
<java-version>1.6</java-version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.springframework.version>3.2.4.RELEASE</org.springframework.version>
<org.springframework.security.version>3.2.4.RELEASE</org.springframework.security.version>
<spring.integration.version>2.2.6.RELEASE</spring.integration.version>
</properties>
<dependencies>
<!-- link to tomcat to get reference to servlet api -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>servlet-api</artifactId>
<version>6.0.33</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>apache-log4j-extras</artifactId>
<version>1.1</version>
</dependency>
<!--
Core utilities used by other modules.
Define this if you use Spring Utility APIs (org.springframework.core.*/org.springframework.util.*)
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--
Bean Factory and JavaBeans utilities (depends on spring-core)
Define this if you use Spring Bean APIs (org.springframework.beans.*)
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--
Aspect Oriented Programming (AOP) Framework (depends on spring-core, spring-beans)
Define this if you use Spring AOP APIs (org.springframework.aop.*)
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--
Application Context (depends on spring-core, spring-expression, spring-aop, spring-beans)
This is the central artifact for Spring's Dependency Injection Container and is generally always defined
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--
Various Application Context utilities, including EhCache, JavaMail, Quartz, and Freemarker integration
Define this if you need any of these integrations
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--
Transaction Management Abstraction (depends on spring-core, spring-beans, spring-aop, spring-context)
Define this if you use Spring Transactions or DAO Exception Hierarchy
(org.springframework.transaction.*/org.springframework.dao.*)
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--
JDBC Data Access Library (depends on spring-core, spring-beans, spring-context, spring-tx)
Define this if you use Spring's JdbcTemplate API (org.springframework.jdbc.*)
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--
Object-to-Relation-Mapping (ORM) integration with Hibernate, JPA, and iBatis.
(depends on spring-core, spring-beans, spring-context, spring-tx)
Define this if you need ORM (org.springframework.orm.*)
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--
Object-to-XML Mapping (OXM) abstraction and integration with JAXB, JiBX, Castor, XStream, and XML Beans.
(depends on spring-core, spring-beans, spring-context)
Define this if you need OXM (org.springframework.oxm.*)
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--
Web application development utilities applicable to both Servlet and Portlet Environments
(depends on spring-core, spring-beans, spring-context)
Define this if you use Spring MVC, or wish to use Struts, JSF, or another web framework with Spring (org.springframework.web.*)
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--
Spring MVC for Servlet Environments (depends on spring-core, spring-beans, spring-context, spring-web)
Define this if you use Spring MVC with a Servlet Container such as Apache Tomcat (org.springframework.web.servlet.*)
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--
Support for testing Spring applications with tools such as JUnit and TestNG
This artifact is generally always defined with a 'test' scope for the integration testing framework and unit testing stubs
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
<build>
<finalName>extjs-spring-1.0-SNAPSHOT</finalName>
</build>
<description>A maven Archetype to create new EXTJS 4 project powered by a spring MVC 3.2.4 service.</description>
</project>
Наконец, вы можете снова запустить и протестировать свой проект.



