Spring Data Document で MongoDB を使う

Spring Data - Document はドキュメントDBを扱うためのフレームワークで、MongoDB や CouchDB をサポートしています。

というわけで、Spring Data - Document で MongoDB を使うサンプルを 2種類作ってみました。(基本的な構成は前回 id:fits:20110205 のサンプルと同じです)

  • MongoTemplate を使ったサンプル(低レベルAPI
  • MongoRepository を使ったサンプル(高レベルAPI

使用した環境は以下の通りです。

  • Maven 3.0.2
  • Spring 3.0.5
  • Spring Data MongoDB 1.0.0 Snapshot
  • MongoDB 1.7.5

サンプルのソースは http://github.com/fits/try_samples/tree/master/blog/20110206/

MongoTemplate を使ったサンプル

低レベルAPIの MongoTemplate を直接使ったサンプルです。
内容的には、前回 id:fits:20110205 サンプルの com.mongodb.DB を使用したものと大差ありません。

まず、デフォルトコレクションをプロパティファイルに追加しておきます。

src/main/resources/fits/sample/mongodb.properties
uri=mongodb://localhost/
db=sample1
collection=data

次に、設定クラスで MongoTemplate を返す Bean 定義を実装します。(前回サンプルの DB を返す代わりになる)

src/main/java/fits/sample/AppConfig.java
package fits.sample;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.document.mongodb.MongoTemplate;
import com.mongodb.Mongo;
import com.mongodb.MongoURI;

//設定クラス
@Configuration
public class AppConfig {

    private @Value("#{mongodbProperties.uri}") String dbUri;
    private @Value("#{mongodbProperties.db}") String dbName;
    //デフォルトコレクション名
    private @Value("#{mongodbProperties.collection}") String colName;

    @Bean
    public Mongo mongo() throws Exception {
        return new Mongo(new MongoURI(dbUri));
    }

    @Bean
    public MongoTemplate mongoTemplate() throws Exception {
        return new MongoTemplate(mongo(), dbName, colName);
    }
}

@Autowired で自動的にインジェクションされる MongoTemplate オブジェクトを使って、MongoDB にデータを保存したり検索したりするサービスクラスを実装します。

なお、今回は検索条件に MongoDB Java ドライバーの API を使っており、findData では QueryBuilder を使って以下の検索条件を組み立てています。

  • name プロパティに指定した名前を含み、point プロパティの値が指定値より大きい

また、コレクションは MongoTemplate のコンストラクタで設定したデフォルトコレクションが使用されます。

src/main/java/fits/sample/MongoTemplateSampleService.java
package fits.sample;

import java.util.List;
import java.util.regex.Pattern;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.document.mongodb.MongoTemplate;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;

@Service
public class MongoTemplateSampleService implements SampleService {

    @Autowired
    private MongoTemplate temp;

    //Data を追加する
    public void addData(List<Data> list) {
        temp.insertList(list);
    }

    //指定名の Data を取得する
    public List<Data> getData(String name) {
        BasicDBObject query = new BasicDBObject("name", name);
        return temp.query(query, Data.class);
    }

    //指定名を含み、指定ポイントより大きい Data を取得する
    public List<Data> findData(String name, int point) {
        //クエリの作成
        DBObject query = QueryBuilder.start("name").regex(Pattern.compile(".*" + name + ".*")).and("point").greaterThan(point).get();
        return temp.query(query, Data.class);
    }
}

最後に実行クラスです。
applicationContext.xml ファイル等は前回 id:fits:20110205 のサンプルのものと同じものを使います。(Data クラスには id プロパティを追加しています)

src/main/java/fits/sample/App.java
package fits.sample;

import java.util.Arrays;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

//アプリケーションクラス
public class App {

    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        SampleService ss = ctx.getBean(SampleService.class);

        //Data の保存
        ss.addData(Arrays.asList(
            new Data("test1", 10),
            new Data("sample", 100),
            new Data("spring-test-10", 50),
            new Data("test-2", 20)
        ));

        List<Data> list = ss.getData("sample");
        assert list.get(0).getPoint() == 100;

        //Data の検索
        for (Data d : ss.findData("test", 10)) {
            System.out.printf("id: %d, name: %s, point: %d\n", d.getId(), d.getName(), d.getPoint());
        }
    }
}
ビルドと実行結果
> mvn compile
> mvn exec:java
・・・
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).
log4j:WARN Please initialize the log4j system properly.
id: 23925136477505512611248829721, name: spring-test-10, point: 50
id: 23925136477505512611265606937, name: test-2, point: 20
・・・

test を含み、point が 10 より大きい Data がヒットしている事を確認できます。

