Доступ к данным Neo4j
Этот урок освещает процесс создания приложения c Neo4J, которое использует Spring Data.
Что вы создадите
Вы будете использовать Neo4j NoSQL графовое хранилище для сборки встроенного Neo4j сервера, сохранения сущностей и разработки запросов.
Что вам потребуется
- Примерно 15 минут свободного времени
- Любимый текстовый редактор или IDE
- JDK 6 и выше
- Gradle 1.11+ или Maven 3.0+
- Вы также можете импортировать код этого урока, а также просматривать web-страницы прямо из Spring Tool Suite (STS), собственно как и работать дальше из него.
Как проходить этот урок
Как и большинство уроков по Spring, вы можете начать с нуля и выполнять каждый шаг, либо пропустить базовые шаги, которые вам уже знакомы. В любом случае, вы в конечном итоге получите рабочий код.
Чтобы начать с нуля, перейдите в Настройка проекта.
Чтобы пропустить базовые шаги, выполните следующее:
- Загрузите и
распакуйте архив с кодом этого урока, либо кнонируйте из репозитория с помощью
Git:
git clone https://github.com/spring-guides/gs-accessing-data-neo4j.git
- Перейдите в каталог
gs-accessing-data-neo4j/initial
- Забегая вперед, опишите простую сущность
Когда вы закончите, можете сравнить получившийся результат с образцом в gs-accessing-data-neo4j/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 "http://repo.spring.io/libs-release" }
mavenLocal()
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.8.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
jar {
baseName = 'gs-accessing-data-neo4j'
version = '0.1.0'
}
repositories {
mavenLocal()
mavenCentral()
maven { url "http://repo.spring.io/libs-release" }
maven { url "http://m2.neo4j.org" }
}
dependencies {
compile("org.springframework.boot:spring-boot-starter")
compile("org.springframework:spring-context")
compile("org.springframework:spring-tx")
compile("org.springframework.data:spring-data-neo4j")
compile("org.hibernate:hibernate-validator")
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 выбранным набором версий
Описание простой сущности
Neo4j описывает сущности и их взаимосвязи, оба из этих сторон одинаково важны.
Представьте себе, что вы моделируете систему, где вы сохраняете запись о каждом человеке.
Но вы также хотите отслеживать и тех, с кем он работает (teammates
в данном примере).
С Neo4j вы можете описать все это несколькими простыми аннотациями.
src/main/java/hello/Person.java
package hello;
import java.util.HashSet;
import java.util.Set;
import org.neo4j.graphdb.Direction;
import org.springframework.data.neo4j.annotation.Fetch;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelatedTo;
@NodeEntity
public class Person {
@GraphId Long id;
public String name;
public Person() {}
public Person(String name) { this.name = name; }
@RelatedTo(type="TEAMMATE", direction=Direction.BOTH)
public @Fetch Set<Person> teammates;
public void worksWith(Person person) {
if (teammates == null) {
teammates = new HashSet<Person>();
}
teammates.add(person);
}
public String toString() {
String results = name + "'s teammates include\n";
if (teammates != null) {
for (Person person : teammates) {
results += "\t- " + person.name + "\n";
}
}
return results;
}
}
Здесь у вас есть класс Person
с одним лишь атрибутом name
.
У вас есть два конструктора, один без, а другой с параметром name
. Для
использования Neo4j далее, вам необходим конструктор без параметров.
Следующей важной частью является установка teammates
. Это простой
Set<Person>
, но помеченный как @RelatedTo
. Это означает, что
каждый элемент этого набора сответствует отдельному узлу Person
. Обратите внимание,
что направление установлено в BOTH
. Это означает, что когда вы генерируете
TEAMMATE
взаимосвязь в одном направлении, оно существует и в другом направлении.
Есть ещё @Fetch
аннотация в этом поле. Это означает, что элементы будут возвращены
сразу. Иначе, вам пришлось бы использовать neo4jTemplate.fetch()
.
В методе worksWith()
вы можете легко связать людей вместе.
И наконец, у вас есть метод toString()
для печати имени человека и его коллег.
Создание простых запросов
Spring Data Neo4j ориентирована на хранение данных в Neo4j. Но он наследует функциональность от проекта Spring Data Commons, включая умение составлять запросы. Фактически, вам не нужно изучать язык запросов Neo4j, а можно просто написать несколько методов и запросы будут написаны за вас.
Чтобы увидеть, как это работает, создайте интерфейс запросов узлов Person
.
src/main/java/hello/PersonRepository.java
package hello;
import org.springframework.data.repository.GraphRepository;
public interface PersonRepository extends GraphRepository<Person, String> {
Person findByName(String name);
Iterable<Person> findByTeammatesName(String name);
}
PersonRepository
расширяет GraphRepository
класс и указывает тип,
с которым он работает: Person
. Из коробки этот интерфейс идет с множеством
операций, включая стандартные CRUD (create-read-update-delete) операции.
Но вы можете определить и другие запросы, которые вам необходимы, просто описав сигнатуры
метода. В данном случае, вы добавили findByName
, он ищет узлы типа Person
и находит те, которые соответствуют по name
. У вас также есть findByTeammatesName
,
который ищет Person
узел, проверяя в каждом из элементов поля teammates
на
соответствие значения name
.
Приступим к работе и посмотрим, что он найдет!
Создание класса Application
Создайте класс Application со всеми компонентами.
src/main/java/hello/Application.java
package hello;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.kernel.impl.util.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.core.GraphDatabase;
import java.io.File;
@Configuration
@EnableNeo4jRepositories(basePackages = "hello")
public class Application extends Neo4jConfiguration implements CommandLineRunner {
public Application() {
setBasePackage("hello");
}
@Bean
GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabase("accessingdataneo4j.db");
}
@Autowired
PersonRepository personRepository;
@Autowired
GraphDatabase graphDatabase;
public void run(String... args) throws Exception {
Person greg = new Person("Greg");
Person roy = new Person("Roy");
Person craig = new Person("Craig");
System.out.println("Before linking up with Neo4j...");
for (Person person : new Person[]{greg, roy, craig}) {
System.out.println(person);
}
Transaction tx = graphDatabase.beginTx();
try {
personRepository.save(greg);
personRepository.save(roy);
personRepository.save(craig);
greg = personRepository.findByName(greg.name);
greg.worksWith(roy);
greg.worksWith(craig);
personRepository.save(greg);
roy = personRepository.findByName(roy.name);
roy.worksWith(craig);
// Мы уже знаем, что Рой работает с Грегом
personRepository.save(roy);
// Мы уже знаем, что Крейг работает с Роем и Грегом
System.out.println("Lookup each person by name...");
for (String name: new String[]{greg.name, roy.name, craig.name}) {
System.out.println(personRepository.findByName(name));
}
System.out.println("Looking up who works with Greg...");
for (Person person : personRepository.findByTeammatesName("Greg")) {
System.out.println(person.name + " works with Greg.");
}
tx.success();
} finally {
tx.close();
}
}
public static void main(String[] args) throws Exception {
FileUtils.deleteRecursively(new File("accessingdataneo4j.db"));
SpringApplication.run(Application.class, args);
}
}
В конфигурацию вам необходимо добавить аннотацию @EnableNeo4jRepositories
,
а также наследоваться от класса Neo4jConfiguration
для удобства работы с
нужными компонентами.
Единственное, чего не хватает, это бина службы графовой БД. В данном случае вы используете
EmbeddedGraphDatabase
, который создает и переиспользует хранилище в виде файла
accessingdataneo4j.db.
Вы вызываете экземпляр PersonRepository
, описанный ранее. Spring Data Neo4j будет
динамически создавать нужный класс, реализующий этот интерфейс и при необходимости использовать
нужный код запроса для удовлетворения потребностей интерфейса.
public static void main
использует Spring Boot SpringApplication.run()
для запуска приложения и CommandLineRunner
для сборки связей.
В данном случае вы создаете три экземпляра Person
, Greg,
Roy и Craig. Первоначально они существуют только в памяти.
Важно отметить, что на данный момент пока ни один из них не является коллегой друг другу.
Чтобы что-то сохранить в Neo4j, вы должны начать транзакцию, используя graphDatabase
.
здесь вы будете сохранять каждого человека. Затем вы выбираете каждого из них и связываете вместе.
Вначале вы находите Грега и указываете, что он работает с Роем и Крейгом, затем сохраняете его снова.
Помните, что связь между ними была определена как BOTH
, а значит двунаправлена. Это
означает, что Рой и Крейг будут также обновлены.
Поэтому, когда вам необходимо обновить Роя, важно извлечь запись о нем из Neo4j первой. Вам нужен последние данные о коллегах Роя до добавления Крейга в его список.
Почему нет кода для получения Крейга и добавления у него связей? Потому что вы уже сделали это! Грег ранее был связан с Крейгом как коллега, так же как и с Роем. Это означает, что вам не нужно снова обновлять связи Крейга. Вы можете убедиться в этом, когда печатается информация о каждом из них и их связях.
В заключение, проверяется другой запрос, где наоборот, отвечается на вопрос "кто работает с кем?".
Сборка исполняемого JAR
Вы можете собрать единый исполняемый JAR-файл, который содержит все необходимые зависимости, классы и ресурсы. Это делает его легким в загрузке, версионировании и развертывании сервиса как приложения на протяжении всего периода разработки, на различных средах и так далее.
./gradlew build
Затем вы можете запустить JAR-файл:
java -jar build/libs/gs-accessing-data-neo4j-0.1.0.jar
Если вы используете Maven, вы можете запустить приложение, используя mvn spring-boot:run
,
либо вы можете собрать приложение с mvn clean package
и запустить JAR примерно так:
java -jar target/gs-accessing-data-neo4j-0.1.0.jar
Запуск сервиса
Если вы используете Gradle, вы можете запустить ваш сервис из командной строки:
./gradlew clean build && java -jar build/libs/gs-accessing-data-neo4j-0.1.0.jar
mvn clean package && java -jar target/gs-accessing-data-neo4j-0.1.0.jar
.
Как вариант, вы можете запустить ваш сервис напрямую из Gradle примерно так:
./gradlew bootRun
mvn spring-boot:run
.Вы должны увидеть следующее:
Before linking up with Neo4j... Greg's teammates include Roy's teammates include Craig's teammates include Lookup each person by name... Greg's teammates include - Craig - Roy Roy's teammates include - Craig - Greg Craig's teammates include - Roy - Greg Looking up who works with Greg... Roy works with Greg. Craig works with Greg.
Итог
Поздравляем! Вы только что настроили встроенный Neo4j сервер, сохранили несколько простых, но связанных сущностей и разработали несколько простых запросов.
С оригинальным текстом урока вы можете ознакомиться на spring.io.
comments powered by Disqus