Позвольте мне объяснить, как это работает, предоставив очень простые примеры. Как и в предыдущих статьях, мы будем создавать приложение Spring с использованием недавно выпущенной версии 1.0 проекта Spring Data MongoDB . Наш файл POM содержит очень простые зависимости, не более того.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
4.0.0mongodbcom.example.spring0.0.1-SNAPSHOTjar UTF-8 3.0.7.RELEASE org.springframework.data spring-data-mongodb 1.0.0.RELEASE org.springframework spring-beans org.springframework spring-expression cglib cglib-nodep 2.2 log4j log4j 1.2.16 org.mongodb mongo-java-driver 2.7.2 org.springframework spring-core ${spring.version} org.springframework spring-context ${spring.version} org.springframework spring-context-support ${spring.version} org.apache.maven.plugins maven-compiler-plugin 2.3.2 1.6 1.6 |
Чтобы правильно настроить контекст Spring , я буду использовать конфигурационный подход с использованием классов Java. Я все больше и больше выступаю за использование этого стиля, поскольку он обеспечивает строго типизированную конфигурацию, и большинство ошибок могут быть обнаружены во время компиляции, больше нет необходимости проверять ваши XML-файлы. Вот как это выглядит:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package com.example.mongodb.hierarchical;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.mongodb.core.MongoFactoryBean;import org.springframework.data.mongodb.core.MongoTemplate;import org.springframework.data.mongodb.core.SimpleMongoDbFactory;@Configurationpublic class AppConfig { @Bean public MongoFactoryBean mongo() { final MongoFactoryBean factory = new MongoFactoryBean(); factory.setHost( "localhost" ); return factory; } @Bean public SimpleMongoDbFactory mongoDbFactory() throws Exception{ return new SimpleMongoDbFactory( mongo().getObject(), "hierarchical" ); } @Bean public MongoTemplate mongoTemplate() throws Exception { return new MongoTemplate( mongoDbFactory() ); } @Bean public IDocumentHierarchyService documentHierarchyService() throws Exception { return new DocumentHierarchyService( mongoTemplate() ); }} |
Это довольно мило и понятно. Спасибо, весна, ребята! Теперь все стандартные вещи готовы. Давайте перейдем к интересной части: документы. Наша база данных будет содержать коллекцию документов, в которой хранятся документы типа SimpleDocument. Мы описываем это, используя аннотации Spring Data MongoDB для SimpleDocument POJO.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
package com.example.mongodb.hierarchical;import java.util.Collection;import java.util.HashSet;import org.springframework.data.annotation.Id;import org.springframework.data.annotation.Transient;import org.springframework.data.mongodb.core.mapping.Document;import org.springframework.data.mongodb.core.mapping.Field;@Document( collection = "documents" )public class SimpleDocument { public static final String PATH_SEPARATOR = "."; @Id private String id; @Field private String name; @Field private String path; // We won't store this collection as part of document but will build it on demand @Transient private Collection< SimpleDocument > documents = new HashSet< SimpleDocument >(); public SimpleDocument() { } public SimpleDocument( final String id, final String name ) { this.id = id; this.name = name; this.path = id; } public SimpleDocument( final String id, final String name, final SimpleDocument parent ) { this( id, name ); this.path = parent.getPath() + PATH_SEPARATOR + id; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public Collection< SimpleDocument > getDocuments() { return documents; }} |
Позвольте мне объяснить несколько вещей здесь. Во-первых, путь магического свойства: это ключ для построения и запроса через нашу иерархию. Путь содержит идентификаторы всех родителей документа, обычно разделенные каким-то разделителем, в нашем случае просто . (точка) Хранение иерархических отношений документа таким способом позволяет быстро построить иерархию, искать и перемещаться. Во-вторых, обратите внимание на временную коллекцию документов : эта непостоянная коллекция создается постоянным провайдером и содержит все документы-потомки (которые, в случае, также содержат собственных потомков). Давайте посмотрим на это в действии, изучив реализацию метода find :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
package com.example.mongodb.hierarchical;import java.util.Arrays;import java.util.Collection;import java.util.HashMap;import java.util.Map;import org.springframework.data.mongodb.core.MongoOperations;import org.springframework.data.mongodb.core.query.Criteria;import org.springframework.data.mongodb.core.query.Query;public class DocumentHierarchyService { private MongoOperations template; public DocumentHierarchyService( final MongoOperations template ) { this.template = template; } @Override public SimpleDocument find( final String id ) { final SimpleDocument document = template.findOne( Query.query( new Criteria( "id" ).is( id ) ), SimpleDocument.class ); if( document == null ) { return document; } return build( document, template.find( Query.query( new Criteria( "path" ).regex( "^" + id + "[.]" ) ), SimpleDocument.class ) ); } private SimpleDocument build( final SimpleDocument root, final Collection< SimpleDocument > documents ) { final Map< String, SimpleDocument > map = new HashMap< String, SimpleDocument >(); for( final SimpleDocument document: documents ) { map.put( document.getPath(), document ); } for( final SimpleDocument document: documents ) { map.put( document.getPath(), document ); final String path = document .getPath() .substring( 0, document.getPath().lastIndexOf( SimpleDocument.PATH_SEPARATOR ) ); if( path.equals( root.getPath() ) ) { root.getDocuments().add( document ); } else { final SimpleDocument parent = map.get( path ); if( parent != null ) { parent.getDocuments().add( document ); } } } return root; }} |
Как видите, чтобы получить один документ с целой иерархией, нам нужно выполнить всего два запроса (но более оптимальный алгоритм может сократить его до одного запроса). Вот пример иерархии и результат чтения корневого документа из MongoDB
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
template.dropCollection( SimpleDocument.class );final SimpleDocument parent = new SimpleDocument( "1", "Parent 1" );final SimpleDocument child1 = new SimpleDocument( "2", "Child 1.1", parent );final SimpleDocument child11 = new SimpleDocument( "3", "Child 1.1.1", child1 );final SimpleDocument child12 = new SimpleDocument( "4", "Child 1.1.2", child1 );final SimpleDocument child121 = new SimpleDocument( "5", "Child 1.1.2.1", child12 );final SimpleDocument child13 = new SimpleDocument( "6", "Child 1.1.3", child1 );final SimpleDocument child2 = new SimpleDocument( "7", "Child 1.2", parent );template.insertAll( Arrays.asList( parent, child1, child11, child12, child121, child13, child2 ) );...final ApplicationContext context = new AnnotationConfigApplicationContext( AppConfig.class );final IDocumentHierarchyService service = context.getBean( IDocumentHierarchyService.class );final SimpleDocument document = service.find( "1" );// Printing document show following hierarchy://// Parent 1// |-- Child 1.1// |-- Child 1.1.1// |-- Child 1.1.3// |-- Child 1.1.2// |-- Child 1.1.2.1// |-- Child 1.2 |
Вот и все. Простая мощная концепция. Конечно, добавление индекса к свойству пути значительно ускорит запрос. Есть много улучшений и оптимизаций, но основная идея должна быть ясна сейчас.
Справка: Хранение иерархических данных в MongoDB от нашего партнера по JCG Андрея Редько в блоге Андрея Редько .