Spring Data – Teil 3: MongoDB

Keine Kommentare

In diesem Teil meiner Blogserie werden wir sehen, wie einfach sich mit Spring Data MongoDB der Zugriff auf einen MongoDB Datastore gestaltet.

MongoDB

Bei MongoDB handelt es sich um einen sogenannten NoSQL-Datastore, der auf die Persistierung von Dokumenten-orientierten Objekten spezialisiert ist. Für den Einstieg in die Entwicklung empfehle einen Blick in die Developer Zone auf der MongoDB Homepage. Nach dem Download der MongoDB legen wir ein Verzeichnis an und starten die Datenbank mit

${MONGO_HOME}/bin/mkdir ../data
${MONGO_HOME}/bin/mongod --dbpath ../data --rest

und werden unter http://localhost:28017/ mit einem Admin-Interface belohnt. Für die erste Schritte bietet sich die interaktvie Mongo Shell an, die man wie folgt startet:

C:\dev\bin\mongo\bin>mongo
MongoDB shell version: 2.0.2
connecting to: test
> show dbs
admin   (empty)
local   (empty)
test    0.078125GB
> show collections
foo
system.indexes
> db.foo.save({a:1, b:"bar"})
> db.foo.save({a:1, b:"bar"})
> db.foo.save({c:2, d:"doo"})
> db.foo.find()
{ "_id" : ObjectId("4f1e575efc25822cd8ff8cf2"), "a" : 1, "b" : "bar" }
{ "_id" : ObjectId("4f1e5766fc25822cd8ff8cf3"), "a" : 1, "b" : "bar" }
{ "_id" : ObjectId("4f1e5771fc25822cd8ff8cf4"), "c" : 2, "d" : "doo" }

Wir lassen uns die Namen der Datenbanken ausgeben, dann die Collections (eine Collection ist ein logischer Namensraum) innerhalb der Default-Datenbank test. Danach speichern wir in der Collection foo drei Dokumente, die wir in JSON-Notation angeben. Dabei fällt auf, dass

  1. jedes Dokument automatisch eine eindeutige Id erhält
  2. Dokumente mit gleichen Attributlisten existieren können
  3. Dokumente verschiedener Struktur innerhalb der gleichen Collections abgelegt werden können

Daher kann man eine Collection nur bedingt mit einer Tabelle einer relationalen Datenbank vergleichen. Ebenfalls müssen wir uns von ACID-Transaktionen verabschieden. Warum das so ist, kann man u.a. in diesem Artikel nachlesen: Grundlagen Cloud Computing: CAP-Theorem.

Spring Data MongoDB

Spring Data MongoDB verfolgt prinzipiell den gleichen Ansatz wie Spring Data JPA, nämlich die Definition von Repository-Findern allein als Interface-Methoden, deren Implementierung zur Laufzeit von Spring bereitgestellt wird. Ebenso werden Methoden für CRUD-Operation angeboten.

Konfiguration

Zunächst lassen wir Maven die aktuelle Release-Version von Spring Data MongoDB herunterladen:

<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-mongodb</artifactId>
	<version>1.0.0.RELEASE</version>
</dependency>

Im Spring Application Context gibt es einen eigenen XML Namespace mongo, der die Konfiguration sehr einfach macht:

<!-- Connection to MongoDB server -->
<mongo:db-factory host="localhost" port="27017" dbname="test" />
<!-- MongoDB Template -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
  <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
</bean>
 
<!-- Package w/ automagic repositories -->
<mongo:repositories base-package="mongodb"/>

Mit <mongo:db-factory .../> geben wird die Verbindungsinformationen zum MongoDB-Server und den Namen der Datenbank an. Zum Finetuning der Verbindungsinformationen (Connection Pooling, Clustering etc.) kann man alternativ die Elemente <mongo:mongo> und <mongo:options/> verwenden. Anschließend definieren wir ein Template, das die DB-Factory übergeben bekommt. Zum Schluss müssen wir (wie auch bei Spring Data JPA) angeben, in welchem Paket unsere Repositories liegen. Dabei wird automatisch das einzige MongoDB-Template im Context verwendet. Hat man mehr als ein solches Template, kann man mit <mongo:repositories mongo-template-ref="..."> ein bestimmtes auswählen.

Beispiel

Genau wie im Beitrag über Spring Data JPA wollen wir Objekte der Klasse User persistieren:

@Document
public class User {
 
	@Id 
	private String id;
 
	@Indexed
	private String fullName;
 
	private Date lastLogin;
	...

Zur reinen Persistierung sind die Annotation nicht zwingend notwendig. Zum Definieren eines Index mittels @Indexed muss man aber dann schon auf Annotationen zurückgreifen. Nun schnell noch ein Repository definieren …

public interface UserRepository extends MongoRepository<User, String> {}

… und wir können die ersten Dokumente in unserer MongoDB speichern:

public class MongoDBRepoTest {
 
