MongoDB Java CRUD Örneği: Kapsamlı Bir Eğitim
MongoDB, günümüzün en popüler NoSQL veritabanlarından biridir. Esnek yapısı, yüksek performansı ve ölçeklenebilirliği sayesinde modern uygulamaların veri depolama ihtiyaçlarını karşılamada önemli bir rol oynamaktadır. Java ise kurumsal uygulamalardan mobil geliştirmeye kadar geniş bir yelpazede kullanılan güçlü ve olgun bir programlama dilidir. Bu makalede, Java uygulamalarınızda MongoDB ile nasıl etkileşim kurabileceğinizi, temel CRUD (Create, Read, Update, Delete) operasyonlarını adım adım örneklerle ele alacağız. Amacımız, hem yeni başlayanlar hem de deneyimli geliştiriciler için kapsamlı ve pratik bir rehber sunmaktır.
MongoDB ve NoSQL Veritabanlarına Giriş
Geleneksel ilişkisel veritabanları (SQL), verileri tablolar halinde, önceden tanımlanmış şemalarla saklar. Bu yapı, belirli senaryolar için güçlü ve tutarlıdır ancak yüksek ölçeklenebilirlik, büyük veri hacimleri ve esnek şema gerektiren modern uygulamalarda bazı kısıtlamalar getirebilir. İşte bu noktada NoSQL (Not only SQL) veritabanları devreye girer.
MongoDB, NoSQL veritabanları ailesinin “belge tabanlı” (document-oriented) kategorisine girer. Verileri JSON benzeri BSON (Binary JSON) formatında depolar. Her bir belge (document), ilişkisel veritabanlarındaki bir satıra benzer ancak çok daha zengin ve iç içe geçmiş yapılar içerebilir. Bu esneklik, uygulama geliştiricilerine şema değişiklikleri konusunda büyük bir özgürlük sunar ve hızlı iterasyonlara olanak tanır.
MongoDB’nin başlıca avantajları şunlardır:
* Esnek Şema: Belgeler farklı alanlara sahip olabilir, bu da veri modelinizi uygulama gereksinimlerine göre kolayca değiştirebileceğiniz anlamına gelir.
* Yüksek Performans: Özellikle büyük veri kümeleri ve yüksek okuma/yazma yükleri için tasarlanmıştır.
* Ölçeklenebilirlik: Yatay ölçeklenebilirlik (sharding) özelliği sayesinde veri yükünü birden fazla sunucuya dağıtabilir.
* Zengin Sorgu Dili: İlişkisel veritabanlarındaki JOIN işlemlerine benzer yeteneklere sahip güçlü bir sorgu dili sunar.
* Geliştirici Dostu: JSON tabanlı yapısı sayesinde modern web uygulamalarıyla kolayca entegre edilebilir.
Geliştirme Ortamının Hazırlanması
Java ile MongoDB’yi kullanmaya başlamadan önce geliştirme ortamımızı hazırlamamız gerekiyor. Bu bölümde, MongoDB’nin kurulumundan Java projesi oluşturmaya kadar gerekli adımları ele alacağız.
MongoDB Kurulumu
Yerel makinenizde MongoDB’nin kurulu ve çalışır durumda olması gerekmektedir. Kurulum adımları işletim sisteminize göre değişiklik gösterir:
* Windows: MongoDB Community Server’ı indirip yükleyiciyi çalıştırın.
* macOS: Homebrew kullanarak brew tap mongodb/brew && brew install mongodb-community komutuyla kolayca kurabilirsiniz.
* Linux: Dağıtımınıza özel paket yöneticileri (apt, yum) aracılığıyla veya resmi MongoDB web sitesinden manuel kurulum yapabilirsiniz.
Kurulum tamamlandıktan sonra, MongoDB sunucusunu başlatmanız gerekecektir. Genellikle bu işlem için mongod komutunu kullanırsınız. Sunucunun varsayılan olarak 27017 portunda çalıştığını unutmayın.
Java Projesi Oluşturma
Bir Java projesi oluşturmak için Maven veya Gradle gibi bir bağımlılık yönetim aracı kullanmanızı şiddetle tavsiye ederiz. Bu, MongoDB Java Driver’ı projenize kolayca eklemenizi sağlayacaktır.
Maven ile Proje Oluşturma
Yeni bir Maven projesi oluşturabilir veya mevcut projenizin pom.xml dosyasına aşağıdaki bağımlılığı ekleyebilirsiniz:
org.mongodb
mongodb-driver-sync
4.11.1
Gradle ile Proje Oluşturma
Gradle kullanıyorsanız, build.gradle dosyanızın dependencies bloğuna aşağıdaki satırı ekleyin:
dependencies {
// MongoDB Java Driver
implementation 'org.mongodb:mongodb-driver-sync:4.11.1' // En son stabil sürümü kontrol edin
}
Not: Lütfen yukarıdaki sürüm numarasını (örneğin, 4.11.1) makaleyi okuduğunuz tarihteki en son stabil MongoDB Java Driver sürümüyle güncellemeyi unutmayın. Sürümleri Maven Central veya MongoDB resmi web sitesinden kontrol edebilirsiniz.
IDE Seçimi
IntelliJ IDEA, Eclipse veya VS Code gibi popüler bir Java IDE’si kullanarak projenizi geliştirebilirsiniz. Bu IDE’ler, kod tamamlama, hata ayıklama ve bağımlılık yönetimi gibi konularda size yardımcı olacaktır.
MongoDB Java Driver ile Bağlantı Kurma
MongoDB Java Driver, Java uygulamalarınızın MongoDB veritabanı ile iletişim kurmasını sağlayan ana kütüphanedir. Bağlantı kurmak için MongoClient sınıfını kullanırız.
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoCollection;
import org.bson.Document;
public class MongoDBConnection {
private static final String CONNECTION_STRING = "mongodb://localhost:27017";
private static final String DATABASE_NAME = "mydatabase";
private static final String COLLECTION_NAME = "mycollection";
public static void main(String[] args) {
try (MongoClient mongoClient = MongoClients.create(CONNECTION_STRING)) {
System.out.println("MongoDB'ye başarıyla bağlandı!");
MongoDatabase database = mongoClient.getDatabase(DATABASE_NAME);
System.out.println("Veritabanı '" + DATABASE_NAME + "' seçildi.");
MongoCollection collection = database.getCollection(COLLECTION_NAME);
System.out.println("Koleksiyon '" + COLLECTION_NAME + "' seçildi.");
// Bağlantıyı test etmek için basit bir işlem yapabiliriz
long docCount = collection.countDocuments();
System.out.println("Koleksiyonda " + docCount + " belge var.");
} catch (Exception e) {
System.err.println("MongoDB bağlantı hatası: " + e.getMessage());
e.printStackTrace();
}
}
}
Yukarıdaki kodda:
* CONNECTION_STRING: MongoDB sunucunuzun adresini belirtir. Varsayılan yerel bağlantı mongodb://localhost:27017 şeklindedir.
* MongoClients.create(): Belirtilen bağlantı dizesini kullanarak bir MongoClient örneği oluşturur. Bu nesne, veritabanına olan bağlantıyı yönetir.
* mongoClient.getDatabase(DATABASE_NAME): Belirtilen adla bir veritabanı nesnesi alır. Eğer veritabanı yoksa, ilk belge eklendiğinde otomatik olarak oluşturulur.
* database.getCollection(COLLECTION_NAME): Belirtilen adla bir koleksiyon nesnesi alır. Koleksiyon da veritabanı gibi, ilk belge eklendiğinde otomatik olarak oluşturulur.
* try-with-resources bloğu, MongoClient nesnesinin işi bittiğinde otomatik olarak kapatılmasını sağlar, bu da kaynak yönetimini kolaylaştırır.
Veri Modeli Tasarımı
MongoDB’de veriler belgeler (documents) halinde saklanır. Java’da bu belgeleri temsil etmek için genellikle org.bson.Document sınıfını veya özel POJO (Plain Old Java Object) sınıflarını kullanırız. İlk olarak Document sınıfı ile çalışmayı göreceğiz, daha sonra POJO mapping konusuna değineceğiz.
Bir Person (Kişi) nesnesini temsil eden bir Document örneği şöyle görünebilir:
{
"name": "Ali Can",
"age": 30,
"city": "İstanbul",
"email": "ali.can@example.com",
"hobbies": ["reading", "hiking"]
}
Java’da bu Document nesnesini manuel olarak oluşturabiliriz:
Document personDocument = new Document("name", "Ali Can")
.append("age", 30)
.append("city", "İstanbul")
.append("email", "ali.can@example.com")
.append("hobbies", Arrays.asList("reading", "hiking"));
Bu yaklaşım basit senaryolar için uygun olsa da, daha karmaşık uygulamalarda POJO sınıfları kullanmak kodun okunabilirliğini ve bakımını artırır.
CRUD Operasyonları
Şimdi, MongoDB Java Driver kullanarak temel CRUD operasyonlarını nasıl gerçekleştireceğimizi adım adım inceleyelim.
Create (Ekleme) Operasyonları
Veritabanına yeni belgeler eklemek için insertOne() ve insertMany() metotlarını kullanırız.
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.result.InsertOneResult;
import com.mongodb.client.result.InsertManyResult;
import org.bson.Document;
import org.bson.types.ObjectId;
import java.util.Arrays;
import java.util.List;
public class CreateOperations {
private static final String CONNECTION_STRING = "mongodb://localhost:27017";
private static final String DATABASE_NAME = "mydatabase";
private static final String COLLECTION_NAME = "people";
public static void main(String[] args) {
try (MongoClient mongoClient = MongoClients.create(CONNECTION_STRING)) {
MongoDatabase database = mongoClient.getDatabase(DATABASE_NAME);
MongoCollection collection = database.getCollection(COLLECTION_NAME);
// 1. Tek Bir Belge Ekleme (Insert One)
Document person1 = new Document("name", "Ayşe Yılmaz")
.append("age", 25)
.append("city", "Ankara")
.append("email", "ayse.yilmaz@example.com")
.append("hobbies", Arrays.asList("swimming", "photography"));
InsertOneResult insertOneResult = collection.insertOne(person1);
System.out.println("Tek belge eklendi. ID: " + insertOneResult.getInsertedId());
// 2. Birden Fazla Belge Ekleme (Insert Many)
Document person2 = new Document("name", "Mehmet Demir")
.append("age", 35)
.append("city", "İzmir")
.append("email", "mehmet.demir@example.com")
.append("hobbies", Arrays.asList("coding", "gaming"));
Document person3 = new Document("name", "Zeynep Kaya")
.append("age", 28)
.append("city", "Bursa")
.append("email", "zeynep.kaya@example.com")
.append("hobbies", Arrays.asList("reading", "yoga", "traveling"));
List people = Arrays.asList(person2, person3);
InsertManyResult insertManyResult = collection.insertMany(people);
System.out.println("Birden fazla belge eklendi. Eklenen ID'ler: " + insertManyResult.getInsertedIds());
} catch (Exception e) {
System.err.println("Ekleme operasyonu hatası: " + e.getMessage());
e.printStackTrace();
}
}
}
Her belge eklendiğinde, MongoDB otomatik olarak bir _id alanı oluşturur ve buna benzersiz bir ObjectId değeri atar. Bu _id alanı, belgenin birincil anahtarıdır.
Read (Okuma) Operasyonları
Veritabanından belge okumak için find() metodunu kullanırız. Bu metot, sorgu sonuçlarını döndüren bir FindIterable nesnesi döner.
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.Filters;
import com.mongodb.client.model.Projections;
import com.mongodb.client.model.Sorts;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.util.ArrayList;
import java.util.List;
public class ReadOperations {
private static final String CONNECTION_STRING = "mongodb://localhost:27017";
private static final String DATABASE_NAME = "mydatabase";
private static final String COLLECTION_NAME = "people";
public static void main(String[] args) {
try (MongoClient mongoClient = MongoClients.create(CONNECTION_STRING)) {
MongoDatabase database = mongoClient.getDatabase(DATABASE_NAME);
MongoCollection collection = database.getCollection(COLLECTION_NAME);
// Önceki ekleme işlemlerinden sonra koleksiyonun boş olmadığından emin olalım.
// Eğer boşsa, test için birkaç belge ekleyelim.
if (collection.countDocuments() == 0) {
collection.insertMany(Arrays.asList(
new Document("name", "Ayşe Yılmaz").append("age", 25).append("city", "Ankara"),
new Document("name", "Mehmet Demir").append("age", 35).append("city", "İzmir"),
new Document("name", "Zeynep Kaya").append("age", 28).append("city", "Bursa"),
new Document("name", "Ali Can").append("age", 30).append("city", "İstanbul")
));
}
System.out.println("\n--- Tüm Belgeleri Okuma ---");
for (Document doc : collection.find()) {
System.out.println(doc.toJson());
}
System.out.println("\n--- Filtreleme (Yaşı 30'dan büyük olanlar) ---");
Bson filterAge = Filters.gt("age", 30); // age > 30
for (Document doc : collection.find(filterAge)) {
System.out.println(doc.toJson());
}
System.out.println("\n--- Birden Fazla Filtreleme (Yaşı 25 ve 35 arasında olanlar VE Ankara'da yaşayanlar) ---");
Bson filterMultiple = Filters.and(
Filters.gte("age", 25), // age >= 25
Filters.lte("age", 35), // age <= 35
Filters.eq("city", "Ankara") // city == "Ankara"
);
for (Document doc : collection.find(filterMultiple)) {
System.out.println(doc.toJson());
}
System.out.println("\n--- Tek Bir Belgeyi Okuma (İlk Eşleşen) ---");
Document firstPerson = collection.find(Filters.eq("name", "Ayşe Yılmaz")).first();
if (firstPerson != null) {
System.out.println(firstPerson.toJson());
} else {
System.out.println("Ayşe Yılmaz bulunamadı.");
}
System.out.println("\n--- Sıralama (Yaşa göre azalan, isme göre artan) ---");
Bson sortCriteria = Sorts.descending("age", "name"); // Önce yaşa göre azalan, sonra isme göre artan
for (Document doc : collection.find().sort(sortCriteria)) {
System.out.println(doc.toJson());
}
System.out.println("\n--- Projeksiyon (Sadece belirli alanları gösterme) ---");
// Sadece "name" ve "city" alanlarını göster, "_id" alanını gizle
Bson projectionFields = Projections.fields(
Projections.include("name", "city"),
Projections.excludeId()
);
for (Document doc : collection.find().projection(projectionFields)) {
System.out.println(doc.toJson());
}
System.out.println("\n--- Sonuçları Listeye Çevirme ---");
List peopleList = collection.find(Filters.gt("age", 20)).into(new ArrayList<>());
System.out.println("Yaşı 20'den büyük olanlar listeye çevrildi. Toplam: " + peopleList.size());
peopleList.forEach(doc -> System.out.println(doc.getString("name") + " - " + doc.getInteger("age")));
} catch (Exception e) {
System.err.println("Okuma operasyonu hatası: " + e.getMessage());
e.printStackTrace();
}
}
}
Filters Sınıfı: Sorgu filtrelerini oluşturmak için com.mongodb.client.model.Filters sınıfının statik metotlarını kullanırız. Örnekler:
* Filters.eq("field", value): Eşittir
* Filters.gt("field", value): Büyüktür
* Filters.gte("field", value): Büyüktür veya eşittir
* Filters.lt("field", value): Küçüktür
* Filters.lte("field", value): Küçüktür veya eşittir
* Filters.in("field", value1, value2): Belirtilen değerlerden birine sahip
* Filters.and(filter1, filter2): VE koşulu
* Filters.or(filter1, filter2): VEYA koşulu
Sorts Sınıfı: Sorgu sonuçlarını sıralamak için com.mongodb.client.model.Sorts sınıfını kullanırız.
* Sorts.ascending("field"): Artan sıra
* Sorts.descending("field"): Azalan sıra
Projections Sınıfı: Belirli alanları seçmek veya gizlemek için com.mongodb.client.model.Projections sınıfını kullanırız.
* Projections.include("field1", "field2"): Sadece belirtilen alanları dahil et
* Projections.exclude("field1"): Belirtilen alanı hariç tut
* Projections.excludeId(): _id alanını hariç tut
Update (Güncelleme) Operasyonları
Mevcut belgeleri güncellemek için updateOne() ve updateMany() metotlarını kullanırız. Bu metotlar iki ana argüman alır: bir filtre (hangi belgelerin güncelleneceğini belirler) ve bir güncelleme tanımı (hangi alanların nasıl güncelleneceğini belirler).
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.Filters;
import com.mongodb.client.model.Updates;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.conversions.Bson;
public class UpdateOperations {
private static final String CONNECTION_STRING = "mongodb://localhost:27017";
private static final String DATABASE_NAME = "mydatabase";
private static final String COLLECTION_NAME = "people";
public static void main(String[] args) {
try (MongoClient mongoClient = MongoClients.create(CONNECTION_STRING)) {
MongoDatabase database = mongoClient.getDatabase(DATABASE_NAME);
MongoCollection collection = database.getCollection(COLLECTION_NAME);
// Güncelleme öncesi test verisi ekleyelim
if (collection.countDocuments() == 0) {
collection.insertMany(Arrays.asList(
new Document("name", "Ayşe Yılmaz").append("age", 25).append("city", "Ankara"),
new Document("name", "Mehmet Demir").append("age", 35).append("city", "İzmir"),
new Document("name", "Zeynep Kaya").append("age", 28).append("city", "Bursa"),
new Document("name", "Ali Can").append("age", 30).append("city", "İstanbul")
));
}
System.out.println("\n--- Tek Belgeyi Güncelleme (Update One) ---");
Bson filter = Filters.eq("name", "Ayşe Yılmaz");
Bson update = Updates.set("city", "Eskişehir"); // city alanını "Eskişehir" olarak güncelle
UpdateResult updateResult = collection.updateOne(filter, update);
System.out.println("Ayşe Yılmaz güncellendi. Eşleşen: " + updateResult.getMatchedCount() + ", Değiştirilen: " + updateResult.getModifiedCount());
// Birden fazla alanı güncelleme
filter = Filters.eq("name", "Mehmet Demir");
update = Updates.combine(
Updates.set("age", 36), // Yaşı 36 yap
Updates.set("hobbies", Arrays.asList("coding", "reading")) // Hobileri güncelle
);
updateResult = collection.updateOne(filter, update);
System.out.println("Mehmet Demir güncellendi. Eşleşen: " + updateResult.getMatchedCount() + ", Değiştirilen: " + updateResult.getModifiedCount());
System.out.println("\n--- Birden Fazla Belgeyi Güncelleme (Update Many) ---");
filter = Filters.gt("age", 29); // Yaşı 29'dan büyük olan herkesi bul
update = Updates.inc("age", 1); // Yaşlarını 1 artır ($inc operatörü)
updateResult = collection.updateMany(filter, update);
System.out.println("Yaşı 29'dan büyük olanlar güncellendi. Eşleşen: " + updateResult.getMatchedCount() + ", Değiştirilen: " + updateResult.getModifiedCount());
System.out.println("\n--- Güncelleme Sonrası Veriler ---");
for (Document doc : collection.find()) {
System.out.println(doc.toJson());
}
} catch (Exception e) {
System.err.println("Güncelleme operasyonu hatası: " + e.getMessage());
e.printStackTrace();
}
}
}
Updates Sınıfı: Güncelleme tanımlarını oluşturmak için com.mongodb.client.model.Updates sınıfının statik metotlarını kullanırız. Önemli operatörler:
* Updates.set("field", value): Alanın değerini belirler.
* Updates.inc("field", value): Sayısal bir alanı belirtilen değer kadar artırır/azaltır.
* Updates.unset("field"): Alanı belgeden kaldırır.
* Updates.push("field", value): Bir diziye eleman ekler.
* Updates.pull("field", value): Bir diziden belirtilen değeri kaldırır.
* Updates.combine(update1, update2): Birden fazla güncelleme işlemini birleştirir.
Delete (Silme) Operasyonları
Belgeleri silmek için deleteOne() ve deleteMany() metotlarını kullanırız. Bu metotlar, silinecek belgeleri belirlemek için bir filtre argümanı alır.
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.Filters;
import com.mongodb.client.result.DeleteResult;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.util.Arrays;
public class DeleteOperations {
private static final String CONNECTION_STRING = "mongodb://localhost:27017";
private static final String DATABASE_NAME = "mydatabase";
private static final String COLLECTION_NAME = "people";
public static void main(String[] args) {
try (MongoClient mongoClient = MongoClients.create(CONNECTION_STRING)) {
MongoDatabase database = mongoClient.getDatabase(DATABASE_NAME);
MongoCollection collection = database.getCollection(COLLECTION_NAME);
// Silme öncesi test verisi ekleyelim
collection.drop(); // Koleksiyonu temizleyelim ki her çalıştırmada aynı başlangıç durumu olsun
collection.insertMany(Arrays.asList(
new Document("name", "Ayşe Yılmaz").append("age", 25).append("city", "Ankara"),
new Document("name", "Mehmet Demir").append("age", 35).append("city", "İzmir"),
new Document("name", "Zeynep Kaya").append("age", 28).append("city", "Bursa"),
new Document("name", "Ali Can").append("age", 30).append("city", "İstanbul"),
new Document("name", "Deniz Ak").append("age", 25).append("city", "Ankara")
));
System.out.println("Başlangıçta koleksiyonda " + collection.countDocuments() + " belge var.");
System.out.println("\n--- Tek Belgeyi Silme (Delete One) ---");
Bson filter = Filters.eq("name", "Ayşe Yılmaz");
DeleteResult deleteResult = collection.deleteOne(filter);
System.out.println("Ayşe Yılmaz silindi. Silinen belge sayısı: " + deleteResult.getDeletedCount());
System.out.println("Şimdi koleksiyonda " + collection.countDocuments() + " belge var.");
System.out.println("\n--- Birden Fazla Belgeyi Silme (Delete Many) ---");
filter = Filters.eq("city", "Ankara"); // Ankara'da yaşayan tüm kişileri sil
deleteResult = collection.deleteMany(filter);
System.out.println("Ankara'da yaşayanlar silindi. Silinen belge sayısı: " + deleteResult.getDeletedCount());
System.out.println("Şimdi koleksiyonda " + collection.countDocuments() + " belge var.");
System.out.println("\n--- Silme Sonrası Kalan Veriler ---");
for (Document doc : collection.find()) {
System.out.println(doc.toJson());
}
} catch (Exception e) {
System.err.println("Silme operasyonu hatası: " + e.getMessage());
e.printStackTrace();
}
}
}
DeleteResult nesnesi, silinen belge sayısını içeren getDeletedCount() metodunu sunar.
POJO Sınıfları ile Çalışma (Otomatik Eşleştirme)
Document sınıfını doğrudan kullanmak, basit senaryolar için uygun olsa da, büyük ve karmaşık projelerde kodun okunabilirliğini ve bakımını zorlaştırabilir. Java’nın nesne yönelimli yapısından tam olarak faydalanmak için POJO (Plain Old Java Object) sınıflarını kullanabiliriz. MongoDB Java Driver, POJO’ları otomatik olarak MongoDB belgelerine ve tersine dönüştürmek için bir mekanizma sunar.
POJO Sınıfı Tanımlama
Örnek bir Person POJO sınıfı oluşturalım:
import org.bson.codecs.pojo.annotations.BsonId;
import org.bson.types.ObjectId;
import java.util.List;
import java.util.Objects;
public class Person {
@BsonId // MongoDB'nin _id alanına karşılık gelir
private ObjectId id;
private String name;
private int age;
private String city;
private String email;
private List hobbies;
// Varsayılan constructor, POJO mapping için gereklidir
public Person() {
}
public Person(String name, int age, String city, String email, List hobbies) {
this.name = name;
this.age = age;
this.city = city;
this.email = email;
this.hobbies = hobbies;
}
// Getter ve Setter metotları
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List getHobbies() {
return hobbies;
}
public void setHobbies(List hobbies) {
this.hobbies = hobbies;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", city='" + city + '\'' +
", email='" + email + '\'' +
", hobbies=" + hobbies +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(id, person.id) &&
Objects.equals(name, person.name) &&
Objects.equals(city, person.city) &&
Objects.equals(email, person.email) &&
Objects.equals(hobbies, person.hobbies);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age, city, email, hobbies);
}
}
Önemli Notlar:
* @BsonId anotasyonu, id alanının MongoDB’deki _id alanına eşleştiğini belirtir. ObjectId tipi kullanmak en yaygın yaklaşımdır.
* Varsayılan (parametresiz) bir constructor, POJO mapping için zorunludur.
* Tüm alanlar için getter ve setter metotları bulunmalıdır.
* toString(), equals(), hashCode() metotlarını override etmek, hata ayıklama ve karşılaştırma için iyi bir pratiktir.
POJO Mapping ile MongoClient Yapılandırması
POJO mapping’i etkinleştirmek için MongoClient oluştururken özel bir CodecRegistry yapılandırmamız gerekir.
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
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.Filters;
import com.mongodb.client.model.Updates;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.InsertOneResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.codecs.configuration.CodecProvider;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import java.util.Arrays;
import java.util.List;
import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry;
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;
public class PojoCrudOperations {
private static final String CONNECTION_STRING = "mongodb://localhost:27017";
private static final String DATABASE_NAME = "mydatabase";
private static final String COLLECTION_NAME = "people_pojo";
public static void main(String[] args) {
// POJO Codec Provider'ı yapılandırma
CodecProvider pojoCodecProvider = PojoCodecProvider.builder().automatic(true).build();
CodecRegistry pojoCodecRegistry = fromRegistries(getDefaultCodecRegistry(), fromProviders(pojoCodecProvider));
try (MongoClient mongoClient = MongoClients.create(MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(CONNECTION_STRING))
.codecRegistry(pojoCodecRegistry) // CodecRegistry'yi buraya ekliyoruz
.build())) {
MongoDatabase database = mongoClient.getDatabase(DATABASE_NAME);
// Koleksiyonu Person tipiyle alıyoruz
MongoCollection collection = database.getCollection(COLLECTION_NAME, Person.class);
// Önceki verileri temizle
collection.drop();
System.out.println("Koleksiyon temizlendi.");
// CREATE - POJO ile Tek Belge Ekleme
Person person1 = new Person("Deniz Can", 22, "Antalya", "deniz.can@example.com", Arrays.asList("diving", "cooking"));
InsertOneResult insertOneResult = collection.insertOne(person1);
System.out.println("Eklendi (Person 1): " + person1.getName() + ", ID: " + insertOneResult.getInsertedId());
// MongoDB'nin atadığı _id değerini POJO'ya geri yazabiliriz
person1.setId(insertOneResult.getInsertedId().asObjectId().getValue());
// CREATE - POJO ile Birden Fazla Belge Ekleme
Person person2 = new Person("Emre Ak", 40, "Adana", "emre.ak@example.com", Arrays.asList("fishing", "cycling"));
Person person3 = new Person("Filiz Gür", 33, "Samsun", "filiz.gur@example.com", Arrays.asList("painting", "gardening"));
List people = Arrays.asList(person2, person3);
collection.insertMany(people);
System.out.println("Birden fazla kişi eklendi.");
// READ - Tüm Belgeleri Okuma
System.out.println("\n--- Tüm Kişiler ---");
for (Person p : collection.find()) {
System.out.println(p);
}
// READ - Filtreleme (Yaşı 30'dan büyük olanlar)
System.out.println("\n--- Yaşı 30'dan Büyük Kişiler ---");
Bson filterAge = Filters.gt("age", 30);
for (Person p : collection.find(filterAge)) {
System.out.println(p);
}
// UPDATE - Tek Belgeyi Güncelleme
System.out.println("\n--- Deniz Can'ı Güncelleme ---");
Bson updateFilter = Filters.eq("_id", person1.getId());
Bson updateOperation = Updates.set("city", "Muğla");
UpdateResult updateResult = collection.updateOne(updateFilter, updateOperation);
System.out.println("Deniz Can güncellendi. Eşleşen: " + updateResult.getMatchedCount() + ", Değiştirilen: " + updateResult.getModifiedCount());
// Güncellenmiş belgeyi tekrar okuyalım
Person updatedPerson = collection.find(Filters.eq("_id", person1.getId())).first();
System.out.println("Güncellenmiş Deniz Can: " + updatedPerson);
// DELETE - Tek Belgeyi Silme
System.out.println("\n--- Emre Ak'ı Silme ---");
Bson deleteFilter = Filters.eq("name", "Emre Ak");
DeleteResult deleteResult = collection.deleteOne(deleteFilter);
System.out.println("Emre Ak silindi. Silinen belge sayısı: " + deleteResult.getDeletedCount());
System.out.println("\n--- Son Kalan Kişiler ---");
for (Person p : collection.find()) {
System.out.println(p);
}
} catch (Exception e) {
System.err.println("POJO CRUD operasyonu hatası: " + e.getMessage());
e.printStackTrace();
}
}
}
Bu örnekte, PojoCodecProvider.builder().automatic(true).build() ile otomatik POJO eşleştirmeyi etkinleştiriyor ve bunu MongoClientSettings içine ekliyoruz. Ardından database.getCollection(COLLECTION_NAME, Person.class) çağrısı ile koleksiyonu doğrudan Person tipiyle alıyoruz. Bu sayede, collection.find() gibi metotlar doğrudan Person nesneleri döndürür ve collection.insertOne(person) gibi metotlar da Person nesnelerini doğrudan kabul eder. Bu, kodunuzu çok daha temiz ve nesne yönelimli hale getirir.
Gelişmiş Konular ve En İyi Uygulamalar
Bu bölümde, MongoDB Java CRUD operasyonlarınızı daha sağlam, performanslı ve güvenli hale getirecek bazı ek konulara değineceğiz.
Hata Yönetimi (Error Handling)
Veritabanı işlemleri sırasında çeşitli hatalar meydana gelebilir (bağlantı sorunları, yetkilendirme hataları, veri bütünlüğü ihlalleri vb.). Her zaman try-catch blokları kullanarak bu hataları yakalamalı ve uygun şekilde işlemelisiniz. MongoDB Java Driver, MongoException veya alt sınıflarını fırlatır.
try {
// MongoDB operasyonları
} catch (MongoTimeoutException e) {
System.err.println("MongoDB bağlantı zaman aşımı: " + e.getMessage());
} catch (MongoWriteException e) {
System.err.println("MongoDB yazma hatası: " + e.getMessage());
// Hatanın detaylarına e.getError().getCode(), e.getError().getMessage() ile erişilebilir.
} catch (MongoException e) {
System.err.println("Genel MongoDB hatası: " + e.getMessage());
} catch (Exception e) {
System.err.println("Beklenmedik bir hata oluştu: " + e.getMessage());
}
Kaynak Yönetimi
MongoClient nesnesi, kaynak yoğun bir nesnedir ve uygulamanızın ömrü boyunca tek bir örnek olarak tutulması (singleton deseni) ve işi bittiğinde uygun şekilde kapatılması önerilir. try-with-resources ifadesi, MongoClient‘ın otomatik olarak kapatılmasını sağlayarak kaynak sızıntılarını önlemenin en iyi yoludur.
try (MongoClient mongoClient = MongoClients.create(CONNECTION_STRING)) {
// İşlemler
} // mongoClient burada otomatik olarak kapanır
Performans İpuçları
* İndeksler (Indexes): Sıkça sorguladığınız alanlara indeks eklemek, okuma operasyonlarının performansını önemli ölçüde artırır. collection.createIndex(Indexes.ascending("field")) metodu ile indeks oluşturabilirsiniz.
* Toplu İşlemler (Batch Operations): Çok sayıda belge eklerken, güncellerken veya silerken insertMany(), updateMany(), deleteMany() gibi toplu işlem metotlarını kullanmak, tek tek işlem yapmaktan çok daha verimlidir.
* Bağlantı Havuzu (Connection Pooling): MongoDB Java Driver, dahili olarak bir bağlantı havuzu kullanır. MongoClientSettings üzerinden havuz boyutunu ve diğer ayarları yapılandırarak performansı optimize edebilirsiniz.
* Projeksiyon: find() sorgularında sadece ihtiyacınız olan alanları seçmek (Projections.include()), ağ trafiğini azaltır ve performansı artırır.
Güvenlik
Üretim ortamlarında MongoDB’yi kullanırken güvenlik büyük önem taşır:
* Kimlik Doğrulama (Authentication): MongoDB’yi varsayılan olarak kimlik doğrulaması olmadan çalıştırmayın. Kullanıcılar ve roller oluşturun ve uygulamalarınızda bu kimlik bilgilerini kullanarak bağlanın (mongodb://username:password@host:port/database).
* Yetkilendirme (Authorization): Her kullanıcının sadece ihtiyacı olan verilere erişebildiğinden emin olmak için uygun roller ve izinler tanımlayın.
* Şifreleme: Hassas verileri depolarken alan düzeyinde şifreleme veya disk şifreleme gibi yöntemleri düşünebilirsiniz.
* Ağ Güvenliği: MongoDB sunucunuzun sadece güvenilir IP adreslerinden erişilebilir olduğundan emin olun (firewall kuralları).
Sonuç
Bu kapsamlı makalede, Java uygulamalarınızda MongoDB ile nasıl etkileşim kuracağınızı, geliştirme ortamını nasıl hazırlayacağınızı ve temel CRUD operasyonlarını hem Document sınıfı hem de POJO mapping ile nasıl gerçekleştireceğinizi öğrendiniz. MongoDB’nin esnekliği ve Java’nın gücü bir araya geldiğinde, modern ve ölçeklenebilir uygulamalar geliştirmek için çok güçlü bir kombinasyon elde edersiniz.
Artık temel bilgileri edindiğinize göre, MongoDB’nin daha gelişmiş özelliklerini keşfetmeye başlayabilirsiniz:
* Aggregation Framework: Karmaşık veri analizi ve raporlama için güçlü bir araç.
* Transactions: Birden fazla belge veya koleksiyon üzerinde atomik işlemler yapmak için.
* Change Streams: Veritabanındaki gerçek zamanlı değişiklikleri izlemek için.
* GridFS: Büyük dosyaları (resimler, videolar vb.) depolamak için.
Umarız bu eğitim, MongoDB ve Java dünyasına sağlam bir başlangıç yapmanıza yardımcı olmuştur. Başarılar dileriz!
