Статьи

MongoDB с проектом Spring Data

Все мы наблюдаем взрыв решений NoSql в эти дни. Я привыкаю к ​​RDBMS, но это не решение всех проблем, с которыми вы можете столкнуться. По моему недавнему опыту я получил возможность работать с MongoDB — база данных документов. В этом посте я собираюсь охватить некоторые основы (и некоторые дополнительные возможности в следующем посте) использования MongoDB вместе с проектом Spring Data . Прежде чем мы начнем, небольшой отказ от ответственности: в данный момент 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
4.0.0
 
mongodb
com.example.spring
0.0.1-SNAPSHOT
jar
 
 
    UTF-8
    3.0.5.RELEASE
 
 
 
     
        org.springframework.data
        spring-data-mongodb
        1.0.0.M3
     
 
     
        log4j
        log4j
        1.2.16
     
 
     
        org.mongodb
        mongo-java-driver
        2.5.3
     
 
     
        org.springframework
        spring-core
        ${spring.version}
     
 
     
        org.springframework
        spring-context
        ${spring.version}
     
 
 
 
     
        springsource-milestone
        Spring Framework Milestone Repository
    

Здесь есть две ключевые зависимости:

MongoDB Java-драйвер
Spring Data для MongoDB

Есть несколько способов определить MongoDB в контексте приложения Spring . Позвольте мне показать немного многословно, но более гибко:

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
 
     
     
   
     
  
     
        <constructor-arg index="0" ref="mongo" />
        <constructor-arg index="1" value="elements-db"/>
     
 
         
       
 
     
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
        <constructor-arg name="mappingContext" ref="mappingContext" />        
      
 
     
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
        <constructor-arg name="mongoConverter" ref="converter" />       
        <property name="writeResultChecking" value="EXCEPTION" />
        <property name="writeConcern" value="NORMAL"/>
     
  
     
         
    

Роль каждого боба здесь:

  • Монго определяет соединение с базой данных MongoDB (мы используем настройки по умолчанию, порт 27027)
  • конвертер используется для преобразования классов Java в / из DBObject MongoDB (== JSON)
  • mongoTemplate предоставляет операции, которые мы можем выполнять над 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.example.mongodb;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.CollectionCallback;
import org.springframework.data.document.mongodb.MongoOperations;
import org.springframework.data.document.mongodb.query.Index;
import org.springframework.data.document.mongodb.query.Index.Duplicates;
import org.springframework.data.document.mongodb.query.Order;
import org.springframework.stereotype.Service;
 
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.MongoException;
 
@Service
public class MongoService  {
    @Autowired private MongoOperations template;
 
    public void createCollection( final String name ) {
        template.createCollection( name  );
    }
 
    public void dropCollection( final String name ) {
        template.dropCollection( name  );
    }
 
    public void insert( final Object object, final String collection ) {
        template.insert( object, collection );
    }
    
    public void createIndex( final String name, final String collection ) {
        template.ensureIndex(
            new Index()
                .on( name, Order.DESCENDING )
                .unique( Duplicates.DROP ),
            collection 
        );
    }
  
    // Remove / save / ... operations here
}

Вот и все с основами. В следующем посте будут рассмотрены расширенные функции: использование массовых вставок, операции обновления или вставки и выполнение команд MongoDB . 🙂

После обсуждения проектов MongoDB и Spring Data я хотел бы показать некоторые расширенные функции (которые могут быть доступны в следующем выпуске Spring Data или выпуске как часть основных функций).

Прежде всего, давайте расширим наш MongoService с помощью метода, который подсчитывает документы в коллекции, которые соответствуют конкретному запросу.

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;
import java.util.Arrays;
import java.util.Collection;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.CollectionCallback;
import org.springframework.data.document.mongodb.MongoOperations;
import org.springframework.data.document.mongodb.convert.MongoConverter;
import org.springframework.data.document.mongodb.query.Criteria;
import org.springframework.data.document.mongodb.query.Index;
import org.springframework.data.document.mongodb.query.Index.Duplicates;
import org.springframework.data.document.mongodb.query.Order;
import org.springframework.data.document.mongodb.query.Query;
 
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
 
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.MongoException;
 
