Легкий старт: Spring + MongoDB


 
Пошукав на Хабре схожі статті, знайшов тільки Morphia — легкий ORM для MongoDB, керований анотаціями , нічого по зв'язці Spring Data + MongoDB не знайшлося, у зв'язку з цим вирішив написати пост з розділу «для найменших» з налаштування та використання зв'язки Spring + MongoDB.
 
 
Що буде з себе представляти сам додаток:
Зробимо найпростіший менеджер контактів, для того, щоб спробувати на прикладі елементарні операції CRUD.
 
 
Використовувані бібліотеки:
     
  • Spring IoC, MVC, Data (Mongo)
  •  
  • Mongo Driver
  •  
  • Log4j через sl4j
  •  
  • ну і трошки додаткових, які опишу вже в конфігураційному файлі
  •  
Збирати проект буде Maven, а код, особисто я, пишу в Intellij IDEA (думаю, це найкраща IDE для Java). До речі кажучи, про переваги цього середовища свого часу розповідав asolntsev в пості Чому IDEA краще Eclipse .
 
 
Конфігурація проекту
 Для початку опишемо pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>war</packaging>

    <groupId>habra</groupId>
    <artifactId>habr</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- На момент написания статьи, версии библиотек являются последними -->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <version.jdk>1.6</version.jdk>
        <version.spring>4.0.2.RELEASE</version.spring>
        <version.spring.mongodb>1.4.0.RELEASE</version.spring.mongodb>
        <version.jackson>1.9.13</version.jackson>
    </properties>

    <dependencies>
        <!-- Все, что нужно для Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${version.spring}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${version.spring}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${version.spring}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${version.spring}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
            <version>${version.spring.mongodb}</version>
        </dependency>

        <!-- MongoDB драйвер -->
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongo-java-driver</artifactId>
            <version>2.11.4</version>
        </dependency>

        <!--
        Jackson JSON Mapper
        Тащу его всегда, когда нужно писать API на базе JSON объектов.
        В нашем случае можете эту библиотеку не тянуть.
        -->
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>${version.jackson}</version>
        </dependency>

        <!-- Servlet Api -->
        <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>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- Логгирование -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>

        <!-- TEST -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

        <!--
        Apache Commons, тяну пратически в каждый свой проект из-за их полезности.
        Можете также их пропустить.
        -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <webXml>src/main/webapp/WEB-INF/web.xml</webXml>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Тепер, коли всі бібліотеки описані, можна приступити до опису конфігураційних файлів Spring.
Для цього я зазвичай створюю папку «spring» в src / main / resources, деякі зберігають файли конфігурації Spring в webapp / WEB-INF / *, але це вже кому як зручно. Скажу відразу, конфігураційних файлу буде 2, по украй мірі я вважаю, що це найбільш вірний варіант опису конфігурації. 1-й файл включатиме конфігурацію по створенню бінов, підключення до БД, і пр., так сказати конфігурація контексту всього додатки. 2-й файл — це опис роботи DispatcherServlet, загалом все, що пов'язано вже з відображенням сторінок, і взагалі c Spring MVC.
 
 Почнемо з опису контексту всього додатки (src / main / resources / spring / applicationContext.xml):
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mongo="http://www.springframework.org/schema/data/mongo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/data/mongo
        http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">

    <!-- Включаем контекстные аннотации типа @Service, @Controller, @Repository... -->
    <context:annotation-config/>

    <!--
    Указываем Springу пакет, в котором он будет искать классы,
    помеченные аннотациями @Service, @Repository, и создавать их бины, но исключать он будет @Controller,
    т.к. эти классы нам нужны будут в другом месте.
    -->
    <context:component-scan base-package="ru.habrahabr.sm">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--
    Загружает properties файл в конфигурацию Spring (т.е. сюда).
    Переменные из файла можно будет использовать как ${mongo.host} (пример см. ниже)
    -->
    <context:property-placeholder location="classpath:database.properties"/>

    <!-- Создаем бин 'mongo' -->
    <mongo:mongo host="${mongo.host}" port="${mongo.port}"/>

    <!--
    Создаем бин 'mongoDbFactory'.
    Если MongoDB не требует авторизации, то поля username, password можно убрать
    -->
    <mongo:db-factory
            username="${mongo.username}"
            password="${mongo.password}"
            dbname="${mongo.db}"
            mongo-ref="mongo"/>

    <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
    </bean>
