Çö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.
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.
Hocam o zaman User böyle mi olmalıdır?
Java:
public class User {   
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
    private String firstname;
    private String lastname;
    @Column(unique = true) private String username;
    private String password;
    @Column(unique = true) private String email;
    private boolean enabled;
   
   
    public User() {
        this.enabled = false;
    }
   
}

Hocam birde @Notnull annotasyonunu DTO'da kullanmıştım. Onu User'e de eklemem gerekir mi?
Java:
public class CreateUser{

    private String firstname;
    private String lastname;
    @NotNull private String username;
    @NotNull @NotBlank private String password;
    @NotNull @NotBlank private String repassword ;
   
    @NotNull @NotBlank private String email;

}
 
Hocam o zaman User böyle mi olmalıdır?
Java:
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
    private String firstname;
    private String lastname;
    @Column(unique = true) private String username;
    private String password;
    @Column(unique = true) private String email;
    private boolean enabled;
 
 
    public User() {
        this.enabled = false;
    }
 
}

Hocam birde @Notnull annotasyonunu DTO'da kullanmıştım. Onu User'e de eklemem gerekir mi?
Java:
public class CreateUser{

    private String firstname;
    private String lastname;
    @NotNull private String username;
    @NotNull @NotBlank private String password;
    @NotNull @NotBlank private String repassword ;
 
    @NotNull @NotBlank private String email;

}

User'a @NotNull eklemene gerek yok, cunku o runtime validasyon annotasyonu. CreateUser 'da kontrol edilmesi yeterli. Ayrica @NotBlank zaten null kontrolu yapiyor, ikisine ayni anda gerek yok.

User'da this.enabled=false yapilan kisim da database de yapilabilir "default false" seklinde. Ama bu haliyle de bence anlasilir. JPA zaten varsa kendi default constructor'i kullaniyor.

Su haliyle insert ederken yapilacak exception handling de eklenirse bence gayet iyi.
Buradaki "catch" blogu ayri bir fonksiyon olabilir. Cok muhtemelen kullanici kendi hesabini update ederken de ayni seyi yapman gerekecek. ( Yeni email adresi kullanimda mi vs )

Java:
try{
repo.save(user)
}catch(Exception e){
    if (e instance of ConstraintViolationException){ // farkli bir err olabilir, hatirlamiyorum tam class'i
      final String errMsg = e.getMessage();
      if(errMsg.contains("username"))    {
          throw new UsernameInUseException(user.userName);
      }else if(errMsg.contains("email")){
          // email in use
      }else{
          throw new InternalError(e);
      }
    }
}
 
Son düzenleme:
User'a @NotNull eklemene gerek yok, cunku o runtime validasyon annotasyonu. CreateUser 'da kontrol edilmesi yeterli. Ayrica @NotBlank zaten null kontrolu yapiyor, ikisine ayni anda gerek yok.

User'da this.enabled=false yapilan kisim da database de yapilabilir "default false" seklinde. Ama bu haliyle de bence anlasilir. JPA zaten varsa kendi default constructor'i kullaniyor.

Su haliyle insert ederken yapilacak exception handling de eklenirse bence gayet iyi.
Buradaki "catch" blogu ayri bir fonksiyon olabilir. Cok muhtemelen kullanici kendi hesabini update ederken de ayni seyi yapman gerekecek. ( Yeni email adresi kullanimda mi vs )

Java:
try{
repo.save(user)
}catch(Exception e){
    if (e instance of ConstraintViolationException){ // farkli bir err olabilir, hatirlamiyorum tam class'i
      final String errMsg = e.getMessage();
      if(errMsg.contains("username"))    {
          throw new UsernameInUseException(user.userName);
      }else if(errMsg.contains("email")){
          // email in use
      }else{
          throw new InternalError(e);
      }
    }
}
Hocam çok teşekkür ettim.

@bitwise hocam peki neden final anahtarı ile bir String oluşturduk?
final String errMsg = e.getMessage();
e.getMessage(); o an zaten değiştirilemez bir mesaj değil midir?

Rahatsız ettim kusura bakmayın.
 
Son düzenleme:
Hocam çok teşekkür ettim.

@bitwise hocam peki neden final anahtarı ile bir String oluşturduk?
final String errMsg = e.getMessage();
e.getMessage(); o an zaten değiştirilemez bir mesaj değil midir?

Rahatsız ettim kusura bakmayın.

O benim el aliskanligimdan dolayi aslinda, oteki turlu yapsak da olurdu.

Zaten simdi bakinca catch blogunu "Exception e" yerine direkt yakalamak istedigimiz Exception ile olustursak daha mantikli olurmus diye de dusundum.
 
O benim el aliskanligimdan dolayi aslinda, oteki turlu yapsak da olurdu.

Zaten simdi bakinca catch blogunu "Exception e" yerine direkt yakalamak istedigimiz Exception ile olustursak daha mantikli olurmus diye de dusundum.
Hocam çok teşekkür ettim. Sayenizde daha güzel hazırladım.

Zaten simdi bakinca catch blogunu "Exception e" yerine direkt yakalamak istedigimiz Exception ile olustursak daha mantikli olurmus diye de dusundum.
Yok aslında bu türlü daha iyi. Başka hatalar ile kıyaslayıp, başka hataları da yakalamak için kullanılır yani.
 
Hocam çok teşekkür ettim. Sayenizde daha güzel hazırladım.


Yok aslında bu türlü daha iyi. Başka hatalar ile kıyaslayıp, başka hataları da yakalamak için kullanılır yani.

Rica ederim. Bu arada ben "ConstraintViolationError" hatasini uydurdum, gercekte atilan exception nedir gelistirme bitince bana yazabilir misin? Onu da merak ediyorum.
 

Technopat Haberler

Yeni konular

Geri
Yukarı