@Service
public class MongoService {
    public long countDocuments( final String collection, final Query query ) { 
        return template.executeCommand(
            "{ " +
                "\"count\" : \"" + collection + "\"," +
                "\"query\" : " + query.getQueryObject().toString() +
            " }"  ).getLong( "n" );
    }
}

Подход для этой конкретной функции состоит в том, чтобы вызвать собственный счетчик команд MongoDB, передавая запрос в качестве параметра. Возвращающая структура содержит количество документов в n свойстве.

Или, более дружественным к коду способом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.CollectionCallback;
 
import com.mongodb.DBCollection;
import com.mongodb.MongoException;
 
public long countDocuments( final String collection, final Query query ) { 
    return template.execute( collection,
        new CollectionCallback< Long >() {
            @Override
            public Long doInCollection( DBCollection collection )
                    throws MongoException, DataAccessException {
                return collection.count( q.getQueryObject() ) );
            }
        }
    );
}

Следующая полезная функция — массовые вставки. Обратите внимание, что в текущей версии MongoDB 1.8.1, когда в коллекции вставок документов есть дубликат, массовая вставка останавливается на первом дубликате и возвращается, поэтому все остальные документы не будут вставлены . Будьте в курсе такого поведения. Прежде чем перейти к фрагменту кода, позвольте мне представить простой класс SimpleDocument, который мы будем сохранять в 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.example.mongodb;
 
import org.springframework.data.document.mongodb.mapping.Document;
 
@Document( collection = "documents" )
public class SimpleDocument {
    private String id;
    private String name;
    private String content;
 
    public SimpleDocument() {
    }
 
    public SimpleDocument( final String id, final String name ) {
        this.id = id;
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
    public String getContent() {
        return content;
    }
 
    public void setContent(String content) {
        this.content = content;
    }
}

Следующий метод вставляет все документы как единое массовое обновление:

1
2
3
public void insert( final Collection< SimpleDocument > documents ) { 
    template.insert( documents, SimpleDocument.class );    
}

Еще одна очень полезная и полезная функция для изучения — upserts MongoDB (подробнее об этом здесь http://www.mongodb.org/display/DOCS/Updating): если документ, соответствующий определенным критериям, существует, он будет обновлен, в противном случае — новый документ будет быть вставленным в коллекцию. Ниже приведен код, демонстрируемый с помощью следующего варианта использования: если SimpleDocument с таким именем существует, он будет обновлен, в противном случае новый документ будет добавлен в коллекцию:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Autowired private MongoConverter converter;
 
public void insertOrUpdate( final SimpleDocument document ) {
    final BasicDBObject dbDoc = new BasicDBObject();
    converter.write( document, dbDoc );
 
    template.execute( SimpleDocument.class,
        new CollectionCallback< Object >() {
            public Object doInCollection( DBCollection collection )
                    throws MongoException, DataAccessException {
                collection.update(
                    new Query()
                        .addCriteria( new Criteria( "name" ).is( document.getName() ) )
                        .getQueryObject(),
                    dbDoc, 
                    true,
                    false
                );
 
                return null;
            }
        }
    );
}

Обратите внимание на использование bean-компонента converter, который помогает конвертировать Java-класс в DBObject MongoDB .

Последнее, что я хотел бы показать, это операция findAndModify, которая делает несколько вещей в виде одной атомарной последовательности:

— найти критерии соответствия документа
— выполнить обновление
— вернуть обновленный документ (или старый, в зависимости от ваших потребностей)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public void findAndModify( final Query query, final Update update ) {
    return template.execute( SimpleDocument.class,
        new CollectionCallback< SimpleDocument >() {
            @Override
            public SimpleDocument doInCollection( DBCollection collection )
                    throws MongoException, DataAccessException {
                return converter.read( SimpleDocument.class,      
                    collection.findAndModify(
                        query.getQueryObject(),
                        null,
                        null,
                        false,
                        update.getUpdateObject(),
                        true,
                        false
                    )
                );
            }
        }  
    );
}

Пока это все интересные случаи использования, с которыми я столкнулся. Честно говоря, я очень рад MongoDB и настоятельно рекомендую его, если он подходит для вашего приложения.

Справка: Использование MongoDB вместе с проектом Spring Data: основные понятия и   Использование MongoDB вместе с проектом Spring Data: передовые концепции от нашего партнера по JCG Андрея Редько в блоге Андрея Редько .