</beans>

 Опис контексту Spring MVC (src / main / resources / spring / dispatcherServlet.xml):
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- Включаем MVC аннотации -->
    <mvc:annotation-driven/>

    <!--
    Использование MVC Resources
    Проще говоря, все файлы из папки webapp/resources/ будут доступны по адресу: localhost/resources/
    -->
    <mvc:resources mapping="/resources/**" location="/resources/"/>

    <!-- Указываем Spring MVC где искать классы-контроллеры -->
    <context:component-scan base-package="ru.habrahabr.sm">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- Указываем Spring MVC где будут лежать наши Viewшки, в данном случае это "/WEB-INF/pages/" -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

Відмінно, Spring налаштований, але якщо ви уважно читали конфігураційні файли, то помітили, що в applicationContext.xml ми тягнули "classpath: database.properties ". Це означає, що потрібно створити файл в «src / main / resources / database.properties» з наступним змістом:
mongo.host=localhost
mongo.port=27017
mongo.db=mydb
mongo.username=username
mongo.password=password

Зрозуміло, потрібно замінити дані після знаку '=' своїми. Зауважте, що поля username, password необов'язкові, і якщо у вас не потрібна авторизація для отримання доступу до MongoDB, загляньте в applicationContext.xml, там вказано які поля потрібно прибрати. Відразу створимо конфігураційний файл для системи логгірованія (src/main/resources/log4j.properties):
log4j.rootLogger=INFO, stdout

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.conversionPattern=%d{dd.MM.yy HH:mm:ss} %5p - %m%n
log4j.appender.stdout.encoding=UTF-8

Нарешті можна перейти до завершального етапу конфігурування Java Web додатки — опис web.xml (src / main / webapp / WEB-INF / web.xml):
<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
         http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <!-- Spring Application Context -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    <!-- /Spring Application Context -->

    <!-- Spring Dispatcher Servlet Context -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/dispatcherServlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!-- /Spring Dispatcher Servlet Context -->

    <!-- Filters -->

    <!-- Character Filter -->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- /Character Filter -->

    <!-- /Filters -->

</web-app>

На даному етапі у Вас вже повинна бути наступна структура файлів у проекті:
 
У папку «src / webapp / resources /» можете ложить будь статичний контент (картинки, стилі, js скрипти, і т.д.).
 
 
Написання коду
Нарешті проект зконфігурований, і можна приступити до написання самого коду програми. Почнемо з опису моделі та реалізації шару по роботі з БД. Хочу відзначити наступне: у MongoDB немає целочисленного AUTO_INCREMENT PK поля, а об'єкту за замовчуванням присвоюється ID такого виду: ObjectId («5326b46f44ae9e6328b4566c»). Spring Data розуміє цей ID як об'єкт типу String. Простіше кажучи, якщо вам потрібно зробити так, щоб у вас ID об'єкту було цілочисловим і авто увеличивающимся, то доведеться над цим попрацювати самостійно. Але насправді в цьому немає нічого складного і не варто цього боятися, а я зараз опишу як це робиться! Якщо ж Вас влаштовує і довгий String в якості ID об'єкта, то пропустіть всі класи (і відповідно їх використання) у яких зустрічається слово «Sequence».
 
Для початку створюємо колекцію в БД з ім'ям sequences. І руками вносимо туди об'єкт (insert):
{
    "_id" : "contacts",
    "sequence" : 0
}
У цій колекції ми будемо зберігати пару: «ім'я таблиці» — «останній ID», це означає, що для кожної таблиці (колекції), в якій Ви хочете використовувати нашу самописна AUTO_INCREMENT реалізацію, вам потрібно внести новий запис, як вказано вище, тільки замість «contacts» введіть ім'я потрібної Вам колекції. До речі, якщо ще не знаєте який інструмент (management-tool) використовувати для MongoDB, то рекомендую Robomongo .
 
Тепер створюємо клас-обгортку для цієї колекції:
package ru.habrahabr.sm.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

/**
 * Date: 26.03.2014
 * Time: 15:38
 *
 * @author Ruslan Molchanov (ruslanys@gmail.com)
 */
@Document(collection = Sequence.COLLECTION_NAME)
public class Sequence {
    public static final String COLLECTION_NAME = "sequences";

    @Id
    private String id;
    private Long sequence;

