Yazılım geliştirme mülakatları için hazırlanmış sorular ve cevaplar
Java'da ClassLoader, Java programlarındaki sınıf dosyalarını yüklemek için kullanılan bir sınıftır. Java kodu, javac derleyicisi tarafından sınıf dosyasına derlenir ve JVM, sınıf dosyasında yazılmış byte kodlarını çalıştırarak Java programını yürütür.
Java'da üç varsayılan ClassLoader vardır: Bootstrap, Extension ve System (Application) ClassLoader.
String: String sınıfı değiştirilemez (immutable) bir sınıftır. Bir String nesnesi oluşturulduktan sonra, onun içeriği değiştirilemez. Örneğin, bir String üzerinde herhangi bir değişiklik yapmak (bir karakter eklemek, çıkarmak veya değiştirmek) yeni bir String nesnesi oluşturur.
StringBuilder: StringBuilder sınıfı değiştirilebilir (mutable) bir sınıftır. Bir StringBuilder nesnesi oluşturulduktan sonra, onun içeriği değiştirilebilir. Bu, aynı nesne üzerinde değişiklik yapmanıza olanak tanır ve genellikle daha verimlidir.
StringBuilder: Thread-safe değildir. Bu, StringBuilder nesnesinin aynı anda birden fazla thread tarafından güvenli bir şekilde kullanılamayacağı anlamına gelir. Tek thread'li uygulamalarda veya birden fazla thread'in aynı StringBuilder nesnesine erişmeyeceği durumlarda kullanılması uygundur.
StringBuffer: Thread-safe'dir. Bu sınıfın tüm yöntemleri senkronize edilmiştir, yani aynı anda birden fazla thread tarafından güvenli bir şekilde kullanılabilir. Bu nedenle, çok thread'li uygulamalarda veya birden fazla thread'in aynı StringBuffer nesnesine erişmesi gereken durumlarda kullanılması uygundur.
Set Nesnesi: Kendisine verilen elemanların her birinde sadece bir tanesini tutar. Kopya ya da tekrarlanan elemanları barındırmaz.
List Nesnesi: Kendisine verilen elemanları sıralı şekilde tutar. Tekrarlana elemanları barındırabilir.
Map Nesnesi: Her biri birbirinden farklı anahtarlar ile eşleştirilen nesnelerden oluşur.
PreparedStatement ile SQL ifadelerimizi veritabanımızda önceden derlenmek üzere gönderebileceğimiz ve her defasında derlenmiş hale değer göndererek tekrar tekrar kullanabileceğimiz bir yapıdır.
Statement nesnesinde programımız üzerinde ifade derlenip veritabanımız sadece sorgulama işlemini gerçekleştirmekte idi.
Error, jvm tarafından runtime'da handle edilmesi mümkün olmayan türden hatalardır. Exception ise try catch ile handle edilebilir. Java'da exceptionları 5 farklı keyword ile handle edilebilir. -- Try -- Catch -- Finally -- Throw -- Throws
RuntimeException ve Error dışında Throwable sınıfını extend eden sınıflar Checked Exceptions olarak tanımlanabilir. Checked Exceptionlar compile edilirken alınan hatalardır. (IOException, SQLException)
RuntimeException sınıfını extend eden exceptionlar Unchecked Exceptionlar olarak adlandırılır. Compile edilirken kontrol edilip gözükmez, (ArithmeticException, NullPointerException)
Java'da kullandığımız geleneksel threadler (bundan sonra Platform Thread olarak da bahsedeceğiz) işletim sistemi tarafından yönetilir ve planlanır. Yeni bir platform thread oluşturmak için bir sistem çağrısı yapılmalıdır ve bu maliyetli bir işlemdir.
Geleneksel threadlerin aksine virtual threadler JVM tarafından yönetilir. Bu nedenle, tahsisleri bir sistem çağrısı gerektirmez ve işletim sisteminin bağlam anahtarından (context switch) muaftırlar. Sonuç olarak, sistemin bağlam anahtarından kurtulduğumuz için daha az maliyetle birçok virtual thread üretebiliriz. Ayrıca virtual threadler, gerçek çekirdek thread olan taşıyıcı threadler üzerinde çalışır. Virtual threadler, Java kodu perspektifinden normal threadler gibi hissedilir, ancak işletim sistemi threadlerine 1:1 eşlenmezler. Taşıyıcı thread havuzundan uygun olan taşıyıcı threadin üzerine virtual threadler eşlenir.
Thread safe, birden çok threadin bir kaynağı aynı anda kullanması durumlarında ortaya çıkan tutarsızlıkların sonucundaki hatalara karşı; o anki threadin kaynağını güvenceye alan ve bunu o kaynağı kullanan tüm threadler için uygulayan bir konsepttir.
Asenkron yani multithreading bir yapıda threadlerin aynı veri kaynağına erişip değiştirmeye çalışması, hatalı davranışların sergilenmesine ve tutarlı bir sonuç elde edilememesine neden olabilir. Bu ve bunun gibi problemleri önlemek için threadleri güvenli bir şekilde tasarlamak ve geliştirmek gerekir. Bu metodolojiye thread safe, iş parçacığı güvenliği denir.
Bir nesnenin veya bir sınıfın saklanacak forma dönüştürülme işlemidir. Extend edilen Serilization sınıfı alt sınıf olan kullanacağımız sınıfın byte'lar halinde streamlere yazılabilir böylece bir java objesi veritabanına kaydedilebilir.
Deserilization ise byte haline çevrilen java objesinin eski haline çevrimine denir.
Thread safe, birden çok threadin bir kaynağı aynı anda kullanması durumlarında ortaya çıkan tutarsızlıkların sonucundaki hatalara karşı; o anki threadin kaynağını güvenceye alan ve bunu o kaynağı kullanan tüm threadler için uygulayan bir konsepttir.
Asenkron yani multithreading bir yapıda threadlerin aynı veri kaynağına erişip değiştirmeye çalışması, hatalı davranışların sergilenmesine ve tutarlı bir sonuç elde edilememesine neden olabilir. Bu ve bunun gibi problemleri önlemek için threadleri güvenli bir şekilde tasarlamak ve geliştirmek gerekir. Bu metodolojiye thread safe, iş parçacığı güvenliği denir.
ConcurrentHashMap, Java'da eşzamanlı (concurrent) programlama için kullanılan bir veri yapısıdır. Birden fazla thread tarafından aynı anda güvenli bir şekilde erişilebilen bir harita (map) implementasyonudur.
ConcurrentHashMap, segmentasyon (segmentation) adı verilen bir teknik kullanır. Bu teknik, haritayı birden fazla segmente böler ve her segment kendi kilidine sahiptir. Bu sayede, farklı segmentlere erişen threadler birbirini engellemez ve performans artar.
ConcurrentHashMap, Hashtable'a göre daha iyi performans sunar çünkü Hashtable tüm haritayı kilitlerken, ConcurrentHashMap sadece ilgili segmenti kilitler. Bu da birden fazla threadin aynı anda farklı segmentlere erişmesine izin verir.
Single responsibilty bir nesnenin tek bir amaçla yaratılmasını konu alır. Bağlı olduğu sınıfın içerdiği davranışsal özellikler tek bir amaca uygun olmalı başka bir davranış göstermemelidir. Örneğin bir çalışan sınıfı içerisinde vergi hesaplama fonskiyonu bulunamaz. Bu single responsibilty prensibine aykırı bir kod yazım şeklidir.
"Dependency Inversion" prensibinin uygulanmasını içeren bir patterndir.Dependency Injection tekniğinde bağımlılık oluşturacak parçalarının ayrılıp, bunların sisteme dışarıdan verilmesi (enjekte edilmesi) ile meydana gelir. Temel olarak 3 tür DI vardır. Bunlar;
Tüm yöntemler bağımlı olan sınıfları dışarıdan enjekte etmeye dayanır.
DAO Data Access Object ifadesini: Bu araç geliştiricilere özellikle Java kaynaklı veri erişim araçları ile daha kolay çalışma imkanı sunar. Bir yazılım uygulamasında veritabanı veya diğer veri kaynaklarına erişimi sağlayan bir tasarım desenidir. DAO ile diğer katmanlar etkilenmeden veritabanı ve bilgi bankası değiştirilebilir.
- Spring Framework → Konfigürasyon ağırlıklı, XML veya Java Config gerekebilir.
- Spring Boot → Spring üzerine kuruludur, hazır starter bağımlılıkları ve otomatik konfigürasyonu sayesinde çok daha hızlı geliştirme sağlar.
Özet: Spring Boot = Spring + Auto Configuration + Embedded Server + Production tools
Spring Boot uygulama başlarken classpath'teki bağımlılıkları kontrol eder ve uygun konfigürasyonu otomatik yükler.
Bunu sağlayan mekanizma: @EnableAutoConfiguration anotasyonu.
Spring Boot Actuator, uygulamanın çalışma zamanında sağlık durumu, metrikler, loglar, env bilgileri gibi üretim seviyesinde izleme (monitoring) ve yönetim özellikleri sağlar.
- application.properties → Anahtar-değer (key=value) formatındadır.
- application.yml → YAML formatında, daha okunabilir ve hiyerarşik yapı destekler.
Spring Boot, farklı ortamlar için (dev, test, prod) ayrı konfigürasyonlara izin verir. @Profile anotasyonu ile hangi bean'in hangi ortamda çalışacağını belirleyebiliriz.
@Profile("dev")
@Bean
public DataSource devDataSource() {
...
}
Spring Security, Spring Boot'ta otomatik entegre gelir. Varsayılan olarak tüm endpointler korunur, user isimli kullanıcı oluşturulur ve şifre konsolda gösterilir. Gelişmiş senaryolar için:
@Transactional(rollbackFor = Exception.class)
public void myMethod() throws Exception {
...
}
Çözüm: Transactional metodu başka bir bean'den çağır veya aspectJ ile compile-time weaving kullan.
Çözüm: Transactional metod public olmalı.
@Transactional
public void saveData() {
try {
// hata fırlatılır
} catch (Exception e) {
// log atıp swallow ederse rollback OLMAZ
}
}
Çözüm:
Çözüm: Doğru propagation stratejisini seçmek (REQUIRED, REQUIRES_NEW, NESTED vs.).
Özet:
@Transactional var ama rollback olmuyorsa en büyük sebepler:
- Thread Dump, JVM üzerinde o anda çalışan tüm thread'lerin (iş parçacıklarının) durumunu gösteren bir rapordur.
- İçinde şunlar olur:
Kısaca: Thread Dump = JVM'in "röntgen filmi"
Thread Dump ne zaman kullanılır?
1. @Component
→ En genel stereotype anotasyondur.
→ Spring bean'i tanımlamak için kullanılır.
→ @Component ile işaretlenmiş sınıf, component-scan sırasında Spring IoC Container'a
eklenir.
Ne zaman kullanılır?
→ Eğer sınıfın rolü service, repository veya controller gibi belirgin değilse, genel
amaçlı bir Spring bileşeni olduğunu belirtmek için.
2. @Service
→ @Component'in specialization (özelleştirilmiş) halidir.
→ Semantik olarak bu sınıfın iş mantığını (business logic) içerdiğini belirtir.
→ Ekstra olarak Spring AOP ile iş katmanına yönelik işlemlerde (örneğin transaction,
logging, security) anlam kazanır.
Ne zaman kullanılır?
→ Service/Business katmanı sınıflarında.
3. @Repository
→ @Component'in başka bir specialization'ıdır.
→ Semantik olarak bu sınıfın data access layer (DAO) olduğunu belirtir.
→ Ekstra özellik: Spring, @Repository ile işaretlenmiş sınıflarda veritabanı
exception'larını Spring'in DataAccessException hiyerarşisine dönüştürür.
Ne zaman kullanılır?
→ Database erişim katmanında (JPA, JDBC, MongoDB, vb.).
UPDATE orders
SET status = 'CLOSED'
WHERE status = 'PENDING';
Avantajı: Tek transaction, tek IO, çok hızlı.
Not: Eğer güncellemeler farklı değerler içeriyorsa, bu yöntem
her zaman uygulanamayabilir.
jdbcTemplate.batchUpdate(
"UPDATE employees SET salary = ? WHERE id = ?",
employees,
1000, // batch size
(ps, emp) -> {
ps.setBigDecimal(1, emp.getSalary());
ps.setLong(2, emp.getId());
}
);
Batch size genelde 1000 – 5000 arasında
ayarlanır.
Böylece 200.000 update = 200 query yerine, 200 batch × 1000
update olur → çok daha hızlı.
@Transactional
public void bulkUpdate(...) {
// batch işlemler
}
UPDATE employees SET salary = salary * 1.1 WHERE department_id = 5;
→ department_id üzerinde index varsa çok daha hızlı çalışır.
E-commerce gibi sistemlerde "sepet (shopping cart)" performansı kritik bir konudur.
1. Sepet verisini nerede tutarım?
→ Frontend tarafında (client-side):
Avantaj: Hızlı, server yükünü azaltır.
Dezavantaj: User login olmadan farklı cihazlardan senkronize edemezsin.
→ Backend tarafında (server-side):
Genellikle Redis + DB birlikte kullanılır.
Redis → anlık sepet işlemleri (okuma/yazma çok hızlı).
DB → checkout sırasında kalıcı hale getirme.
2. Nasıl hızlı getiririm?
→ Cache (Redis) kullan:
Key → cart:userId
Value → JSON formatında sepet içeriği.
Böylece her istek DB'ye gitmez, Redis'ten milisaniyede gelir.
→ Indexleme yap:
DB'de userId + cartId üzerinde index olmalı.
→ API optimizasyonu:
GET /cart → tek endpoint, tüm ürünleri + toplam tutarı döner.
Gerekirse ürün bilgilerini batch query ile getir (N+1 probleminden kaçın).
3. Nasıl hızlı update ederim?
→ Redis Hash veya JSON set kullan:
Örn: HSET cart:123 productId:456 quantity 3
O(1) performans, çok hızlıdır.
→ Optimistic Locking (ETag, Versioning) kullan:
Aynı sepeti iki istek aynı anda güncellerse çakışmayı önler.
→ Partial update yap:
"Sepeti komple sil → tekrar yaz" yerine sadece değişen ürünü update et.
→ Event-driven yaklaşım:
Update geldiğinde bir Kafka/RabbitMQ event yayınla → DB & Redis senkronize olur.
4. Örnek Senaryo (Redis + DB birlikte)
→ Kullanıcı ürünü sepete ekler → API çağrısı alır.
→ API önce Redis'te günceller (cart:userId).
→ Checkout sırasında Redis'teki sepet alınır, DB'ye yazılır (kalıcı hale gelir).
→ Redis'te TTL (time to live) koy → mesela 7 gün işlem yapılmazsa otomatik silinsin.
5. Spring Boot Örneği (Redis ile)
@Service
public class CartService {
private final RedisTemplate<String, Object> redisTemplate;
private final String CART_KEY_PREFIX = "cart:";
public CartService(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void addToCart(String userId, String productId, int quantity) {
String key = CART_KEY_PREFIX + userId;
redisTemplate.opsForHash().put(key, productId, quantity);
redisTemplate.expire(key, Duration.ofDays(7)); // TTL
}
public Map<Object, Object> getCart(String userId) {
String key = CART_KEY_PREFIX + userId;
return redisTemplate.opsForHash().entries(key);
}
public void removeFromCart(String userId, String productId) {
String key = CART_KEY_PREFIX + userId;
redisTemplate.opsForHash().delete(key, productId);
}
}
Özet
→ Küçük / basit projeler: localStorage + backend DB yeterli.
→ Orta / büyük ölçek: Redis + DB (hybrid model).
→ Çok büyük ölçek (Amazon, Trendyol, Hepsiburada): Redis Cluster + Kafka event + DB
(CQRS ve Event Sourcing yaklaşımı).
1. Spring Batch (BatchConfig)
→ Ne iş yapar:
Büyük veri setleri üzerinde toplu işlem (read-process-write) yapar.
Örnek: 200.000 kaydı güncellemek, dosyadan veri okumak, ETL işlemleri.
→ Nasıl çalışır:
Job → Step → Reader, Processor, Writer
Chunk'lar ile parçalı işleme (batch update)
Restart, retry, skip, transaction yönetimi hazır gelir
→ Özellikleri:
Veri yoğun işleri yönetmek için optimize edilmiştir.
Database, CSV, Excel, JMS, Kafka gibi kaynaklardan veri okuyabilir.
Spring Batch framework'ü, iş mantığını ve state managementi sağlar.
2. Cron Job (@Scheduled)
→ Ne iş yapar:
Belirli zamanlarda veya periyodik olarak bir metodu çalıştırır.
Örnek: Her gece 00:00'da rapor oluşturmak, her 5 dakikada bir API çağrısı yapmak.
→ Nasıl çalışır:
Spring Boot'ta @EnableScheduling ve @Scheduled(cron = "...") ile yapılır.
Tek bir metod çağrılır, çoğunlukla küçük işleri yürütmek için uygundur.
| Özellik | Spring Batch (BatchConfig) | Cron Job (@Scheduled) |
|---|---|---|
| Amaç | Büyük veri işleme, ETL, toplu güncelleme | Zamanlanmış görevler |
| Veri kaynağı | DB, CSV, Excel, JMS, Kafka vs. | Metod / iş mantığı |
| Transaction yönetimi | Var (chunk, retry, skip) | Yok / manuel |
| Performans | Büyük veri setleri için optimize | Küçük/orta işlerde yeterli |
| Zamanlama | Spring Batch job launcher veya scheduler | @Scheduled / cron |
| Durum yönetimi | Job execution, Step execution, restart | Yok |
Yani:
Spring Batch = iş mantığı ve veri işleme
Cron Job = zamanlama ve tetikleme
Evet, Spring Batch büyük veri işleme yönteminde hızlı çalışabilir, ama bu tamamen kullanım şekline ve yapılandırmaya bağlıdır.
1. Neden hızlı çalışabilir?
→ Chunk-based processing:
Büyük veri seti küçük parçalara (chunk) bölünerek işlenir.
Örneğin 200.000 kayıt → chunk size 1000 → 200 batch.
Böylece transaction yönetimi ve memory kullanımı optimize edilir.
→ Batch Update / JDBC Batch:
Update veya insert işlemleri toplu (batch) olarak veritabanına gönderilir.
Tek tek kayıt göndermek yerine, birden fazla kayıt tek seferde işlenir → performans
artar.
→ Transaction yönetimi:
Spring Batch, her chunk için tek bir transaction yönetir.
Gereksiz commit ve rollback'leri azaltır.
→ Restart ve Skip mekanizması:
Hata durumunda yalnızca problemli chunk tekrar işlenir, tüm veri baştan işlenmez.
2. Performansı etkileyen faktörler
→ Chunk boyutu: Çok küçük → sık commit → yavaş. Çok büyük → memory
problemi.
Önerilen: 500–5000 arası, veri tipine ve memory'ye göre ayarla.
→ Veritabanı optimizasyonu:
Index'ler, partitioning, batch insert/update destekleyen DB özellikleri.
→ Reader / Writer seçimi:
JDBC, JPA, Hibernate, CSV veya flat file reader hız farkı yaratır.
→ Parallel processing / Multi-threading:
Spring Batch, step'leri paralel çalıştırabilir. Çok büyük verilerde performans artışı
sağlar.
3. Özet
→ Spring Batch tek başına "otomatik hızlı" değildir, doğru yapılandırılırsa büyük veri
setlerinde çok hızlı çalışır.
→ Ana hız faktörleri: chunk size, batch update, transaction yönetimi, paralel processing
ve reader/writer optimizasyonu.
→ Spring MVC: Thread-per-request modeli (blocking I/O).
→ Spring WebFlux: Event-loop tabanlı (non-blocking, reactive), çok daha az thread ile yüksek concurrency sağlar.
→ Kullanım senaryosu: WebFlux → yüksek trafik, streaming API'ler; MVC → klasik CRUD uygulamaları.
→ Mono<T>: 0 veya 1 sonuç döner.
→ Flux<T>: 0…N sonuç döner (stream).
Örn: Mono<User> (tek kullanıcı), Flux<User> (çok kullanıcı).
→ Servisler dinamik olarak scale olunca IP/port sabit kalmaz.
→ Eureka/Consul gibi discovery server'lar, servislerin birbirini bulmasını sağlar.
→ Örn: OrderService → UserService'in IP'sini bilmeden istek atabilir.
→ Tüm microservice'lerin konfigürasyonlarını merkezi yönetmek için kullanılır.
→ Konfigürasyonlar genellikle Git repo'da tutulur.
→ Dinamik olarak config değişimi (refresh scope) yapılabilir.
→ Amaç: Bir servis sürekli hata veriyorsa diğer servisleri korumak.
→ Resilience4j / Hystrix ile uygulanır.
→ Örn: @CircuitBreaker(name="inventoryService", fallbackMethod="fallback")
→ Kafka: High throughput, event streaming, partition + offset bazlı, log mantığında çalışır. Gerçek zamanlı veri işleme (stream processing).
→ RabbitMQ: Message broker, queue tabanlı, daha klasik mesajlaşma.
→ Cache olarak: @EnableCaching + @Cacheable, @CacheEvict.
→ Data structure store olarak: Hash, List, Set kullanarak (örn: alışveriş sepeti).
→ Message broker olarak: Pub/Sub mekanizması.
→ R2DBC (Reactive Relational Database Connectivity), klasik JDBC'nin reactive versiyonudur.
→ JDBC blocking çalışır → WebFlux uyumlu değil.
→ R2DBC → Non-blocking DB erişimi (Postgres, MySQL, MSSQL için destek var).
→ Microservices'te dağıtık transaction problemini çözmek için kullanılan pattern.
→ Her servis kendi DB'sine sahip → klasik transaction yok.
→ Saga → işlemleri adım adım yürütür, hata olursa compensating transaction ile geri alır.
→ Örn: Sipariş → Ödeme → Stok → kargo. Ödeme başarısız olursa sipariş iptal edilir.
Spring WebFlux çok güçlü ama her durumda uygun değildir. İşte detaylar:
✅ WebFlux'un güçlü olduğu alanlar
→ Çok yüksek concurrency (aynı anda binlerce request → streaming, chat, IoT).
→ Streaming API'ler (Server-Sent Events, WebSocket).
→ I/O bound işler (dosya okuma, başka servis çağırma, DB'den veri çekme).
→ Non-blocking driver'lar ile (R2DBC, Reactive Mongo, WebClient).
❌ WebFlux'un uygun olmadığı senaryolar
✅CPU-bound işler (ağır işlemci yükü)
→ Örn: görüntü işleme, büyük matematiksel hesaplar, AI model çalıştırma.
→ Çünkü WebFlux event-loop modeli kullanır → CPU'yu meşgul eden işler event-loop'u bloke
eder, performans düşer.
→ Çözüm: Bu tür işleri ayrı thread pool (ör. Schedulers.boundedElastic()) üzerinde
çalıştırmak.
✅Blocking API / Driver kullanımı
→ Eğer kullandığın kütüphane blocking ise (örn: klasik JDBC), WebFlux'ın faydasını
kaybedersin.
→ Çünkü tek bir blocking işlem event-loop'u durdurur → diğer tüm request'ler
etkilenir.
→ Çözüm: R2DBC, Reactive MongoDB, Reactive Redis gibi non-blocking driver'lar kullan.
✅Düşük concurrency, basit CRUD uygulamaları
→ Örn: Küçük bir şirket içi CRUD paneli.
→ MVC daha basittir, debugging kolaydır, community daha büyüktür.
→ WebFlux burada gereksiz karmaşıklık yaratır.
✅Transaction-heavy senaryolar (RDBMS + ACID)
→ WebFlux + R2DBC ile transaction yönetimi sınırlıdır.
→ Eğer çok karmaşık SQL transaction'ların varsa (multi-step join, distributed
transaction), klasik Spring MVC + JDBC daha stabil olur.
✅Ecosystem uyumsuzluğu
→ Bazı 3rd party kütüphaneler (özellikle eski olanlar) reactive desteklemez.
→ Örn: raporlama, PDF, e-mail API'leri blocking çalışır → WebFlux'ı verimsiz hale
getirir.
🎯 Özet
WebFlux = non-blocking, I/O-heavy, high concurrency uygulamalar için süper.
Spring MVC = CPU-heavy, transaction-heavy, basit CRUD için daha uygun.
→ Backpressure: Publisher'ın ürettiği veri hızının, Subscriber'ın tüketme hızını aşması.
→ Yönetim yolları: onBackpressureBuffer(), onBackpressureDrop(), limitRate() gibi Reactor operatörleriyle kontrol edilir.
→ WebFlux Netty event-loop modeli kullanır.
→ Default: N CPU çekirdeği için 2N thread oluşturur.
→ Request processing → event-loop threadlerinde yürür, CPU-bound işler için boundedElastic veya parallel scheduler kullanılır.
→ Zuul 1: Servlet (blocking I/O).
→ Spring Cloud Gateway: Netty (non-blocking, reactive).
→ Gateway → route, filter, rate limiting, circuit breaker gibi modern özellikler destekler.
→ Spring Cloud Sleuth → microservice'ler arasında traceId ve spanId ekler.
→ Log'lar merkezi hale gelir, Zipkin/Jaeger UI'dan request flow izlenebilir.
→ Örn: traceId=123 spanId=456 → hangi servislerde gezdiği görülür.
→ Producer tarafında: enable.idempotence=true.
→ Transactional producer + consumer offset commit birlikte yönetilir (transactional.id).
→ Böylece duplicate veya kayıp mesaj engellenir.
→ Redis Sentinel: High availability (master-slave failover).
→ Redis Cluster: Sharding + horizontal scaling.
→ Büyük verilerde Redis Cluster, yüksek availability için Sentinel.
→ Command (yazma) ve Query (okuma) işlemleri ayrı servisler/DB'ler ile yapılır.
→ Örn: Write DB (Postgres), Read DB (ElasticSearch/Redis).
→ Microservices'te performans + ölçeklenebilirlik sağlar.
→ spring-kafka: Classic (blocking) consumer/producer API'si.
→ reactor-kafka: Kafka client'ını Reactive Streams (Flux/Mono) ile entegre eder.
→ WebFlux tabanlı uygulamalarda reactor-kafka tercih edilir.
→ Choreography: Event-driven, servisler birbirine event gönderir (loosely coupled).
→ Orchestration: Merkezi bir Saga orchestrator servis, tüm adımları yönetir.
→ Trade-off: Choreography → basit ama karmaşıklaşabilir, Orchestration → merkezi kontrol ama single point of failure riski.
→ Redis Streams (Kafka benzeri, log-based, consumer group destekli).
→ Event-driven microservices için hafif alternatif.
→ XADD, XREADGROUP komutları ile çalışır.
Spring Boot Auto-Configuration, projenize eklediğiniz bağımlılıkları analiz ederek, uygulamanız için gerekli olan bean'leri otomatik olarak yapılandırır. Bu işlem @EnableAutoConfiguration anotasyonu (genellikle @SpringBootApplication içinde bulunur) ile tetiklenir. Spring Boot, spring.factories dosyalarında tanımlanan AutoConfiguration sınıflarını yükler ve belirli koşullar sağlandığında bu sınıflardaki bean tanımlarını IoC konteynerine ekler.
Spring Boot Actuator, çalışan bir Spring Boot uygulamasının sağlık durumu, metrikleri, yapılandırması ve diğer operasyonel bilgilerini izlemek ve yönetmek için kullanılır. Önemli endpoint'leri şunlardır:
Spring Boot CLI, Spring Boot uygulamalarını hızlı bir şekilde prototiplemek ve çalıştırmak için komut satırı araçları sunar. Özellikle basit uygulamalar veya hızlı denemeler için faydalıdır. Java kodunu derleme ve çalıştırma süreçlerini basitleştirir.
Spring Boot'ta farklı ortamlar (development, test, production vb.) için farklı yapılandırmalar tanımlamak için profiller kullanılır. application-{profilAdı}.properties veya application-{profilAdı}.yml şeklinde isimlendirilmiş yapılandırma dosyaları oluşturularak profiller tanımlanır. Uygulama başlatılırken --spring.profiles.active={profilAdı} argümanı ile veya spring.profiles.active ortam değişkeni ile aktif profil belirtilebilir.
Spring Boot uygulamalarını production ortamına deploy etmek için farklı yöntemler vardır:
Bu kombinasyon türü uygulamada birçok farklı türde tekil bean bulunduğunda kullanılır. Bu kombinasyon her bir ayrı bean'i farklılaştırır.
@Autowired annotasyonu kullanıldığında Spring, bağımlılığı otomatik olarak enjekte eder. Ancak birden fazla aday bean olduğunda, hangi bean'in kullanılması gerektiğini belirtmemiz gerekiyor. İşte @Qualifier annotasyonu bu seçimi yapmamızı kolaylaştırır.
Circuit Breaker, bir servisin aşırı yük altında olduğunu veya düzgün çalışmadığını tespit ettiğinde, otomatik olarak o servise gelen istekleri keser. Bu sayede sistem, tek bir servisin başarısızlığından dolayı tamamen çökme durumuna düşmekten korunmuş olur.
Interceptor, Spring MVC paketinde bulunan bir sınıftır. HTTP isteklerinin öncesi, sonrası ve tamamlandıktan sonra yapılması gereken işlemleri bu sınıf aracılığı ile handle edebilmekteyiz.
Gelen isteklerin endpointe ulaşmadan önce işlenmesini sağlamamıza yarayan bir sınıftır. Bir servlete benzer ve DispatcherServlet ten sonra bulunmaktadır. HTTP isteklerini kontrol etmek için kullanılır. İstek başlamadan önce çağrılır ve HTTP isteği ile ilgili bilgileri içeren HttpServletRequest nesnesini ve HTTP isteği ile ilgili yanıtı döndürecek HttpServletResponse nesnesini alır.
Spring IoC Container, Spring Framework'ün çekirdeğidir. Bu konteyner, nesneleri oluşturur, nesneleri birbirine bağlar, bağımlılıklarını yapılandırır ve tüm yaşam döngüsünü yönetir.
Inversion of control bir yazılım tasarım prensibidir. Ioc ile Uygulama içerisindeki obje instance'larının yönetimi sağlanarak, bağımlılıklarını en aza indirgemek amaçlanmaktadır. Projenizdeki bağımlılıkların oluşturulmasını ve yönetilmesini geliştiricinin yerine, framework'ün yapması olarak da açıklanır.
Framework'ün üzerinde çalıştığımız da görülüyor ki; frameworkler birçok işi kendisi yapmakta ve bizim kodumuzu çalıştırmak için framework gerekli kaynakları ve çalışması gereken metotları oluşturup, yönetmektedir. Yazdığımız kod bloğu çalışacağı zaman, framework bizim kodumuzu çağırır ve çalıştırır daha sonra kontrol yeniden framework'e geçmesi olayının tümüne Inversion Of Control adı verilmektedir.
Spring Framework içinde "scope" bir nesnenin yaşam döngüsünü ve ne kadar süreyle erişilebilir olduğunu tanımlayan bir kavramdır. Spring, çeşitli nesne scope'ları sağlar ve bu scope'lar nesnelerin oluşturulma, kullanılma ve yok edilme şeklini belirler.
Bu scope'lar, Spring uygulamalarında nesnelerin nasıl yönetileceğini belirlemek için kullanılır. Scope belirleme, nesnelerin doğru zamanda oluşturulması, paylaşılması ve yok edilmesi açısından önemlidir ve uygulamanın performansı ve davranışı üzerinde etkili olabilir.
singleton Scope
Bir bean default olarak singleton scope'a sahiptir. Bean singleton scope ile tanımlandığı zaman mevcut application context'imiz içerisinde o bean'den yalnızca tek bir adet initialize edileceğini garanti ederiz. Bu bean ile yapılacak olan tüm request'ler cache'lenmiş olan aynı nesne üzerinden yapılır.
prototype Scope
Prototype ile belirlenmiş bir bean, container içerisinde çağırıldığı her request'te yeniden oluşturulacaktır. Scope notasyonun iki farklı kullanımını aşağıda görebilirsiniz.
Prototype Scope kullandığınızda, her request geldiğinde yeni bir instance döndürür. Diyelim ki bir setter methoduna sahip bir sınıfınız var, şimdi bu sınıf için bir bean oluşturduğunuzda, size her zaman sınıfın yeni bir instance'sını verecek ve nesne niteliklerini değiştirmek için setter'ı özgürce kullanıp çalışacaktır.
request Scope
Request bean'i HTTP isteği geldiğinde oluşturulur. örneğin, bir " /products" API'niz var, şimdi controller bu isteği aldığında ve service methodunu çağırdığında, Request Scope ile bir Bean'iniz olacak ve bu API isteği yanıtı geri gönderene kadar her zaman nesnenin aynı instance'ını alırsınız, ancak yeni bir request geldiğinde, yeni bir instance gönderecek.
session Scope
Session Scope Web Uygulamalarında HTTP isteği geldiğinde oluşturulur. Mesela Spring boot uygulamanız kullanıcı sessionlarını sürdürdüğünde, bu scope yardımcı olabilir.
Session Scope kullandığımızda, tüm Session için (kullanıcı düzeyindeki oturumda) her zaman nesnenin aynı instance'ını return eder. Ancak kullanıcı oturumu kapandığında, yeni bir kullanıcı oturumu için nesnenin yeni bir instance'ını alacaksınız.
application Scope
Bir application scope, ServletContext'in yaşam döngüsü için bean örneğini oluşturur. Bu singleton scope'a benzer ancak aralarında farklılıklar mevcuttur. Bir bean application scope değerine sahipken bu bean çoklu servlet tabanlı uygulamalar ile de paylaşılabilirken, singleton scope değerine sahip bir bean yalnızca mevcut application context'i içerisinde tanımlıdır.
Spring Cloud, mikroservis mimarisi oluşturmayı kolaylaştıran bir dizi araç ve kütüphane sağlayan bir şemsiye projedir. Mikroservisler arasındaki iletişimi, yapılandırma yönetimini, servis keşfini, devre kesici (circuit breaker) mekanizmalarını, API ağ geçidini ve daha birçok dağıtık sistem sorununu çözmek için çözümler sunar.
Spring Cloud Config Server, mikroservis uygulamalarının merkezi bir yerden yapılandırılmasını sağlayan bir servistir. Git, SVN veya HashiCorp Vault gibi farklı backend'lerden yapılandırma bilgilerini okuyabilir ve istemci mikroservislere HTTP üzerinden sunar. Avantajları şunlardır:
Spring Cloud Netflix Eureka, mikroservislerin birbirlerini bulmasını sağlayan bir servis keşfi sunucusudur. Her mikroservis, başlatıldığında Eureka Server'a kendi adres ve port bilgilerini kaydeder. Diğer mikroservisler de Eureka Server'a danışarak ihtiyaç duydukları servisin konumunu öğrenir ve iletişim kurarlar. Bu sayede servislerin dinamik olarak ölçeklenmesi ve yer değiştirmesi kolaylaşır.
Spring Cloud Gateway, mikroservisler için bir API ağ geçidi (API Gateway) görevi gören bir projedir. Temel rolleri şunlardır:
Spring Cloud Circuit Breaker (Resilience4j gibi kütüphaneler aracılığıyla), dağıtık sistemlerde servisler arasındaki iletişimde olası hatalara karşı dayanıklılığı artırmak için kullanılır. Bir servis çağrısı belirli bir eşiği aşan sayıda başarısız olursa, devre kesici açılır ve bir süre boyunca hatalı servise yapılan çağrıları engeller. Bu sayede sistemin tamamının çökmesi önlenir ve hatalı servisin iyileşmesi için zaman tanınır.
Spring Cloud Load Balancer, Eureka gibi bir servis keşfi mekanizmasıyla entegre olarak çalışır. Bir istemci bir servise istek gönderdiğinde, Load Balancer mevcut olan servis örnekleri arasından birini seçerek isteği yönlendirir. Bu, yükün birden fazla servis örneğine eşit olarak dağıtılmasını sağlar ve tek bir servis örneğinin aşırı yüklenmesini önler. Farklı yük dengeleme algoritmaları (round-robin, random, weighted response time vb.) desteklenebilir.
Spring Cloud Stream, mikroservisler arasında olay güdümlü (event-driven) ve asenkron iletişimi basitleştiren bir framework'tür. Kafka, RabbitMQ gibi farklı mesajlaşma aracıları (message broker) için soyut bir katman sağlar. Mikroservisler, Spring Cloud Stream'in sağladığı binder'lar aracılığıyla mesaj kuyruklarına kolayca mesaj gönderebilir ve mesajları dinleyebilirler. Bu, servisler arasındaki bağımlılığı azaltır ve daha ölçeklenebilir ve esnek sistemler oluşturmaya olanak tanır.
Monolitik Mimari: Tüm uygulama tek bir büyük kod tabanı ve tek bir dağıtım birimi olarak geliştirilir.
Mikroservis Mimarisi: Uygulama, bağımsız olarak dağıtılabilen küçük, özel amaçlı servislerden oluşur. Her servis kendi veritabanına ve teknoloji yığınına sahip olabilir.
Mikroservislerin Avantajları:
Mikroservislerin Dezavantajları:
Yaygın iletişim yöntemleri şunlardır:
REST (Synchronous): HTTP protokolü üzerinden yapılan senkron iletişimdir. Basit ve yaygın olarak kullanılır.
Dezavantajı: Servisler arasındaki sıkı bağlılık ve olası performans sorunlarıdır.
Mesajlaşma (Asynchronous): Kafka, RabbitMQ gibi mesaj kuyrukları aracılığıyla yapılan asenkron iletişimdir.
Avantajları: Daha gevşek bağlılık, daha iyi ölçeklenebilirlik ve güvenilirlik sağlar.
Dezavantajı: Karmaşıklık ve eventual consistency (sonunda tutarlılık) sorunlarıdır.
gRPC (Synchronous/Asynchronous): Google tarafından geliştirilen yüksek performanslı, dil bağımsız bir RPC framework'üdür. Protobuf ile veri serileştirme kullanır.
Avantajları: Yüksek performans ve güçlü tip güvenliği sunar.
Dezavantajı: Bazı platformlarda ve dillerde daha az yaygın olmasıdır.
Mikroservislerde genellikle her servis kendi bağımsız veritabanına sahiptir (database per service pattern). Bunun avantajları şunlardır:
Ancak bu yaklaşım, dağıtık işlemler (distributed transactions) gibi veri tutarlılığı sorunlarını da beraberinde getirebilir. Bu sorunları çözmek için Saga pattern gibi yaklaşımlar kullanılabilir.
Mikroservislerde distributed tracing, bir isteğin birden fazla servis üzerinden geçerkenki yolunu ve her bir serviste harcadığı süreyi izlemek için kritik öneme sahiptir. Bu, performans sorunlarını tespit etmek, hataları ayıklamak ve sistemin genel davranışını anlamak için gereklidir.
Kullanılabilecek araçlar arasında Zipkin, Jaeger ve Spring Cloud Sleuth (çeşitli tracing backend'lerini destekler) bulunur.
API Gateway, mikroservis mimarisinde istemciler için tek bir giriş noktası sağlayarak karmaşıklığı azaltır. Temel sorumlulukları şunlardır:
Contract Testing (Kontrat Testi): Bir servis sağlayıcısının (provider) sunduğu API kontratının (istek ve cevap formatları) tüketicisi (consumer) tarafından beklendiği gibi olduğunu doğrular. Amaç, servisler arasındaki entegrasyon sorunlarını erken aşamada tespit etmektir.
End-to-End Testing (Uçtan Uca Test): Tüm sistemin veya belirli bir iş akışının baştan sona doğru çalıştığını doğrular. Birden fazla servisin etkileşimini ve genel sistem davranışını test eder.
İkisi de önemlidir çünkü Contract Testing servisler arası uyumluluğu sağlarken, End-to-End Testing sistemin genel işlevselliğini doğrular. Birbirini tamamlarlar ve birlikte kullanıldıklarında daha güvenilir mikroservis sistemleri oluşturulur.
Bu durumda Saga pattern kullanılabilir. Saga pattern, dağıtık sistemlerde birden fazla servis arasında gerçekleşen işlemlerin tutarlılığını sağlamak için kullanılan bir desendir.
Saga pattern iki şekilde uygulanabilir:
Örneğin, bir sipariş işlemi düşünün: Sipariş servisi → Ödeme servisi → Stok servisi → Kargo servisi. Eğer ödeme servisinde bir hata oluşursa, Saga pattern sipariş servisini iptal ederek işlemleri geri alır.
CQRS (Command Query Responsibility Segregation), bir yazılım tasarım desenidir ve komut (command) ile sorgu (query) sorumluluklarını ayırmayı temel alır. Bu desen, özellikle karmaşık iş uygulamalarında performansı, ölçeklenebilirliği ve güvenliği artırmak için kullanılır.
CQRS'de:
CQRS'nin temel avantajları:
Apache Kafka:
RabbitMQ:
Özetle, Kafka büyük veri akışları ve olay kaynaklı mimariler için daha uygunken, RabbitMQ karmaşık yönlendirme gerektiren mesajlaşma senaryoları için daha uygundur.
Saga Pattern, dağıtık sistemlerde birden fazla servis arasında gerçekleşen işlemlerin tutarlılığını sağlamak için kullanılan bir desendir. Özellikle mikro servis mimarilerinde, ACID işlemlerinin kullanılamadığı durumlarda alternatif olarak kullanılır.
Saga Pattern iki şekilde uygulanabilir:
Saga Pattern'in avantajları:
Domain Driven Design (DDD), karmaşık iş gereksinimlerine sahip yazılım sistemlerini tasarlamak için kullanılan bir yaklaşımdır. Eric Evans tarafından geliştirilen bu yaklaşım, yazılım geliştirmeyi iş alanındaki (domain) kavramlar ve kurallar etrafında merkezileştirir.
DDD'nin temel bileşenleri:
DDD, özellikle mikro servis mimarilerinde servislerin sınırlarını belirlemek ve her servisin kendi domain modelini oluşturmak için kullanılır.
BTREE Index
Bir index yaratıldığında tipi verilmez ise default olarak btree oluşturulmaktadır. Özellikle "büyüktür", "büyük eşittir", "küçüktür", "küçük eşittir", "eşittir", "between", "is null", "is not null" gibi sorguların hepsinde kullanılabilir. Like'lı ifadeler ise "sabit değer%" şeklinde ise kullanılabilir. Balance tree algoritmasını kullanmaktadır.
Hash Index
Hash daha çok eşitlik anında kullanılabilen bir index türüdür. Oluşum hızı index yaratma süresi açısından Btree'ye göre çok daha fazladır. Ancak kapladığı alan bakımından Btree'ye göre çok daha az bir yer kaplar. Çünkü Btree ağaç yapısında tutulurken, hash flat bir yapıda tutulmaktadır.
Hash index, kullanım şekli açısından genellikle B-tree ile karşılaştırılmaktadır.
BRIN: Block range index
Postgresql verileri varsayılan olarak 8 Kb'lık bloklar halinde saklamaktadır. Brin indexlemede, indexler tutulurken bloklar içerisindeki en büyük ve en küçük değerler baz alınır. B-tree'nin aksine blok içersinde sıralanmış tüm değerler değil, sadece min ve max değerler tutulur. Eski adıyla min-max indextir.
B-tree yaratıldığında 8Kb'lık veri setlerinin tümünü saklayacak şekilde bir indexleme yapar. Ancak BRIN ındex 8Kb'lık bloklardan sadece minumum ve maximum değerleri alarak index halinde saklar.
Gin Index
Generalized Inverted Index ile her kelime için bir index ve bu indexin içinde aranan ifadenin geçtiği yerlerin listesini sıkıştırılmış olarak tutar.
GIST Index
Generalized search tree, full text search için güçlü diğer bir adaydır. Btree karşılaştırma yapıları için kullanılırken, GIST'te ağaç yapısında veri tutmasına karşın daha çok modern veritabanlarındaki geodata, text documents gibi operatorler için kullanılmaktadır.
Optimistic, yani iyimser eş zamanlılık kontrolünde aynı anda bir kaydın update edilmeyeceği varsayılır ve birden fazla session aynı kaydı update etmek için erişebilir. Eğer aynı kayıt birden fazla kişi tarafından update edilirse kayıtlardan biri iptal olur ve kullanıcıya iptal olduğuna dair bilgilendirme yapılır.
Pessimistic, yani kötümser eş zamanlılık kontrolünde bir kullanıcı bir kaydı değiştirmek istediğinde o kayda kilit koyar ve o kaydı başkası değiştiremez. İlk değiştirmeye çalışan kişi kaydı değiştirdikten sonra değiştirilen kayıt üzerindeki kilit açılır ve diğer değiştirmek isteyenler artık değiştirebilir hale gelir.
Transaction isolation, veritabanı sistemlerinde birden fazla transaction'ın aynı anda çalıştığı durumlarda, bu transaction'ların birbirlerini nasıl etkilediğini kontrol etmek için kullanılan bir mekanizmadır. Amaç, transaction'ların birbirlerini izole ederek veri tutarlılığını sağlamaktır.
ANSI/ISO SQL standardı dört farklı isolation seviyesi tanımlar:
Her izolasyon seviyesinin performans üzerinde farklı etkileri vardır. Daha yüksek izolasyon seviyeleri daha fazla tutarlılık sağlar ancak genellikle daha düşük performansla sonuçlanır.
Oracle Explain Plan, bir SQL sorgusunun nasıl çalıştırılacağını gösteren bir execution plan oluşturmak için kullanılan bir araçtır. Bu plan, Oracle optimizatörünün sorguyu nasıl çalıştıracağını, hangi index'leri kullanacağını, hangi tablolara erişeceğini ve işlem maliyetini gösterir.
Explain Plan kullanımı:
EXPLAIN PLAN FOR
SELECT * FROM employees WHERE department_id = 10;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Explain Plan çıktısı şu bilgileri içerir:
Explain Plan, sorgu performansını analiz etmek ve optimize etmek için güçlü bir araçtır. Yavaş çalışan sorguların nedenlerini anlamak ve performansı artırmak için kullanılır.
Hibernate yazılım nesnelerin, ilişkisel veri tabanlarındaki (relational databases) kayıtlara nasıl karşılık geldiğini yürüten bir teknolojidir.
Java programlama dilinde kullanılan açık kaynaklı bir nesne/ilişkisel eşleme (ORM) aracıdır. Hibernate, veri tabanı işlemlerini, Java sınıfları arasında doğrudan ilişki kurmak yerine nesnelerin saklanması ve yönetilmesi yoluyla yönetir.
FetchType: Aralarında ilişki bulunan entitylerden bir tarafı yüklerken diğer tarafın yüklenme stratejisini belirlememize olanak sağlar.
EAGER kullanırsak nesneyi veritabanından çekerken EAGER olan tüm nesneleri de beraberinde çekeriz.
LAZY kullanırsak, ihtiyaç duyduğumuzda ilgili veriler çekilecektir.
Lazy Loading, bir nesnenin ilişkili nesnelerinin ihtiyaç duyulduğunda, yani erişildiğinde yüklenmesi yöntemidir. Bu, gereksiz veri yüklemeyi önlemek ve performansı artırmak için kullanılır. Hibernate, proxy nesneleri veya bytecode enhancement kullanarak lazy loading'i gerçekleştirir.
Örneğin, bir Customer nesnesi ve bu müşteriye ait Order nesneleri arasında bir ilişki olduğunu düşünelim. Lazy Loading kullanıldığında, Customer nesnesi yüklendiğinde Order nesneleri hemen yüklenmez. Sadece Customer nesnesine erişildiğinde Order nesneleri de yüklenir.
@Entity: bir sınıfın bir veritabanı tablosuna karşılık geldiğini Hibernate'e belirtir.
@Table: sınıfın eşleştirildiği tablonun adını ve isteğe bağlı olarak schema adını belirlemek için kullanılır.
@Entity zorunludur, ancak @Table kullanımı isteğe bağlıdır; eğer kullanılmazsa, sınıf adı tablo adı olarak varsayılır.
Optimistic Kilitlenme, veri çakışmalarını önlemek için sürüm numarası veya zaman damgası kullanır. Veri güncellenmeden önce, sürüm numarası veya zaman damgasının değişip değişmediği kontrol edilir.
Pessimistic Kilitlenme ise, bir kaynağa erişim sırasında veritabanı seviyesinde kilit kullanır, böylece diğer işlemler o kaynağı değiştiremez veya okuyamaz.
Optimistic kilitlenme genellikle okuma yoğun uygulamalarda tercih edilirken, Pessimistic kilitlenme yazma yoğun işlemlerde veya yüksek çakışma riski olan durumlarda kullanılır.
Hibernate'de cascade türleri, bir nesne üzerinde yapılan işlemlerin ilişkili nesnelere nasıl uygulanacağını belirler. Ana cascade türleri: ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH.
Örneğin, bir Parent nesnesi Child nesneleri ile bir ilişki içindeyse ve Parent nesnesi kaydedildiğinde (PERSIST) veya güncellendiğinde (MERGE) Child nesnelerinin de otomatik olarak kaydedilmesi veya güncellenmesi isteniyorsa, ilgili cascade türü ilişkide belirtilir.
Hibernate Query Plan Cache, sorgu planlarını önbelleklemek için kullanılır. Bu, aynı sorgunun tekrar tekrar çalıştırılması durumunda, sorgu derleme süresini azaltarak performansı artırır. Önbellek, sorgu metni ve bağlamı (örneğin, parametre türleri) bazında sorgu planlarını saklar. Bu özellik, özellikle karmaşık sorguların ve sık çalıştırılan sorguların olduğu uygulamalarda önemli performans iyileştirmeleri sağlayabilir.
N+1 sorgu problemi, bir entity ve onun ilişkili nesnelerini yüklerken ortaya çıkan bir performans sorunudur. Örneğin, bir Parent entity'si ile ilişkili çok sayıda Child entity'sini yüklerken, ilk olarak Parent entity'si için bir sorgu çalıştırılır ve ardından her bir Child için ayrı ayrı sorgular çalıştırılır. Bu, toplamda 1 (parent için) + N (N child için) sorgu anlamına gelir ve özellikle N'nin büyük olduğu durumlarda ciddi bir performans düşüklüğüne yol açabilir.
Bu sorunu çözmek için:
Örneğin:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUserAndAccount(
User user,
Account account) {
userRepository.save(user);
if (account.getBalance() < 0) {
throw new RuntimeException("Hatalı bakiye!");
}
}
}
Transaction yayılım davranışını belirler. Varsayılan: Propagation.REQUIRED
Örneğin:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(Log log) {
logRepository.save(log);
}
Örneğin:
@Transactional(isolation = Isolation.SERIALIZABLE)
public void transferMoney(
Long fromId,
Long toId,
double amount) {
// En katı izolasyon seviyesi ile çalışır
}
Örneğin:
@Transactional(rollbackFor =
{IOException.class, SQLException.class})
public void riskyOperation() throws IOException {
// IOException veya SQLException olursa rollback olur
}
@Transactional(noRollbackFor =
IllegalArgumentException.class)
public void safeOperation() {
// IllegalArgumentException olursa rollback olmaz
}
Transaction maksimum çalışma süresini (saniye cinsinden) belirler. Süre aşılırsa rollback yapılır.
Örneğin:
@Transactional(timeout = 5)
public void longRunningTask() {
// 5 saniyeyi geçerse rollback olur
}
Sorgunun sadece okuma amaçlı olduğunu belirtir. Hibernate dirty check yapmaz → performans artar.
Örneğin:
@Transactional(readOnly = true)
public List findAllUsers() {
return userRepository.findAll();
}
ÖZET
Örneğin:
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private PaymentRepository paymentRepository;
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED)
public void placeOrder(
Order order,
Payment payment) {
orderRepository.save(order);
paymentRepository.save(payment);
if (payment.getAmount() <= 0) {
throw new RuntimeException("Ödeme hatalı!");
}
}
@Transactional(readOnly=true)
public List getAllOrders() {
return orderRepository.findAll();
}
}
JPA, Java objeleri ve ilişkisel (relational) database arasında bilgi aktarımı için kullanılan bir standarttır. JPA bu ikisi arasında bir köprü görevi görür. JPA kullanabilmek için projeye implemente edilmesi gerekir ve bu yüzden Java dilindeki Hibernate, TopLink gibi çeşitli ORM araçları bilgiler konusunda bir devamlılık sağlamak için JPA kullanır. JPA aracılığıyla farklı ORM araçları ufak birkaç ayarlama dışında yazılan kod değiştirilmeden kullanılabilir.
Evet, bir final sınıf içindeki final liste değiştirilemez. Ancak listenin içeriği (elemanları) değiştirilebilir. Çünkü final anahtar kelimesi, değişkenin referansını sabitler, ancak listenin içeriği üzerinde değişiklik yapmaya izin verir.
Yani, final bir listeye yeni elemanlar ekleyebilir, mevcut elemanları kaldırabilir veya güncelleyebilirsiniz, ancak listeyi başka bir liste ile değiştiremezsiniz.
Örneğin:
public final class FinalListExample {
private final List<String> list = new ArrayList<>();
public void addElement(String element) {
// Bu işlem geçerlidir,
// çünkü listenin içeriği değiştiriliyor
// liste referansı değil
list.add(element);
}
// Bu metod derleme hatası verir,
// çünkü final liste referansı
// değiştirilmeye çalışılıyor
public void changeList() {
// list = new ArrayList<>(); // HATA!
}
}
her yeni new ile oluşturulan class ların hash code ları farklı olduğundan dolayı Set listesine yeni bir farklı obje olarak eklenir. Örnek kod ve çıktısı aşşağıdadır.
public static void main(String[] args) {
Set<Student> set = new HashSet<Student>();
set.add(new Student(1, "cello"));
set.add(new Student(2, "mello"));
set.stream().forEach(System.out::println);
}
class Student{
private int id;
private String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
}
Output
main6.Student@7a81197d
main6.Student@5ca881b5
Ancak, Student sınıfında equals() ve hashCode() metodları doğru bir şekilde override edilirse, id'leri aynı olan nesneler aynı kabul edilir ve Set'e sadece bir kez eklenir. Bu durumda, id'leri aynı ancak name'leri farklı olan nesneler Set'te sadece bir kez yer alır.
Factory Design Pattern, bir nesne oluşturma sorumluluğunu alt sınıflara devreden bir tasarım desenidir. Bu desen, bir arayüz veya soyut sınıf aracılığıyla nesne oluşturmayı sağlar ve alt sınıflar, hangi nesnenin oluşturulacağını belirler.
Abstract Factory Design Pattern ise, ilişkili veya bağımsız nesne ailelerini oluşturmak için kullanılan bir tasarım desenidir. Bu desen, bir arayüz aracılığıyla birden fazla fabrika oluşturmayı sağlar ve her fabrika, belirli bir nesne ailesine ait nesneleri üretir.
Özetle, Factory Pattern tek bir nesne türünü oluşturmak için kullanılırken, Abstract Factory Pattern birden fazla nesne türünü ve ilişkili nesne ailelerini oluşturmak için kullanılır.