Учебные материалы

Доступ к MongoDB-данным через REST

Этот урок освещает процесс создания приложения, которое обращается к данным на основе графов через гипермедиа RESTful интерфейс.

Что вы создадите

Вы создадите Spring приложение, которое позволяет вам сохранять и получать Person объекты, сохраненные в MongoDB NoSQL БД с использованием Spring Data REST. Spring Data REST предоставляет возможности Spring HATEOAS и Spring Data MongoDB и комбинирует их вместе автоматически.

Spring Data REST также поддерживает Spring Data JPA, Spring Data GemFire и Spring Data Neo4j как хранилища данных.

Что вам потребуется

  • Примерно 15 минут свободного времени
  • Любимый текстовый редактор или IDE
  • JDK 6 и выше
  • Gradle 1.11+ или Maven 3.0+
  • Вы также можете импортировать код этого урока, а также просматривать web-страницы прямо из Spring Tool Suite (STS), собственно как и работать дальше из него.

Как проходить этот урок

Как и большинство уроков по Spring, вы можете начать с нуля и выполнять каждый шаг, либо пропустить базовые шаги, которые вам уже знакомы. В любом случае, вы в конечном итоге получите рабочий код.

Чтобы начать с нуля, перейдите в Настройка проекта.

Чтобы пропустить базовые шаги, выполните следующее:

Когда вы закончите, можете сравнить получившийся результат с образцом в gs-accessing-mongodb-data-rest/complete.

Настройка проекта

Для начала вам необходимо настроить базовый скрипт сборки. Вы можете использовать любую систему сборки, которая вам нравится для сборки проетов Spring, но в этом уроке рассмотрим код для работы с Gradle и Maven. Если вы не знакомы ни с одним из них, ознакомьтесь с соответсвующими уроками Сборка Java-проекта с использованием Gradle или Сборка Java-проекта с использованием Maven.

Создание структуры каталогов

В выбранном вами каталоге проекта создайте следующую структуру каталогов; к примеру, командой mkdir -p src/main/java/hello для *nix систем:

└── src
    └── main
        └── java
            └── hello

Создание файла сборки Gradle

Ниже представлен начальный файл сборки Gradle. Файл pom.xml находится здесь. Если вы используете Spring Tool Suite (STS), то можете импортировать урок прямо из него.

Если вы посмотрите на pom.xml, вы найдете, что указана версия для maven-compiler-plugin. В общем, это не рекомендуется делать. В данном случае он предназначен для решения проблем с нашей CI системы, которая по умолчанию имеет старую(до Java 5) версию этого плагина.

build.gradle