    public Sequence() {
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Long getSequence() {
        return sequence;
    }

    public void setSequence(Long sequence) {
        this.sequence = sequence;
    }
}
Зверніть увагу на запис "@ Document (collection = Sequence.COLLECTION_NAME)" насправді можна було б написати "@ Document (collection =« sequences »)", але це вже скоріше справа смаку. Плюс є одна явна гідність у такого методу, далі зрозумієте яке.
 
Тепер опишемо шар по роботі з БД для класу-обгортки "Sequence ", скажу відразу, щоб не розтягувати статтю, я не буду створювати інтерфейси для сервісів і DAO, буду користуватися вже реалізаціями об'єктів (хоча це начебто не дуже добре), сподіваюся, що Ви розумієте про що йде мова.
 
package ru.habrahabr.sm.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;
import ru.habrahabr.sm.exceptions.SequenceException;
import ru.habrahabr.sm.model.Sequence;

/**
 * Этот код взят с сайта mkyong,
 * точную ссылку найти не смог
 */
@Repository
public class SequenceDao {
    @Autowired private MongoOperations mongoOperations;

    public Long getNextSequenceId(String key) {
        // получаем объект Sequence по наименованию коллекции
        Query query = new Query(Criteria.where("id").is(key));

        // увеличиваем поле sequence на единицу
        Update update = new Update();
        update.inc("sequence", 1);

        // указываем опцию, что нужно возвращать измененный объект
        FindAndModifyOptions options = new FindAndModifyOptions();
        options.returnNew(true);

        // немного магии :)
        Sequence sequence = mongoOperations.findAndModify(query, update, options, Sequence.class);

        // if no sequence throws SequenceException
        if(sequence == null) throw new SequenceException("Unable to get sequence for key: " + key);

        return sequence.getSequence();
    }
}

Тепер клас винятку, яке викидається, якщо в нашій колекції `sequences` не буде відповідної записи в БД (про яку я писав вище):
package ru.habrahabr.sm.exceptions;

/**
 * Date: 26.03.2014
 * Time: 16:09
 *
 * @author Ruslan Molchanov (ruslanys@gmail.com)
 */
public class SequenceException extends RuntimeException {
    public SequenceException(String message) {
        super(message);
    }
}

Тепер створюємо в БД колекцію `contacts`, і клас-обгортку:
package ru.habrahabr.sm.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.io.Serializable;

/**
 * Date: 26.03.2014
 * Time: 15:29
 *
 * @author Ruslan Molchanov (ruslanys@gmail.com)
 */
@Document(collection = Contact.COLLECTION_NAME)
public class Contact implements Serializable {
    public static final String COLLECTION_NAME = "contacts";

    @Id
    private Long id;
    /* *******************************************************
     Если вы хотите, чтобы ID объекта была автогенерируемая
     строка (об этом я писал в посте), то опишите поле ID так:
     @Id
     private String id;
     ********************************************************* */

    private String name;
    private String number;
    private String email;

    public Contact() {
    }