  @Autowired UserRepository repo;
 
  @Before public void setUp() {
    repo.save(new User("root", "Superuser"));
    for ( int i = 0; i < 6; i++ ) {
      repo.save( new User( String.format("user%02d", i), "User " + i ) );
    }
  }

Ein Blick in die Mongo-Konsole zeigt, dass unsere Dokumente dort angekommen sind:

MongoDB shell version: 1.8.3
connecting to: test
> db.user.find()
{ "_id" : "user00", "_class" : "mongodb.User", "fullName" : "User 0", "lastLogin" : ISODate("2012-01-27T08:16:37.589Z") }
{ "_id" : "user01", "_class" : "mongodb.User", "fullName" : "User 1", "lastLogin" : ISODate("2012-01-27T08:16:37.589Z") }
{ "_id" : "user02", "_class" : "mongodb.User", "fullName" : "User 2", "lastLogin" : ISODate("2012-01-27T08:16:37.590Z") }
{ "_id" : "user03", "_class" : "mongodb.User", "fullName" : "User 3", "lastLogin" : ISODate("2012-01-27T08:16:37.590Z") }
{ "_id" : "user04", "_class" : "mongodb.User", "fullName" : "User 4", "lastLogin" : ISODate("2012-01-27T08:16:37.591Z") }
{ "_id" : "user05", "_class" : "mongodb.User", "fullName" : "User 5", "lastLogin" : ISODate("2012-01-27T08:16:37.591Z") }
{ "_id" : "root", "_class" : "mongodb.User", "fullName" : "Superuser", "lastLogin" : ISODate("2012-01-27T08:16:37.576Z") }
> db.user.count()
7
> db.user.getIndexes()
[
        {
                "name" : "_id_",
                "ns" : "test.user",
                "key" : {
                        "_id" : 1
                },
                "v" : 0
        },
        {
                "name" : "fullName",
                "ns" : "test.user",
                "dropDups" : false,
                "sparse" : false,
                "unique" : false,
                "key" : {
                        "fullName" : 1
                },
                "v" : 0
        }
]

Zu beachten ist, dass automatisch beim ersten Speichern eine Collection namens user angelegt wurde. Soll die Collection abweichend vom Klassenname angelegt werden, kann dies über die Annotation @Document(collection="...") festgelegt werden. Der Java-Klassenname wird in einem Attribut _class abgespeichert. Die Ausgabe der Indexe zeigt, dass neben dem Index für die Id auch unser mit @Indexed annotiertes Attribut fullName zur Anlage eines Index geführt hat.

Nun erweitern wird unser Repository um einige eigene Finder-Methoden:

public interface UserRepository extends MongoRepository<User, String> {
   @Query("{ fullName: ?0 }")
   List<User> findByTheUsersFullName(String fullName);
 
   List<User> findByFullNameLike(String fullName, Sort sort);
}

Mit der @Query Annotation können frei definierbare Queries in MongoDB-Syntax definiert werden. Die zweite Query demonstriert eine Suche mit regulären Ausdrücken. Beim Schreiben der ersten Queries ist der Vergleich zwischen MongoDB und SQL sehr hilfreich.

Der komplette Quellecode kann auf Github heruntergeladen werden.

MongoDBTemplate

Für weitergehende Zugriffe reicht ein Interface-basiertes Repository allerdings nicht aus. Für die Verwaltung von Collections oder den Einsatz von Map/Reduce-Funktionen muss man auf das MongoDBTemplate zurückgreifen.

Zusammenfassung

Nach einer kurzen Einführung zur MongoDB konnten wir mittels Spring Data MongoDB sehr einfach die ersten Objekte abspeichern. Danach haben wir gezeigt, wie man eigene Finder-Methoden durch das Schreiben von Interface-Methoden implementiert.

Prinzipiell kann eine Spring Anwendung, die ihre Persistenz mit Spring Data MongoDB realisiert, nun etwa auf CloudFoundry deployt werden, was in diesem Blog ausführlich erklärt wird.

Was bisher geschah

Teil 1: Spring Data Commons
Teil 2: Spring Data JPA

Ausblick

Die Projekte Spring Data Neo4j und Spring GemFire sind gute Kandidaten für weitere Beiträge in dieser Serie.

Tobias Trelle

Diplom-Mathematiker Tobias Trelle ist Senior IT Consultant bei der codecentric AG, Solingen. Er ist seit knapp 20 Jahren im IT-Business unterwegs und interessiert sich für Software-Architekturen und skalierbare Lösungen. Tobias hält Vorträge auf Konferenzen und Usergruppen und ist Autor des Buchs „MongoDB: Ein praktischer Einstieg“.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.