by Marc-David Militz
Forum: MongoDB Theorie
git clone https://github.com/mongodb-developer/java-quick-start
Wenn Sie dieses Repository bereits geklont haben, stellen Sie sicher, dass Sie die aktuellste Version verwenden:
git pull
Im vorherigen Blogbeitrag haben wir die Klassen "HelloMongoDB" und "Connection" erstellt. Dieses Mal arbeiten wir an der Klasse "Create".
Wenn Sie noch keinen kostenlosen Cluster in MongoDB Atlas eingerichtet haben, ist jetzt der richtige Zeitpunkt dafür. Sie bekommen alle Informationen dazu in diesem Blog-Beitrag.
https://www.mongodb.com/blog/post/quick-start-getting-your-free-mongodb-atlas-cluster
MongoDB Enterprise Cluster0-shard-0:PRIMARY> db.grades.findOne({student_id: 0, class_id: 339})
{
"_id" : ObjectId("56d5f7eb604eb380b0d8d8ce"),
"student_id" : 0,
"scores" : [
{
"type" : "exam",
"score" : 78.40446309504266
},
{
"type" : "quiz",
"score" : 73.36224783231339
},
{
"type" : "homework",
"score" : 46.980982486720535
},
{
"type" : "homework",
"score" : 76.67556138656222
}
],
"class_id" : 339
}
Und hier ist die erweiterte JSON-Darstellung desselben Schülers. Sie können es in MongoDB Compass abrufen, wenn Sie möchten.
Extended JSON ist die lesbare Version eines BSON-Dokuments ohne Verlust von Typinformationen. Weitere Informationen zum Java-Treiber und zu BSON finden Sie hier.
https://mongodb.github.io/mongo-java-driver/3.11/bson/extended-json/
{
"_id": {
"$oid": "56d5f7eb604eb380b0d8d8ce"
},
"student_id": {
"$numberDouble": "0"
},
"scores": [{
"type": "exam",
"score": {
"$numberDouble": "78.40446309504266"
}
}, {
"type": "quiz",
"score": {
"$numberDouble": "73.36224783231339"
}
}, {
"type": "homework",
"score": {
"$numberDouble": "46.980982486720535"
}
}, {
"type": "homework",
"score": {
"$numberDouble": "76.67556138656222"
}
}],
"class_id": {
"$numberDouble": "339"
}
}
Wie Sie sehen, speichert MongoDB BSON-Dokumente und für jedes Schlüssel-Wert-Paar enthält das BSON den Schlüssel und den Wert zusammen mit seinem Typ.
Auf diese Weise weiß MongoDB, dass "class_id" tatsächlich ein Double und keine Ganzzahl ist, was in der Mongo Shell-Darstellung dieses Dokuments nicht explizit angegeben ist.
Wir haben bereits 10.000 Schüler ("student_id" von 0 bis 9999) in dieser Collection und jeder von ihnen hat 10 verschiedene Klassen belegt, was zu 100.000 Dokumenten in dieser Collection führt.
Angenommen, ein neuer Student ("student_id" 10.000) ist gerade an dieser Universität eingetroffen und hat in seiner ersten Klasse eine Reihe von (zufälligen) Noten erhalten. Fügen wir diesen neuen Schüler nun mit Java ein.
An dieser Universität variiert die "class_id" von 0 bis 500, sodass wir einen beliebigen Wert zwischen 0 und 500 verwenden können.
package com.mongodb.quickstart;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Create {
public static void main(String[] args) {
Logger.getLogger("org.mongodb.driver").setLevel(Level.WARNING);
try (MongoClient mongoClient = MongoClients.create(System.getProperty("mongodb.uri"))) {
MongoDatabase sampleTrainingDB = mongoClient.getDatabase("sample_training");
MongoCollection<Document> gradesCollection = sampleTrainingDB.getCollection("grades");
}
}
}
Random rand = new Random();
Document student = new Document("_id", new ObjectId());
student.append("student_id", 10000d)
.append("class_id", 1d)
.append("scores", asList(new Document("type", "exam").append("score", rand.nextDouble() * 100),
new Document("type", "quiz").append("score", rand.nextDouble() * 100),
new Document("type", "homework").append("score", rand.nextDouble() * 100),
new Document("type", "homework").append("score", rand.nextDouble() * 100)));
Wie Sie sehen, haben wir aus den vorhandenen Dokumenten in dieser Collection dasselbe Datenmodell reproduziert, da sichergestellt wurde, dass "student_id", "class_id" und "score" alle doppelt vorhanden sind.
Außerdem hätte der Java-Treiber das Feld _id mit einer ObjectId für uns generiert, wenn wir hier keine explizite ID erstellt hätten, aber es hat sich bewährt, die _id selbst festzulegen.
Dies wird unser Leben im Moment nicht verändern, aber es ist sinnvoller, wenn wir POJOs direkt manipulieren und eine saubere REST-API erstellen möchten.
Wie das geht, zeige wir Ihnen in einem künftigen Blogbeitrag.
gradesCollection.insertOne(student);
package com.mongodb.quickstart;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import org.bson.types.ObjectId;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.util.Arrays.asList;
public class Create {
public static void main(String[] args) {
Logger.getLogger("org.mongodb.driver").setLevel(Level.WARNING);
try (MongoClient mongoClient = MongoClients.create(System.getProperty("mongodb.uri"))) {
MongoDatabase sampleTrainingDB = mongoClient.getDatabase("sample_training");
MongoCollection<Document> gradesCollection = sampleTrainingDB.getCollection("grades");
Random rand = new Random();
Document student = new Document("_id", new ObjectId());
student.append("student_id", 10000d)
.append("class_id", 1d)
.append("scores", asList(new Document("type", "exam").append("score", rand.nextDouble() * 100),
new Document("type", "quiz").append("score", rand.nextDouble() * 100),
new Document("type", "homework").append("score", rand.nextDouble() * 100),
new Document("type", "homework").append("score", rand.nextDouble() * 100)));
gradesCollection.insertOne(student);
}
}
}
Sie können diese Klasse mit der folgenden maven-Befehlszeile im Stammverzeichnis oder mit Ihrer IDE ausführen (Einzelheiten finden Sie im vorherigen Beitrag). Vergessen Sie nicht die doppelten Anführungszeichen um die MongoDB-URI, um Überraschungen zu vermeiden.
mvn compile exec:java -Dexec.mainClass="com.mongodb.quickstart.Create" -Dmongodb.uri="mongodb+srv://USERNAME:PASSWORD@cluster0-abcde.mongodb.net/test?w=majority"
Und hier ist das Dokument, wie es in MongoDB Compass angezeigt wird.
{
"_id": {
"$oid": "5d97c375ded5651ea3462d0f"
},
"student_id": {
"$numberDouble": "10000"
},
"class_id": {
"$numberDouble": "1"
},
"scores": [{
"type": "exam",
"score": {
"$numberDouble": "4.615256396625178"
}
}, {
"type": "quiz",
"score": {
"$numberDouble": "73.06173415145801"
}
}, {
"type": "homework",
"score": {
"$numberDouble": "19.378205578990727"
}
}, {
"type": "homework",
"score": {
"$numberDouble": "82.3089189278531"
}
}]
}
Beachten Sie, dass sich die Reihenfolge der Felder vom ursprünglichen Dokument mit "student_id" unterscheidet: 0.
Wir könnten genau die gleiche Reihenfolge erhalten, wenn wir das Dokument wie folgt erstellen möchten.
Random rand = new Random();
Document student = new Document("_id", new ObjectId());
student.append("student_id", 10000d)
.append("scores", asList(new Document("type", "exam").append("score", rand.nextDouble() * 100),
new Document("type", "quiz").append("score", rand.nextDouble() * 100),
new Document("type", "homework").append("score", rand.nextDouble() * 100),
new Document("type", "homework").append("score", rand.nextDouble() * 100)))
.append("class_id", 1d);
Wenn Sie jedoch die Dinge richtig machen, sollte dies keinen Einfluss auf Ihren Code und Ihre Logik haben, da Felder in JSON-Dokumenten nicht sortiert sind.
Zitat dazu von json.org.
An object is an unordered set of name/value pairs.
private static final Random rand = new Random();
Erstellen wir eine Factory-Methode für die Noten.
private static Document generateNewGrade(double studentId, double classId) {
List<Document> scores = asList(new Document("type", "exam").append("score", rand.nextDouble() * 100),
new Document("type", "quiz").append("score", rand.nextDouble() * 100),
new Document("type", "homework").append("score", rand.nextDouble() * 100),
new Document("type", "homework").append("score", rand.nextDouble() * 100));
return new Document("_id", new ObjectId()).append("student_id", studentId)
.append("class_id", classId)
.append("scores", scores);
}
Und jetzt können wir damit 10 Dokumente auf einmal einfügen.
List<Document> grades = new ArrayList<>();
for (int classId = 1; classId <= 10; classId++) {
grades.add(generateNewGrade(10001d, classId));
}
gradesCollection.insertMany(grades, new InsertManyOptions().ordered(false));
Wie Sie sehen, verpacken wir jetzt unsere Notendokumente in eine Liste und senden diese Liste in einem einzigen Aufruf mit der "insertMany"-Methode.
Standardmäßig fügt die "insertMany"-Methode die Dokumente der Reihe nach ein und stoppt, wenn während des Vorgangs ein Fehler auftritt.
Wenn Sie beispielsweise versuchen, ein neues Dokument mit derselben "_id" wie ein vorhandenes Dokument einzufügen, erhalten Sie eine "DuplicateKeyException".
Daher würden bei einer geordneten "insertMany" die letzten Dokumente der Liste nicht eingefügt, und der Einfügeprozess würde angehalten und die entsprechende Ausnahme zurückgeben, sobald der Fehler auftritt.
Wie Sie hier sehen können, ist dies nicht das gewünschte Verhalten, da alle Noten völlig unabhängig voneinander sind.
Wenn eine von ihnen ausfällt, möchten wir alle Noten verarbeiten und am Ende Ausnahme Exceptions für die fehlgeschlagenen bekommen.
Aus diesem Grund wird der zweite Parameter "new InsertManyOptions().Ordered(false)" angezeigt, der standardmäßig "true" ist.
package com.mongodb.quickstart;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.InsertManyOptions;
import org.bson.Document;
import org.bson.types.ObjectId;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.util.Arrays.asList;
public class Create {
private static final Random rand = new Random();
public static void main(String[] args) {
Logger.getLogger("org.mongodb.driver").setLevel(Level.WARNING);
try (MongoClient mongoClient = MongoClients.create(System.getProperty("mongodb.uri"))) {
MongoDatabase sampleTrainingDB = mongoClient.getDatabase("sample_training");
MongoCollection<Document> gradesCollection = sampleTrainingDB.getCollection("grades");
insertOneDocument(gradesCollection);
insertManyDocuments(gradesCollection);
}
}
private static void insertOneDocument(MongoCollection<Document> gradesCollection) {
gradesCollection.insertOne(generateNewGrade(10000d, 1d));
System.out.println("One grade inserted for studentId 10000.");
}
private static void insertManyDocuments(MongoCollection<Document> gradesCollection) {
List<Document> grades = new ArrayList<>();
for (int classId = 1; classId <= 10; classId++) {
grades.add(generateNewGrade(10001d, classId));
}
gradesCollection.insertMany(grades, new InsertManyOptions().ordered(false));
System.out.println("Ten grades inserted for studentId 10001.");
}
private static Document generateNewGrade(double studentId, double classId) {
List<Document> scores = asList(new Document("type", "exam").append("score", rand.nextDouble() * 100),
new Document("type", "quiz").append("score", rand.nextDouble() * 100),
new Document("type", "homework").append("score", rand.nextDouble() * 100),
new Document("type", "homework").append("score", rand.nextDouble() * 100));
return new Document("_id", new ObjectId()).append("student_id", studentId)
.append("class_id", classId)
.append("scores", scores);
}
}
Zur Erinnerung, jeder Schreibvorgang (create, replace, update, delete), der an einem EINZELNEN Dokument ausgeführt wird, ist in MongoDB ACID.
Was bedeutet, dass insertMany standardmäßig nicht ACID ist, aber gute Nachrichten, seit MongoDB 4.0 können wir diesen Aufruf in eine ACID-Transaktion mit mehreren Dokumenten einbinden, um ihn vollständig ACID zu machen.