    public Contact(String name, String number, String email) {
        this.name = name;
        this.number = number;
        this.email = email;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

Тепер опишемо DAO шар для нашого класу «Contact»:
package ru.habrahabr.sm.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;
import ru.habrahabr.sm.model.Contact;

import java.util.List;

/**
 * Date: 26.03.2014
 * Time: 19:13
 *
 * @author Ruslan Molchanov (ruslanys@gmail.com)
 */
@Repository
public class ContactDao {
    @Autowired private MongoOperations mongoOperations;

    public void save(Contact contact) {
        mongoOperations.save(contact);
    }

    public Contact get(Long id) {
        return mongoOperations.findOne(Query.query(Criteria.where("id").is(id)), Contact.class);
    }

    public List<Contact> getAll() {
        return mongoOperations.findAll(Contact.class);
    }

    public void remove(Long id) {
        mongoOperations.remove(Query.query(Criteria.where("id").is(id)), Contact.class);
    }
}
Хотілося б відзначити, що оновити запис в БД можна на рівні DAO наступним чином:
mongoOperations.updateFirst(query, update, Contact.class);
Розібратися з цим самостійно простіше простого і не складе для Вас труднощів. Але хотілося б відзначити, що якщо ви виконаєте:
mongoOperations.save(contact);
на об'єкт, у якого ID вже виставлений, і існує в БД, то відбудеться перезапис об'єкта в БД під зазначеним ID.
 
Тепер опишемо шар бізнес-логіки для класу «Contact»:
package ru.habrahabr.sm.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.habrahabr.sm.dao.ContactDao;
import ru.habrahabr.sm.dao.SequenceDao;
import ru.habrahabr.sm.model.Contact;

import java.util.List;

/**
 * Date: 26.03.2014
 * Time: 20:09
 *
 * @author Ruslan Molchanov (ruslanys@gmail.com)
 */
@Service
public class ContactService {
    @Autowired private SequenceDao sequenceDao;
    @Autowired private ContactDao contactDao;
    
    public void add(Contact contact) {
        contact.setId(sequenceDao.getNextSequenceId(Contact.COLLECTION_NAME));
        contactDao.save(contact);
    }
    
    public void update(Contact contact) {
        contactDao.save(contact);
    }
    
    public Contact get(Long id) {
        return contactDao.get(id);
    }
    
    public List<Contact> getAll() {
        return contactDao.getAll();
    }
    
    public void remove(Long id) {
        contactDao.remove(id);
    }
}

Ну ось, власне і все, все, що стосується зв'язки Spring + MongoDB ми виконали, тобто тепер Ви можете користуватися ORM Spring Data з уже підключеною до проекту MongoDB, але раз я обіцяв на прикладі довідника показати роботу з БД, то залишилося написати ще клас-контролер:
package ru.habrahabr.sm.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import ru.habrahabr.sm.model.Contact;
import ru.habrahabr.sm.services.ContactService;

/**
 * Date: 26.03.2014
 * Time: 20:30
 *
 * @author Ruslan Molchanov (ruslanys@gmail.com)
 */
@Controller
public class MainController {
    @Autowired private ContactService contactService;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView showAll() {
        ModelAndView modelAndView = new ModelAndView("all");

        modelAndView.addObject("contacts", contactService.getAll());

        return modelAndView;
    }

    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public ModelAndView showAddForm() {
        return new ModelAndView("add_form", "contact", new Contact());
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String addContact(@ModelAttribute("contact") Contact contact) {
        if(contact.getId() == null) contactService.add(contact);
        else contactService.update(contact);

        return "redirect:/";
    }

    @RequestMapping(value = "/edit", method = RequestMethod.GET)
    public ModelAndView showEditForm(@RequestParam(required = true) Long id) {
        return new ModelAndView("add_form", "contact", contactService.get(id));
    }

    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public String deleteContact(@RequestParam(required = true) Long id) {
        contactService.remove(id);

        return "redirect:/";
    }
}

Ну і в'юшки (src / webapp / WEB-INF / pages / add_form.jsp):
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Добавить контакт</title>
</head>
<body>
<form:form method="POST" action="/add" modelAttribute="contact">
    <form:hidden path="id" />
    <table>
        <tr>
            <td>Name:</td>
            <td><form:input path="name" /></td>
        </tr>
        <tr>
            <td>Number:</td>
            <td><form:input path="number" /></td>
        </tr>
        <tr>
            <td>E-mail:</td>
            <td><form:input path="email" /></td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" />
            </td>
        </tr>
    </table>
</form:form>
</body>
</html>

Ще одна (src / webapp / WEB-INF / all.jsp):
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
    <title>Все контакты</title>
</head>
<body>
<table width="600px">
    <tr>
        <td><b>ID</b></td>
        <td><b>Name</b></td>
        <td><b>Number</b></td>
        <td><b>E-mail</b></td>
        <td><b>Action</b></td>
    </tr>
    <c:forEach var="contact" items="${contacts}">
        <tr>
            <td>${contact.id}</td>
            <td>${contact.name}</td>
            <td>${contact.number}</td>
            <td>${contact.email}</td>
            <td><a href="/edit?id=${contact.id}">Edit</a> | <a href="/delete?id=${contact.id}">Delete</a></td>
        </tr>
    </c:forEach>
    <tr>
        <td colspan="5">
            <a href="/add">Добавить запись</a>
        </td>
    </tr>
</table>
</body>
</html>

 
Ну от і все, збираємо проект, запускаємо, і вуаля:
 
 
Посилання на исходники: db.tt/2zfWWsJT
 
P.S. Буду щасливий, якщо кому-небудь знадобиться цей пост. Буду вдячний за підказки і виправлення.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.