Spring Data Neo4j
Spring Data Neo4j — это проект в рамках проекта
Spring Data, который предоставляет расширение
модели программирования Spring для написания приложений, использующих
Neo4j в качестве графической базы данных.
Spring Data, который предоставляет расширение
модели программирования Spring для написания приложений, использующих
Neo4j в качестве графической базы данных.
Чтобы написать тесты с использованием
NoSQLUnit для приложений
Spring Data Neo4j , вам не нужно ничего особенного, кроме того, что
Spring Data Neo4j использует специальное свойство, называемое
typeузлами и связями графа, в котором хранится полное имя класса этой сущности.Помимо
typeсвойства на уровне узла / отношения, нам также необходимо создать один индекс для узлов и один индекс для отношений. В случае узловtypesтребуется индексное имя, аrel_typesдля отношений требуется. В обоих случаях мы должны установить значение ключа равнымclassNameзначению полного имени класса.Сопоставление типовIndexingNodeTypeRepresentationStrategy и IndexingRelationshipTypeRepresentationStrategyиспользуется в качестве реализации сопоставления типов по умолчанию, но вы также можете указать, SubReferenceNodeTypeRepresentationStrategyкакие типы сущностей хранятся в дереве на графике, представляющем иерархию типов и интерфейсов, или вы можете настроить еще больше, реализовав NodeTypeRepresentationStrategyинтерфейс. |
Руки на работе
заявка
Звездный флот попросил нас разработать приложение для хранения всех членов звёздного флота , их отношений с другими членами звёздного флота и корабля, на котором они служат.
Лучший способ реализовать это требование — использовать
базу данных Neo4j в качестве бэкэнд-системы. Кроме того,
Spring Data Neo4j используется на постоянном уровне.
Это приложение смоделировано на два
класса Java , один для членов и другой для звездолетов. Обратите внимание, что для этого примера в отношениях нет свойств, поэтому моделируются только узлы.
Член класса
@NodeEntity
public class Member {
private static final String COMMANDS = "COMMANDS";
@GraphId Long nodeId;
private String name;
private Starship assignedStarship;
public Member() {
super();
}
public Member(String name) {
this.name = name;
}
@Fetch @RelatedTo(type=COMMANDS, direction=Direction.OUTGOING)
private Set<Member> commands;
public void command(Member member) {
this.commands.add(member);
}
public Set<Member> commands() {
return this.commands;
}
public Starship getAssignedStarship() {
return assignedStarship;
}
public String getName() {
return name;
}
public void assignedIn(Starship starship) {
this.assignedStarship = starship;
}
//Equals and Hash methods
}
@NodeEntity
public class Member {
private static final String COMMANDS = "COMMANDS";
@GraphId Long nodeId;
private String name;
private Starship assignedStarship;
public Member() {
super();
}
public Member(String name) {
this.name = name;
}
@Fetch @RelatedTo(type=COMMANDS, direction=Direction.OUTGOING)
private Set<Member> commands;
public void command(Member member) {
this.commands.add(member);
}
public Set<Member> commands() {
return this.commands;
}
public Starship getAssignedStarship() {
return assignedStarship;
}
public String getName() {
return name;
}
public void assignedIn(Starship starship) {
this.assignedStarship = starship;
}
//Equals and Hash methods
}
|
Звездный класс
@NodeEntity
public class Starship {
private static final String ASSIGNED = "assignedStarship";
@GraphId Long nodeId;
private String starship;
public Starship() {
super();
}
public Starship(String starship) {
this.starship = starship;
}
@RelatedTo(type = ASSIGNED, direction=Direction.INCOMING)
private Set<Member> crew;
public String getStarship() {
return starship;
}
public void setStarship(String starship) {
this.starship = starship;
}
//Equals and Hash methods
}
Помимо классов моделей нам также нужны два репозитория для реализации операций CRUD и
файл контекста
xml Spring.Spring Data Neo4j использует
инфраструктуру Spring Data Commons, позволяющую нам создавать основанные на интерфейсе композиции репозиториев, обеспечивая реализации по умолчанию для определенных операций.
Класс MemberRepository
public interface MemberRepository extends GraphRepository<Member>,
RelationshipOperationsRepository<Member> {
Member findByName(String name);
}
Смотрите, кроме операций , предусмотренных
GrapRepositoryинтерфейсом , какsave,findAll,findById, … мы определяем один метод запроса тоже называетсяfindByName.Репозитории Spring Data Neo4j (и большинство проектов
Spring Data ) предоставляют механизм для определения запросов с использованием известного
подхода Ruby on Rails для определения запросов поиска.
StarshipRepository class
public interface StarshipRepository extends GraphRepository<Starship>,
RelationshipOperationsRepository<Starship> {
}
файл контекста приложения
<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:neo4j="http://www.springframework.org/schema/data/neo4j"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/data/neo4j
http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd">
<context:component-scan base-package="com.lordofthejars.nosqlunit.springdata.neo4j"/>
<context:annotation-config/>
<neo4j:repositories base-package="com.lordofthejars.nosqlunit.springdata.repository"/>
</beans>
тестирование
Модульное тестирование
Как было сказано ранее, для написания наборов данных для
Spring Data Neo4j нам не нужно делать ничего особенного, кроме правильного создания свойств узла и отношений и определения необходимых индексов. Давайте посмотрим на набор данных, используемый для тестирования
findByNameметода,заполнив базу данных Neo4j .
файл star-trek-TNG-dataset.xml
<?xml version="1.0" ?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key id="name" for="node" attr.name="name" attr.type="string"></key>
<key id="__type__" for="node" attr.name="__type__" attr.type="string"></key>
<key id="starship" for="node" attr.name="starship" attr.type="string"></key>
<graph id="G" edgedefault="directed">
<node id="3">
<data key="__type__">com.lordofthejars.nosqlunit.springdata.neo4j.Member</data>
<data key="name">Jean-Luc Picard</data>
<index name="__types__" key="className">com.lordofthejars.nosqlunit.springdata.neo4j.Member</index>
</node>
<node id="1">
<data key="__type__">com.lordofthejars.nosqlunit.springdata.neo4j.Member</data>
<data key="name">William Riker</data>
<index name="__types__" key="className">com.lordofthejars.nosqlunit.springdata.neo4j.Member</index>
</node>
<node id="4">
<data key="__type__">com.lordofthejars.nosqlunit.springdata.neo4j.Starship</data>
<data key="starship">NCC-1701-E</data>
<index name="__types__" key="className">com.lordofthejars.nosqlunit.springdata.neo4j.Starship</index>
</node>
<edge id="11" source="3" target="4" label="assignedStarship"></edge>
<edge id="12" source="1" target="4" label="assignedStarship"></edge>
<edge id="13" source="3" target="1" label="COMMANDS"></edge>
</graph>
</graphml>
Обратите внимание, что у каждого узла есть хотя бы одно
typeсвойство с полным именем класса и индексом с именемtypes, ключомclassNameи полным именем класса в качестве значения.Следующий шаг — настройка контекста приложения для модульных тестов.
приложения контекстно-погруженный neo4j.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:neo4j="http://www.springframework.org/schema/data/neo4j"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/data/neo4j
http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd">
<import resource="classpath:com/lordofthejars/nosqlunit/springdata/neo4j/application-context.xml"/>
<neo4j:config storeDirectory="target/config-test"/>
</beans>
Notice that we are using
Neo4j namespace for instantiating an embedded
Neo4j database.
Neo4j namespace for instantiating an embedded
Neo4j database.
And now we can write the
JUnit test case:
JUnit test case:
WhenInformationAboutAMemberIsRequired
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("application-context-embedded-neo4j.xml")
public class WhenInformationAboutAMemberIsRequired {
@Autowired
private MemberRepository memberRepository;
@Autowired
private StarshipRepository starshipRepository;
@Autowired
private ApplicationContext applicationContext;
@Rule
public Neo4jRule neo4jRule = newNeo4jRule()
.defaultSpringGraphDatabaseServiceNeo4j();
@Test
@UsingDataSet(locations = "star-trek-TNG-dataset.xml", loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void information_about_starship_where_serves_and_members_under_his_service_should_be_retrieved() {
Member jeanLuc = memberRepository.findByName("Jean-Luc Picard");
assertThat(jeanLuc, is(createMember("Jean-Luc Picard")));
assertThat(jeanLuc.commands(), containsInAnyOrder(createMember("William Riker")));
Starship starship = starshipRepository.findOne(jeanLuc.getAssignedStarship().nodeId);
assertThat(starship, is(createStarship("NCC-1701-E")));
}
private Object createStarship(String starship) {
return new Starship(starship);
}
private static Member createMember(String memberName) {
return new Member(memberName);
}
}
There are some important points in the previous class to take under consideration:
-
Recall that we need to use
Spring
ApplicationContextobject to retrieve embedded
Neo4j instance defined into
Spring application context. -
Since lifecycle of database is managed by
Spring Data container, there is no need to define any
NoSQLUnit lifecycle manager.
Integration Test
Unit tests are usually run against embedded in-memory instances, but in production environment you may require access to external
Neo4j servers by using
Rest connection, or in case of
Spring Data Neo4j instantiating
Neo4j servers by using
Rest connection, or in case of
Spring Data Neo4j instantiating
SpringRestGraphDatabase class. You need to write tests to validate that your application still works when you integrate your code with a remote server, and this tests are typically known as integration tests.
To write integration tests for our application is as easy as defining an application context with
NoSQLUnit to control the lifecycle of
Neo4j database.
SpringRestGraphDatabase and allowNoSQLUnit to control the lifecycle of
Neo4j database.
.application-context-managed-neo4j.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:neo4j="http://www.springframework.org/schema/data/neo4j"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/data/neo4j
http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd">
<import resource="classpath:com/lordofthejars/nosqlunit/springdata/neo4j/application-context.xml"/>
<bean id="graphDatabaseService" class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase">
<constructor-arg index="0" value="http://localhost:7474/db/data"></constructor-arg>
</bean>
<neo4j:config graphDatabaseService="graphDatabaseService"/>
</beans>
Note how instead of registering an embedded instance, we are configuring
Neo4j server.
SpringRestGraphDatabase class to connect to localhost server. And let’s implement an integration test which verifies that all starships can be retrieved fromNeo4j server.
WhenInformationAboutAMemberIsRequired
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("application-context-managed-neo4j.xml")
public class WhenInformationAboutStarshipsAreRequired {
@ClassRule
public static ManagedNeoServer managedNeoServer = newManagedNeo4jServerRule()
.neo4jPath(
"/Users/alexsotobueno/Applications/neo4j-community-1.7.2")
.build();
@Autowired
private StarshipRepository starshipRepository;
@Autowired
private ApplicationContext applicationContext;
@Rule
public Neo4jRule neo4jRule = newNeo4jRule()
.defaultSpringGraphDatabaseServiceNeo4j();
@Test
@UsingDataSet(locations = "star-trek-TNG-dataset.xml", loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void information_about_starship_where_serves_and_members_under_his_service_should_be_retrieved() {
EndResult<Starship> allStarship = starshipRepository.findAll();
assertThat(allStarship, containsInAnyOrder(createStarship("NCC-1701-E")));
}
private Object createStarship(String starship) {
return new Starship(starship);
}
}
Because
application context, in our case it will return the defined
defaultSpringGraphDatabaseServiceNeo4j method returns aGraphDatabaseService instance defined intoapplication context, in our case it will return the defined
SpringRestGraphDatabase instance.
Conclusions
There is not much difference between writing tests for none
Spring Data Neo4j applications than the ones they use it. Only keep in mind to define correctly the
Spring Data Neo4j applications than the ones they use it. Only keep in mind to define correctly the
type property and create required indexes.
Also see that from the point of view of
NoSQLUnit there is no difference between writing unit or integration tests, apart of lifecycle management of the database engine.
NoSQLUnit there is no difference between writing unit or integration tests, apart of lifecycle management of the database engine.
We keep learning,
Alex.
