Доступ к MongoDB-данным через GORM
Этот урок освещает пошаговое создание Spring Boot приложения с использованием Grails Object Relational Mapper (GORM) и MongoDB. Приложение использует плагин GORM для MongoDB
Что вы создадите
Вы создадите Spring приложение, которое сохраняет геопространственные данные в БД MongoDB с использованием GORM.
Что вам потребуется
- Примерно 15 минут свободного времени
- Любимый текстовый редактор или IDE
- JDK 7 и выше
- Gradle 1.8+
- Вы также можете импортировать код этого урока, а также просматривать web-страницы прямо из Spring Tool Suite (STS), собственно как и работать дальше из него.
Как проходить этот урок
Как и большинство уроков по Spring, вы можете начать с нуля и выполнять каждый шаг, либо пропустить базовые шаги, которые вам уже знакомы. В любом случае, вы в конечном итоге получите рабочий код.
Чтобы начать с нуля, перейдите в Настройка проекта.
Чтобы пропустить базовые шаги, выполните следующее:
-
Загрузите и
распакуйте архив с кодом этого урока, либо кнонируйте из репозитория с помощью
Git:
git clone https://github.com/spring-guides/gs-accessing-data-gorm-mongodb.git
- Перейдите в каталог
gs-accessing-data-gorm-mongodb/initial
- Забегая вперед, создайте GORM сущность
Когда вы закончите, можете сравнить получившийся результат с образцом в gs-accessing-data-gorm-mongodb/complete
.
Настройка проекта
Для начала вам необходимо настроить базовый скрипт сборки. Вы можете использовать любую систему сборки, которая вам нравится для сборки проетов Spring, но в этом уроке рассмотрим код для работы с Gradle. Если вы не знакомы с ней, ознакомьтесь с соответсвующим уроком Сборка Java-проекта с использованием Gradle.
Создание структуры каталогов
В выбранном вами каталоге проекта создайте следующую структуру каталогов; к примеру,
командой mkdir -p src/main/groovy/hello
для *nix систем:
└── src └── main └── groovy └── cities
Создание файла сборки Gradle
Ниже представлен начальный файл сборки Gradle. Если вы используете Spring Tool Suite (STS), то можете импортировать урок прямо из него.
pom.xml
, вы найдете, что указана версия для maven-compiler-plugin.
В общем, это не рекомендуется делать. В данном случае он предназначен для решения проблем с нашей CI системы,
которая по умолчанию имеет старую(до Java 5) версию этого плагина.
build.gradle
buildscript {
repositories {
mavenLocal()
mavenCentral()
maven { url "https://repo.spring.io/milestone" }
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: 'groovy'
apply plugin: 'spring-boot'
jar {
baseName = 'gs-spring-boot-gorm-mongodb'
version = '0.1.0'
}
repositories {
mavenLocal()
mavenCentral()
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.grails:gorm-mongodb-spring-boot:1.1.0.RELEASE")
runtime("org.springframework.boot:spring-boot-starter-data-mongodb")
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 выбранным набором версий
Создание GORM сущности
В этом примере мы моделируем простую сущность Person
с использованием GORM:
src/main/groovy/cities/City.groovy
package cities
import grails.persistence.*
import grails.mongodb.geo.*
import org.bson.types.ObjectId
@Entity
class City {
ObjectId id
String name
Point location
static constraints = {
name blank:false
location nullable:false
}
static mapping = {
location geoIndex:'2dsphere'
}
}
Блок ограничения
описывает любые правила валидации. В данном случае, накладывается ограничение только на использование непустого поля
name
и не равного значению null
поля location
.
Блок маппинга указывает на
то, что свойство location
должно быть проиндексировано с использованием
геопространственного 2dsphere
индекса.
Свойство location
имеет тип grails.mongodb.geo.Point
, который сохраняет в MongoDB
как GeoJSON Point.
Создание контроллера ресурсов
Создайте контроллер для вашего Groovy Spring приложения:
src/main/groovy/cities/CityController.groovy
package cities
import com.mongodb.BasicDBObject
import javax.annotation.PostConstruct;
import org.springframework.web.bind.annotation.*
import org.springframework.http.*
import static org.springframework.web.bind.annotation.RequestMethod.*
import grails.mongodb.geo.*
@RestController
class CityController {
@RequestMapping(value="/", method = GET)
List index() {
City.list().collect { [name: it.name] }
}
@RequestMapping(value="/near/{cityName}", method = GET)
ResponseEntity near(@PathVariable String cityName) {
def city = City.where { name == cityName }.find()
if(city) {
List<City> closest = City.findAllByLocationNear(city.location)
return new ResponseEntity([name: closest[1].name], HttpStatus.OK)
}
else {
return new ResponseEntity(HttpStatus.NOT_FOUND)
}
}
@PostConstruct
void populateCities() {
City.withTransaction{
City.collection.remove(new BasicDBObject())
City.saveAll(
[ new City( name:"London",
location: Point.valueOf([-0.125487, 51.508515])),
new City( name:"Paris",
location: Point.valueOf([2.352222, 48.856614])),
new City( name:"New York",
location: Point.valueOf([-74.005973, 40.714353])),
new City( name:"San Francisco",
location: Point.valueOf([-122.419416, 37.774929])) ]
)
}
}
}
Пример описывает две REST точки выхода:
index
передает список всех известных городовnear
находит город, ближайший к указанному городу
Метод populateCities
выполняется при запуске, т.к. аннотирован @PostConstruct
.
Метод содержит некоторые первоначальные данные, для оперирования с ними в приложении. Обратите
внимание, как каждому городу передан экземпляр Point
местонахождения с указанием
долготы/широты:
@PostConstruct
void populateCities() {
City.withTransaction{
City.collection.remove(new BasicDBObject())
City.saveAll(
[ new City( name:"London",
location: Point.valueOf([-0.125487, 51.508515])),
new City( name:"Paris",
location: Point.valueOf([2.352222, 48.856614])),
new City( name:"New York",
location: Point.valueOf([-74.005973, 40.714353])),
new City( name:"San Francisco",
location: Point.valueOf([-122.419416, 37.774929])) ]
)
}
}
Методу near
название города в качестве параметра и затем производится поиск ближайших
городов. Обратите внимание, что первая запись в результате будет городом, который запросил сам себя,
потому что индекс 1 использован для поиска ближайшего города:
@RequestMapping(value="/near/{cityName}", method = GET)
ResponseEntity near(@PathVariable String cityName) {
def city = City.where { name == cityName }.find()
if(city) {
List<City> closest = City.findAllByLocationNear(city.location)
return new ResponseEntity([name: closest[1].name], HttpStatus.OK)
}
else {
return new ResponseEntity(HttpStatus.NOT_FOUND)
}
}
Далее вы увидите как запустить приложение и создать новую сущность Person
.
Создание приложения исполняемым
Несмотря на то, что пакет этого сервиса может быть в составе web-приложения и
WAR файлов,
более простой подход, продемонстрированный ниже создает отдельное самостоятельное приложение.
Вы упаковываете все в единый, исполняемый JAR-файл, который запускается через хорошо знакомый
старый main()
Java-метод. Попутно, вы используете поддержку Spring для встроенного
Tomcat
контейнера сервлетов как HTTP среду выполнения вместо развертывания на сторонний экземпляр.
src/main/groovy/cities/Application.groovy
package hello
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.ComponentScan
@EnableAutoConfiguration
@ComponentScan
class Application {
static void main(String[] args) {
SpringApplication.run Application, args
}
}
main()
метод передает управление вспомогательному классу
SpringApplication,
где Application.class
- аргумент его run()
метода. Это сообщает Spring
о чтении метаданных аннотации из Application
и управлении ею как компонента в
Spring application context.
Аннотация @ComponentScan
говорит Spring'у рекурсивно искать в пакете cities
и его потомках классы, помеченные прямо или косвенно Spring аннотацией
@Component.
Эта директива гарантирует, что Spring найдет и зарегистрирует CityController
,
потому что он отмечен @RestController
, который, в свою очередь, является своего рода
@Component
аннотацией.
Аннотация @EnableAutoConfiguration
переключает на приемлемое по умолчанию поведение, основанное на содержании вашего classpath. К примеру,
т.к. приложение зависит от встраиваемой версии Tomcat (tomcat-embed-core.jar), Tomcat сервер установлен
и сконфигурирован на приемлемое по умолчанию поведение от вашего имени. И т.к. приложение также
зависит от Spring MVC (spring-webmvc.jar), Spring MVC
DispatcherServlet
сконфигурирован и зарегестрирован для вас - web.xml
не нужен! Автоконфигурация полезный и
гибкий механизм. Подробную информацию смотрите в
API документации.
Сборка исполняемого JAR
Вы можете собрать единый исполняемый JAR-файл, который содержит все необходимые зависимости, классы и ресурсы. Это делает его легким в загрузке, версионировании и развертывании сервиса как приложения на протяжении всего периода разработки, на различных средах и так далее.
./gradlew build
Затем вы можете запустить JAR-файл:
java -jar build/libs/gs-accessing-data-gorm-mongodb-0.1.0.jar
Если вы используете Maven, вы можете запустить приложение, используя mvn spring-boot:run
,
либо вы можете собрать приложение с mvn clean package
и запустить JAR примерно так:
java -jar target/gs-accessing-data-gorm-mongodb-0.1.0.jar
Запуск приложения
Если вы используете Gradle, вы можете запустить ваш сервис из командной строки:
./gradlew clean build && java -jar build/libs/gs-accessing-data-gorm-mongodb-0.1.0.jar
mvn clean package && java -jar target/gs-accessing-data-gorm-mongodb-0.1.0.jar
.
Как вариант, вы можете запустить ваш сервис напрямую из Gradle примерно так:
./gradlew bootRun
mvn spring-boot:run
.Тестирование приложения
Когда приложение запустится, вы можете получить список всех городов. К примеру, используя *nix инструмент curl
:
$ curl -i http://localhost:8080/
Результат ответа, показанном ниже, будет иметь список городов в JSON формате:
$ curl -i http://localhost:8080/ HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Fri, 21 Mar 2014 07:52:34 GMT [{"name":"London"},{"name":"Paris"},{"name":"New York"},{"name":"San Francisco"}]
Затем вы можете отправить GET
запрос на /near
для поиска ближайшего города
к указанному имени городу:
$ curl -i http://localhost:8080/near/Paris HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Fri, 21 Mar 2014 07:53:47 GMT {"name":"London"}
Поддержка Groovy Script
Предыдущий пример требовал от вас установки системы сборки Gradle или Maven, но вы можете также использовать GORM в простом Groovy скрипте.
Как пример, создайте новый файл app.groovy и поместите в него следующий код:
app.groovy
@Grab("org.grails:gorm-mongodb-spring-boot:1.1.0.RELEASE")
@Grab("org.mongodb:mongo-java-driver:2.12.2")
import grails.persistence.*
import grails.mongodb.geo.*
import org.bson.types.ObjectId
import com.mongodb.BasicDBObject
import static org.springframework.web.bind.annotation.RequestMethod.*
@RestController
class CityController {
@RequestMapping(value="/", method = GET)
List index() {
City.list().collect { [name: it.name] }
}
@RequestMapping(value="/near/{cityName}", method = GET)
ResponseEntity near(@PathVariable String cityName) {
def city = City.where { name == cityName }.find()
if(city) {
List<City> closest = City.findAllByLocationNear(city.location)
return new ResponseEntity([name: closest[1].name], HttpStatus.OK)
}
else {
return new ResponseEntity(HttpStatus.NOT_FOUND)
}
}
@PostConstruct
void populateCities() {
City.withTransaction{
City.collection.remove(new BasicDBObject())
City.saveAll(
[ new City(name:"London",
location: Point.valueOf([-0.125487, 51.508515])),
new City(name:"Paris",
location: Point.valueOf([2.352222, 48.856614])),
new City(name:"New York",
location: Point.valueOf([-74.005973, 40.714353])),
new City(name:"San Francisco",
location: Point.valueOf([-122.419416, 37.774929]))
]
)
}
}
}
@Entity
class City {
ObjectId id
String name
Point location
static constraints = {
name blank:false
location nullable:false
}
static mapping = {
location geoIndex:'2dsphere'
}
}
Далее, установите Spring Boot CLI.
Запустите его как показано ниже:
$ spring run app.groovy
Настройка MongoDB соединения
Для настройки MongoDB соединения вы можете использовать файл приложения application.yml
:
src/main/groovy/cities/Application.groovy
spring:
mongodb:
host: "localhost"
databaseName: "citydb"
options:
connectionsPerHost: 20
Доступные параметры конфигурации такие же, как и те, что предоставляет плагин GORM для MongoDB.
Как вариант, если у вас есть свои требования к конфигурации, то вы можете настроить Spring бин
типа com.mongodb.Mongo
в вашем приложении.
Итог
Поздравляем! Вы только что создали Spring приложение с использованием GORM для доступа к данным MongoDB.
С оригинальным текстом урока вы можете ознакомиться на spring.io.
comments powered by Disqus