Çözüldü Java Optional ile Modeli ne zaman kullanmak gerekir?

Bu konu çözüldü olarak işaretlenmiştir. Çözülmediğini düşünüyorsanız konuyu rapor edebilirsiniz.

706111

Hectopat
Katılım
28 Ağustos 2023
Mesajlar
6.020
Makaleler
1
Çözümler
29
Arkadaşlar merhaba.

Diyelimki AccountRepository'si var. Altına da sorguları ekliyoruz böyle. Belki hata verir böyle aynı isimde yazarsak, ona bir şey diyemem şimdilik. (Yani alttaki gibi repostiroy'e tanımlarsak, Spring ya da JPA özel sorgulara dönüştürüyor);

Java:
public interface AccountRepository extends JpaRepository<Account, Long>{
    Optional<Account> findByEmail(String email);
    Account findByEmail(String email);
}

Bunların hangisini hangi amaç ile ve ne zaman kullanmalıyız? İkisi de sonunda bir Account döndürebiliyor.
 
Çözüm
Aslinda genel olarak ne zaman Optional kullanmaliyiz ile ayni soru bu. JPA sadece bu yapiya uyum sagliyor.

Eger kodun geri kalaninda fluent fonksiyonel programlama yapiliyorsa Optional kullanmak yegdir.

Yine interface'e bakan kisi, bu alanin "null" olabilecegini tek okuyusta anlayabilmis olur. Kotlinde oldugu gibi nullable olmayan tipler Java'da @NonNull gibi annotasyonlar disinda tanimlanamadigi icin Optional olmayan return parametresinin null olmadigini varsayarim ben. Aksi halde kodun her tarafi " x != null" kontrolleri ile dolu olur.

Optional kullanarak interface i implemente eden ve kullanan insanlara bu alanin null olabilecegini ve null durumu handle etmelerini zorunlu kilmis olursun bu da kod kalitesini arttirir.

Ancak Optional da silver bullet degil; != yerine ifPresent() kullanarak semantik olarak ayni seyi farkli sekilde yapmis oluyorsun. Sadece daha okunabilir olmus oluyor. Ayrica ufak da olsa bir performans farki da var, surekli wrapper yaptigin icin. Son aklima gelen dezavantaj da serialization. Optional<Type> serialize etmek icin biraz daha ugrasman gerekiyor direkt Type serializasyonuna kiyasla.

Isminde "findBy" gectigi icin Optional<Account> olmali. Bunu yaparak bu fonksiyonu cagiran her yerde Account bulunamama durumunu handle etmeye zorlamis oluyorsun ki olmasi gereken bu. Cagiran yer de isterse bunu kendisi handle eder isterse o da kendisini cagiran yere delege eder.

Direkt "Account findByEmail()" seklinde yaparsan cagiran yerde null-safety zorlamasi olmaz. Bazen null bazen null olmayan tipler genel olarak proje buyudukce basa bela olurlar.
Aslinda genel olarak ne zaman Optional kullanmaliyiz ile ayni soru bu. JPA sadece bu yapiya uyum sagliyor.

Eger kodun geri kalaninda fluent fonksiyonel programlama yapiliyorsa Optional kullanmak yegdir.

Yine interface'e bakan kisi, bu alanin "null" olabilecegini tek okuyusta anlayabilmis olur. Kotlinde oldugu gibi nullable olmayan tipler Java'da @NonNull gibi annotasyonlar disinda tanimlanamadigi icin Optional olmayan return parametresinin null olmadigini varsayarim ben. Aksi halde kodun her tarafi " x != null" kontrolleri ile dolu olur.

Optional kullanarak interface i implemente eden ve kullanan insanlara bu alanin null olabilecegini ve null durumu handle etmelerini zorunlu kilmis olursun bu da kod kalitesini arttirir.

Ancak Optional da silver bullet degil; != yerine ifPresent() kullanarak semantik olarak ayni seyi farkli sekilde yapmis oluyorsun. Sadece daha okunabilir olmus oluyor. Ayrica ufak da olsa bir performans farki da var, surekli wrapper yaptigin icin. Son aklima gelen dezavantaj da serialization. Optional<Type> serialize etmek icin biraz daha ugrasman gerekiyor direkt Type serializasyonuna kiyasla.

Isminde "findBy" gectigi icin Optional<Account> olmali. Bunu yaparak bu fonksiyonu cagiran her yerde Account bulunamama durumunu handle etmeye zorlamis oluyorsun ki olmasi gereken bu. Cagiran yer de isterse bunu kendisi handle eder isterse o da kendisini cagiran yere delege eder.

Direkt "Account findByEmail()" seklinde yaparsan cagiran yerde null-safety zorlamasi olmaz. Bazen null bazen null olmayan tipler genel olarak proje buyudukce basa bela olurlar.
 