buildscript {
    repositories {
        maven { url "https://repo.spring.io/libs-release" }
        mavenLocal()
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.9.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'

jar {
    baseName = 'gs-accessing-mongodb-data-rest'
    version =  '0.1.0'
}

repositories {
    mavenLocal()
    mavenCentral()
    maven { url "https://repo.spring.io/libs-release" }
    maven { url "https://m2.neo4j.org" }
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.data:spring-data-mongodb")
    compile("org.springframework.data:spring-data-rest-webmvc")
    testCompile("junit:junit")
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.11'
}

Spring Boot gradle plugin предоставляет множество удобных возможностей:

  • Он собирает все jar'ы в classpath и собирает единое, исполняемое "über-jar", что делает более удобным выполнение и доставку вашего сервиса
  • Он ищет public static void main() метод, как признак исполняемого класса
  • Он предоставляет встроенное разрешение зависимостей, с определенными номерами версий для соответсвующих Spring Boot зависимостей. Вы можете переопределить на любые версии, какие захотите, но он будет по умолчанию для Boot выбранным набором версий

Установка и запуск MongoDB

Для работы в этом уроке вы должны поднять локальный MongoDB сервер.

На Mac OS X машине с homebrew выполните следующее:

brew install mongodb

Более подробную информацию вы найдете на странице http://docs.mongodb.org/manual/installation.

После установки вам необходимо запустить mongo демон:

$ mongod
all output going to: /usr/local/var/log/mongodb/mongo.log

MongoDB клиент, который также может быть установлен, может быть запущен из другого окна терминала командой mongo.

Создание доменного объекта

Создайте новый доменный объект для представления человека.

src/main/java/hello/Person.java

package hello;

import org.springframework.data.annotation.Id;

public class Person {

	@Id private String id;

	private String firstName;
	private String lastName;

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
}

Person имеет имя и фамилию. Также имеется id объекта, настроенный на автоматическую генерацию, поэтому вам не потребуется для этого дополнительных усилий.

Создание Person репозитория

Далее вам необходимо создать простой репозиторий.

src/main/java/hello/PersonRepository.java

package hello;

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends MongoRepository {

	List findByLastName(@Param("name") String name);

}

Этот репозиторий является интерфейсом и позволяет вам выполнять различные операции с участием Person объектов. Он получает эти операции, наследуя интерфейс PagingAndSortingRepository, определенным в Spring Data Commons.

В процессе выполнения, Spring Data REST будет создавать реализацию этого интерфейса автоматически. Затем он будет использовать аннотацию @RepositoryRestResource, обращаясь к Spring MVC для создания RESTful точки выхода /people.

@RepositoryRestResource не обязательна для указания. Она используется только когда необходимо изменить детали экспорта, такие как использование /people вместо значения по умолчанию /persons.

В вашем интерфейсе вы также описали собственный запрос получения списка Person объектов по заданной фамилии. Чуть позже вы увидите, как его вызвать.

Создание приложения исполняемым

Несмотря на то, что пакет этого сервиса может быть в составе web-приложения и WAR файлов, более простой подход, продемонстрированный ниже создает отдельное самостоятельное приложение. Вы упаковываете все в единый, исполняемый JAR-файл, который запускается через хорошо знакомый старый main() Java-метод. Попутно, вы используете поддержку Spring для встроенного Tomcat контейнера сервлетов как HTTP среду выполнения вместо развертывания на сторонний экземпляр.

src/main/java/hello/Application.java

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;

@Configuration
@EnableMongoRepositories
@Import(RepositoryRestMvcConfiguration.class)
@EnableAutoConfiguration
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

Метод main() передает управление вспомогательному классу SpringApplication, предоставляя Application.class как аргумент его run() методу. Это говорит Spring о том, чтобы прочитать аннотацию метаданных из Application и управлять им как компонентом в Spring Application Context.

Аннотация @EnableMongoRepositories активирует Spring Data MongoDB. Spring Data MongoDB будет создавать конкретную реализацию для PersonRepository и настраивать на взаимодействие с MongoDB БД, используя язык запросов Cypher.

Spring Data REST является Spring MVC приложением. Аннотация @Import(RepositoryRestMvcConfiguration.class) импортирует коллекцию Spring MVC контроллеров, JSON конвертеров и других бинов, необходимых для обеспечения RESTful интерфейса. Эти компоненты связаны с Spring Data MongoDB backend.

@EnableAutoConfiguration аннотация переключает на доступные по умолчанию настройки, основанные на содержимом вашего classpath. К примеру, т.к. приложение зависит от встраиваемой версии Tomcat(tomcat-embed-core.jar), то Tomcat сервер установлен и настроен по умолчанию от вашего имени. И также, т.к. приложение зависит от Spring MVC (spring-webmvc.jar), Spring MVC DispatcherServlet настроен и зарегистрирован за вас - web.xml не нужен! Поэтому здесь MultipartConfigElement, он настроен DispatcherServlet с функциональностью загрузки файлов. Автонастройка является мощным и гибким механизмом. Более подробно вы можете ознакомиться в API документации.

Сборка исполняемого JAR

Вы можете собрать единый исполняемый JAR-файл, который содержит все необходимые зависимости, классы и ресурсы. Это делает его легким в загрузке, версионировании и развертывании сервиса как приложения на протяжении всего периода разработки, на различных средах и так далее.

./gradlew build

Затем вы можете запустить JAR-файл:

java -jar build/libs/gs-accessing-mongodb-data-rest-0.1.0.jar

Если вы используете Maven, вы можете запустить приложение, используя mvn spring-boot:run, либо вы можете собрать приложение с mvn clean package и запустить JAR примерно так:

java -jar target/gs-accessing-mongodb-data-rest-0.1.0.jar
Процедура, описанная выше, создает исполняемый JAR. Вы также можете вместо него собрать классический WAR-файл.

Если вы используете Gradle, вы можете запустить ваш сервис из командной строки:

./gradlew clean build && java -jar build/libs/gs-accessing-mongodb-data-rest-0.1.0.jar
Если вы используете Maven, то можете запустить ваш сервис таким образом: mvn clean package && java -jar target/gs-accessing-mongodb-data-rest-0.1.0.jar.

Как вариант, вы можете запустить ваш сервис напрямую из Gradle примерно так:

./gradlew bootRun
С mvn - mvn spring-boot:run.

Данные по логгированию отображаются. Сервис должен быть поднят и запущен через несколько секунд.

Тестирование приложения

Теперь, когда приложение запущено, вы можете протестировать его. Вы можете использовать любой REST клиент, какой захотите. Пример, показанный ниже, использует *nix инструмент curl.

Сначала посмотрим на сервис верхнего уровня:

$ curl http://localhost:8080
{
  "_links" : {
    "people" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    }
  }
}

Здесь вы получите первое представление о том, что сервер может предложить. Ссылка на people указывает на http://localhost:8080/people. Она включает несколько вариантов, таких как ?page, ?size и ?sort.

Spring Data REST использует HAL формат для вывода JSON. Он гибок и предлагает удобный способ для предоставления ссылок, связанных с данными, которые необходимо получить.
$ curl http://localhost:8080/people
{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}

Есть также и без элементов, соответственно и без страниц. Время создавать нового Person!

$ curl -i -X POST -H "Content-Type:application/json" -d '{  "firstName" : "Frodo",  "lastName" : "Baggins" }' http://localhost:8080/people
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
Location: http://localhost:8080/people/53149b8e3004990b1af9f229
Content-Length: 0
Date: Mon, 03 Mar 2014 15:08:46 GMT
  • -i гарантирует вам отображение сообщения ответа, включая заголовки. URI новосозданного Person показан
  • -X POST указывает на использование POST для создания новой записи
  • -H "Content-Type:application/json" устанавливает тип содержимого, поэтому приложение знает о содержании JSON объекта
  • -d '{ "firstName" : "Frodo", "lastName" : "Baggins" }' отправляемые данные
Обратите внимание, как предыдущая POST операция включает заголовок Location. Он содержит URI новосозданного ресурса. Spring Data REST также имеет два метода RepositoryRestConfiguration.setReturnBodyOnCreate(…) и setReturnBodyOnCreate(…), которыми вы можете настраивать фреймворк для немедленного возврата представления только что созданного ресурса.

Вы можете снова запросить всех людей:

$ curl http://localhost:8080/people
{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  },
  "_embedded" : {
    "persons" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/people/53149b8e3004990b1af9f229"
        }
      }
    } ]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}