ちなみに、pom.xml ファイルは以下のようにしました。
javax.mail 等は今回は使わないし、設定が面倒そうだったので除外するようにしています。(リモートリポジトリから取得できず、手動でローカルリポジトリにデプロイする必要があるみたいなので)

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>fits.sample</groupId>
  <artifactId>spring_data_mongo</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>Spring Data mongodb sample</name>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <configuration>
          <executable>java</executable>
          <mainClass>fits.sample.App</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <repositories>
     <!-- Spring 用のリポジトリ設定 -->
     <repository>
        <id>com.springsource.repository.maven.release</id>
        <url>http://maven.springframework.org/release/</url>
     </repository>
     <!-- Spring Data 用のリポジトリ設定 -->
     <repository>
        <id>com.springsource.repository.maven.snapshot</id>
        <url>http://maven.springframework.org/snapshot/</url>
     </repository>
  </repositories>
  <dependencies>
    <!-- Spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.0.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2</version>
    </dependency>
    <!-- Spring Data MongoDB -->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-mongodb</artifactId>
      <version>1.0.0.BUILD-SNAPSHOT</version>
      <exclusions>
        <exclusion>
          <groupId>javax.mail</groupId>
          <artifactId>mail</artifactId>
        </exclusion>
        <exclusion>
          <groupId>javax.jms</groupId>
          <artifactId>jms</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.sun.jdmk</groupId>
          <artifactId>jmxtools</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.sun.jmx</groupId>
          <artifactId>jmxri</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <!-- MongoDB -->
    <dependency>
      <groupId>org.mongodb</groupId>
      <artifactId>mongo-java-driver</artifactId>
      <version>2.4</version>
    </dependency>
  </dependencies>
</project>

MongoRepository を使ったサンプル

高レベルAPIとでも言える MongoRepository を使ったサンプルです。
多分こっちがメインの使い方だと思います。

まず、アプリケーション構成ファイル applicationContext.xml に MongoRepository のサブインターフェースをスキャンする設定を追加します。

  • mongo:repositories 要素で fits.sample パッケージ内の MongoRepository のサブインターフェースをスキャンする設定

この設定により、MongoRepository のサブインターフェース(後述の DataRepository)を実装し、mongoTemplate(Bean名)を自動的に設定したオブジェクトが自動的にインジェクションされるようになります。

src/main/resources/applicationContext.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:util="http://www.springframework.org/schema/util"
    xmlns:mongo="http://www.springframework.org/schema/data/mongo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd
      http://www.springframework.org/schema/util
      http://www.springframework.org/schema/util/spring-util-3.0.xsd
      http://www.springframework.org/schema/data/mongo
      http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">

    <context:component-scan base-package="fits.sample" />

    <util:properties id="mongodbProperties" location="classpath:fits/sample/mongodb.properties" />

    <!-- MongoRepository のサブインターフェースをスキャンする設定 -->
    <mongo:repositories base-package="fits.sample" />
</beans>

次に MongoRepository のサブインターフェースを作成します。

  • MongoRepository のジェネリックタイプに検索対象のタイプと id のタイプを指定

また、以下のような命名規則に則った find メソッドを用意すれば、自動的に検索メソッドの実装を用意してくれます。(ActiveRecord の動的ファインダーみたいなもの)

findBy[プロパティ名][比較オペレータ名]・・・
src/main/java/fits/sample/DataRepository.java
package fits.sample;

import java.util.List;
import org.springframework.data.document.mongodb.repository.MongoRepository;

public interface DataRepository extends MongoRepository<Data, java.math.BigInteger> {
    //name プロパティが指定名の Data を検索する
    List<Data> findByName(String name);

    //name プロパティに指定の名前を含み、
    //point プロパティが指定値より大きい Data を検索する
    List<Data> findByNameLikeAndPointGreaterThan(String name, int point);
}

最後にサービスクラスを実装します。
@Autowired で DataRepository の変数を定義すれば、mongoTemplate が設定された DataRepository の実装オブジェクトが自動的にインジェクションされます。(アプリケーション構成ファイルの mongo:repositories 要素のおかげ)

src/main/java/fits/sample/MongoRepositorySampleService.java
package fits.sample;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;

@Service
public class MongoRepositorySampleService implements SampleService {

    @Autowired
    private DataRepository rep;

    //Data を追加する
    public void addData(List<Data> list) {
        rep.save(list);
    }

    //指定名の Data を取得する
    public List<Data> getData(String name) {
        return rep.findByName(name);
    }

    //指定名を含み、指定ポイントより大きい Data を取得する
    public List<Data> findData(String name, int point) {
        return rep.findByNameLikeAndPointGreaterThan(name, point);
    }
}

App.java, AppConfig.java ファイル等は MongoTemplate 版のサンプルと同じものをそのまま使用します。

ビルドと実行結果
> mvn compile
> mvn exec:java
・・・
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).
log4j:WARN Please initialize the log4j system properly.
id: 23925185718526550666808467778, name: spring-test-10, point: 50
id: 23925185718526550666825244994, name: test-2, point: 20
・・・

MongoTemplate 版と同様の結果となります。