Çözüm
Birde hocam Optional ile yaptığımız basit exceptionları, orElseThrow ile fırlatmamıza izin veriyor. Tıpkı findByID gibi JPA Repository'de var olan fonksiyon gibi.
 
Birde hocam Optional ile yaptığımız basit exceptionları, orElseThrow ile fırlatmamıza izin veriyor. Tıpkı findByID gibi JPA Repository'de var olan fonksiyon gibi.

Evet.

Ya da birden fazla Optional<Type> fonksiyonunu fall back yapabilirsin. Mesela findByEmail() ile bulunamazsa findByName() ile aransin gibi.

findByEmail().or(()->findByName()) gibi. Closure kullanip bunlari sadece gerektigi zaman execute edebilir hale getirebilirsin. flatMap() ya da stream() API'lari ile birlestirebilirsin. Optional<> kullanmanin fonksiyonel faydasi cok.

Ama neticede bir fonksiyonun null dondugu zaman ne yapman gerektigini farkli sekilde yapmis oluyorsun, bir sekilde yaptigini otekiyle de yaparsin. Sahsen ben Optional<> yerine Kotlin'deki Type? yapisini tercih ederim. Neyin nullable, neyin NotNull oldugu compile edilirken belli olmali.
 
Hocam nullabe ve notnull, DTO için kullanılmıyor mu? Mesela dışarıdan veri alacağız. Bunun için DTO modeli oluşturduk. Birkaç değişkenini @Notnull diye işaretliyoruz Java'da.
 
Hocam nullabe ve notnull, DTO için kullanılmıyor mu? Mesela dışarıdan veri alacağız. Bunun için DTO modeli oluşturduk. Birkaç değişkenini @Notnull diye işaretliyoruz Java'da.

Javax @NotNull annotasyonunu kastediyorsan evet. Sadece DTO icin degil fonksiyonlar icin de kullanilabilir.


Ancak burada runtime vs compile time ayrimini iyi anlamak gerek. Sen bir parametreyi ya da field'i @NotNull olarak isaretleyebilirsin fakat bunu process eden birileri olmazsa onlar null olabilirler. Runtime sirasinda data geldiginde eger isaretli alan null ise hata veren bir mekanizmaya ihtiyacin var. Spring bunu yapiyor, Hibernate de yapiyor. Baska yapan reflection kutuphaneleri de var.

Konuda onceden tartistigimiz Optional<> muhabbeti compile time bir olay.

Yine IDEA'nin kendi @NotNull annotasyonu da var. Compile zamanda hata alirsin eger dogru sekilde ayarladiysan build konfigurasyonunu.
 
Teşekkür ettim hocam. Compile time ile runtime konularını da iyice öğreneyim.
 
Teşekkür ettim hocam. Compile time ile runtime konularını da iyice öğreneyim.

Rica ederim. Cok bir numarasi yok; birisi kodunu compile ettigin zaman, digeri programin calisirken.

Eger bir hata alinacaksa bunu en hizli ve en erken sekilde almak daha iyidir. Kod calisirken NullPointerException yemektense compile sirasinda "Haci sen null olmayan alana null assign etmeye calisiyorsun" diye hata alman cok fazla zaman ve kalite kazandirir.

Fail-Fast felsefesi. Fail fast (business) - Wikipedia
 
Email ve username'e sahip olan hesapların kontrolünü yapıyordum;
Java:
    @Override
    public UserDTO registerNewUser(CreateUser user) throws EmailAlReady, UserAlReady{
    
        Optional<User> exitingUserByMail = userRepository.findByEmail(user.getEmail());
        if(exitingUserByMail.isPresent()) {
            throw new EmailAlReady();
        }
            
        Optional<User> exitingUserByUsername = userRepository.findByUsername(user.getUsername());
        if(exitingUserByUsername.isPresent()) {
            throw new UserAlReady();
        }

...
...

    }

Ancak bu kadar oldu.
 

Biraz code review gibi olacak ama bence soyle olsa daha dogru:

1. DB tanimlarken email ve username alanlarini "unique" index ve muhtemelen not nullable yaparsan
2. Email ve username kullaniliyor mu diye kontrol etmeden direkt insert edersen
3. Kullanilmasi durumunda gelen exception'i handle ederken "Constraint hede hodo failed" olmasindan bunu anlarsin. Boylece her basarili kayit icin 3 yerine 1 defa DB ye istek atmis olursun. Birak o validasyonu RDBMS yapsin senin icin insert ederken.

Ayrica JPA da "existsByX" template'i var. Butun User alanlarini doldurmasindansa sadece bununla eslesen kayit var mi yok mu bana getir seklinde kullanabilirsin. Optional kullanmana da gerek kalmaz, ya true ya da false donecek zira.
 
Bu siteyi kullanmak için çerezler gereklidir. Siteyi kullanmaya devam etmek için çerezleri kabul etmelisiniz. Daha Fazlasını Öğren.…