Объект persons содержит список с Frodo. Обратите внимание, как он включает в себя ссылку self. Spring Data REST также использует Evo Inflector для использования имени в группировках.

Вы можете запросить напрямую конкретную запись:

$ curl http://localhost:8080/people/53149b8e3004990b1af9f229
{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people/53149b8e3004990b1af9f229"
    }
  }
}
Это может показаться чисто веб-интерфейсом, но за сценой MongoDB БД.

В этом уроке рассматривается только один доменный объект. В более сложной системе, где доменные объекты связаны друг с другом, Spring Data REST отображает дополнительные ссылки, помогая переходить к связанным записям.

Нахождение всех ппользовательских запросов:

$ curl http://localhost:8080/people/search
{
  "_links" : {
    "findByLastName" : {
      "href" : "http://localhost:8080/people/search/findByLastName{?name}",
      "templated" : true
    }
  }
}

Вы можете увидеть URL для запроса, включающего параметр HTTP запроса name. Если обратите внимание, то он соответствует аннотации @Param("name"), встроенной в интерфейс.

Чтобы использовать запрос findByLastName, сделайте следующее:

$ curl http://localhost:8080/people/search/findByLastName?name=Baggins
{
  "_embedded" : {
    "persons" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/people/53149b8e3004990b1af9f229"
        }
      }
    } ]
  }
}

Т.к. вы описали возвращать List<Person> в коде, то он вернет все результаты. Если бы вы описали возвращать только Person, то он бы выбрал и вернул один их Person объектов. Поскольку это может быть непредсказуемым, вы скорее всего не захотите это делать для запросов, возвращающих несколько записей.

Вы можете также использовать PUT, PATCH или DELETE REST вызовы для замены, обновления или удаления существующих записей:

$ curl -X PUT -H "Content-Type:application/json" -d '{ "firstName": "Bilbo", "lastName": "Baggins" }' http://localhost:8080/people/53149b8e3004990b1af9f229
$ curl http://localhost:8080/people/53149b8e3004990b1af9f229
{
  "firstName" : "Bilbo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people/53149b8e3004990b1af9f229"
    }
  }
}
$ curl -X PATCH -H "Content-Type:application/json" -d '{ "firstName": "Bilbo Jr." }' http://localhost:8080/people/53149b8e3004990b1af9f229
$ curl http://localhost:8080/people/53149b8e3004990b1af9f229
{
  "firstName" : "Bilbo Jr.",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people/53149b8e3004990b1af9f229"
    }
  }
}
PUT заменяет всю запись. Отсутствующие поля будут заменены на null. PATCH может быть использован для обновления конкретных элементов.

Вы можете удалить записи:

$ curl -X DELETE http://localhost:8080/people/53149b8e3004990b1af9f229
$ curl http://localhost:8080/people
{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}

Очень удобной стороной этого Hypermedia-driven Interface является то, как вы можете обнаруживать все RESTful точки выхода, используя curl(либо любой другой REST клиент). При этом нет необходимости в обмене официальным договором или документом интерфейса с вашими клиентами.

Итог

Поздравляем! Вы только что написали простое приложение с гипермедиа RESTful интерфейсом на клиенте и MongoDB на сервере.

С оригинальным текстом урока вы можете ознакомиться на spring.io.

comments powered by Disqus