Spring Boot & JSPs in Executable JARs

Nowadays almost everyone employ their favorite JS based framework while developing their dynamic web applications. There are dozens of client and server side UI frameworks around there, and you can be sure that you are going to be criticized about your choice it doesn’t matter which one you chose. Some will ask why it is not Angular, and some other will ask why it is Angular? We also employ Vaadin UI Framework in our projects as well. However, I still prefer not to employ any one of those frameworks during the trainings I give over java-egitimleri.com. My choice is still for JSP in order to teach fundamentals of web application development to my audience. I have some reasons for this;

  • It is a lot easier to demonstrate building blocks of Web MVC architecture, explain how front controller pattern works
  • You don’t need any third party dependency or any pre-configuration to make them running
  • They can be modified easily at runtime, without requiring any recompilation, or server restart

My aim is not to deal with various UI gadgetry, and instead focus more on backend process of developing enterprise web applications during my trainings.

Trainings mostly contain Spring stuff, and Spring Boot is the framework/platform to melt down all those Spring, JPA/Hibernate and various middleware related theory into an executable and demonstrable artifact easily. However, Spring Boot doesn’t play nicely with JSPs in executable JAR format. They ask you either to switch to executable WAR format, and not use Undertow as web container, or employ another templating engine, such as Thymeleaf or FreeMarker.

Anyway, I was ok with switching into WAR packaging, createing src/main/webapp folder to place all those JSPs, and their static resources underneath, and keep up with embedded Tomcat. However, a few days ago, I questioned myself again, if there might be an alternative way to keep all those JSP and related stuff under src/main/resources, and came up with a little but invaluable information around the web. According to JSR-245 and Servlet 3.0 API, it is possible to place static resources within /META-INF/resources folder of JAR files. This means that, we can move our JSPs and their related static content into /src/main/resources/META-INF/resources folder in our Spring Boot projects!

I tried it immediately, and it worked without any problem inside my STS IDE. Afterwards, I decided to create an executable JAR and check if everything still works there as well. Unfortunately it’s not!

When I extracted executable JAR file, I noticed that Spring Boot Maven Plugin repackaged JAR content by moving everything under /BOOT-INF/classes folder including /META-INF/resources. Unfortunately, Spring Boot Maven Plugin has no way to specify excludes during repackaging.

The only viable solution is to create another simple maven project containing your JSPs and their related static content, under src/main/resources/META-INF/resources, and make it a dependency to your Spring Boot project. This might look a bit weird to some of you but this looks the only viable workaround to this repackage issue with the plugin, as the guys responsible with developing Spring Mave Plugin respond with suggesting moving into Gradle in case you are blocked with Maven ­čÖé


Ayr─▒ Bir Repository Katman─▒ ┼×art m─▒? (2)

Daha ├Ânceki bir yaz─▒mda ayr─▒ bir repository katman─▒ ┼čart m─▒? diye sormu┼č ve ORM teknolojilerinin ve JPA’n─▒n pop├╝lerle┼čmesi ile CRUD tabanl─▒ bir tak─▒m uygulamalarda yaz─▒l─▒m geli┼čtiricilerin ayr─▒ca bir DAO/Repository aray├╝z├╝ olu┼čturman─▒n ├žok da fazla i┼člevi olmad─▒─č─▒n─▒ savunarak, do─črudan JPA EntityManager ├╝zerinden servis katman─▒nda veri eri┼čim i┼člemleri ger├žekle┼čtirmeye y├Âneldiklerinden bahsetmi┼čtim. Bu yaz─▒mda bu t├╝r bir prati─čin muhtemel dezavantajlar─▒ndan bahsetmeye ├žal─▒┼čaca─č─▒m.

Bu t├╝r bir prati─čin en temel zaaf─▒, uygulaman─▒n veriyi saklama ve veriye eri┼čim i├žin ili┼čkisel veritaban─▒ y├Ântemine do─črudan ba─č─▒ml─▒ hale gelmesine neden olmas─▒d─▒r diyebiliriz. Her ne kadar JPA API’de bize bir aray├╝z ├╝zerinden ├žal─▒┼čmay─▒ sa─člasa da, JPA API’nin i┼člevi uygulamay─▒ ORM ├╝reticilerinin ger├žekle┼čtirimlerinden izole k─▒lmaya ├žal─▒┼čmakt─▒r. Oysa ki DAO/Repository aray├╝zleri arkas─▒na gizlenmi┼č ayr─▒ bir veri eri┼čim katman─▒ uygulama i├žin bug├╝n ili┼čkisel yar─▒n ise tamamen farkl─▒, ├Ârne─čin graph temelli, ba┼čka bir veri eri┼čim ve saklama y├Ântemi ile ├žal─▒┼čmay─▒ kolayla┼čt─▒ran bir fayda sa─člamaktad─▒r. Aksi durumda ise servis katman─▒ do─črudan spesifik bir teknolojiye (JPA/Hibernate) ba─č─▒ml─▒ k─▒l─▒nmaktad─▒r. Hatta OpenEntityManagerInViewFilter/OpenSessionInViewFilter gibi baz─▒ ORM ├ž├Âz├╝mlerinin spesifik problemleri i├žin geli┼čtirilen ├ž├Âz├╝mler bu ba─č─▒ml─▒l─▒─č─▒n servis katman─▒ d─▒┼č─▒nda controller ve presentation katmanlar─▒na dahi s─▒zmas─▒na neden olabilmektedir.

Servis katman─▒nda do─črudan JPA API ile ├žal─▒┼čman─▒n bir di─čer zaaf─▒ ise birim testlerinde ortaya ├ž─▒kmaktad─▒r. Ayr─▒ bir DAO/Repository aray├╝z├╝ ile ├žal─▒┼č─▒rken daha ├╝st bir d├╝zeyde ve tek bir metot ├ža─čr─▒s─▒ ile encapsule edilebilecek bir davran─▒┼č, do─črudan JPA API ile ├žal─▒┼č─▒ld─▒─č─▒ vakit EntityManager, TypedQuery, Query gibi birka├ž ayr─▒ yap─▒ ve bunlarla ilgili genellikle say─▒ca tek bir metot ├ža─čr─▒s─▒ndan daha fazla say─▒da metot ├ža─čr─▒s─▒ ile ifade edilmi┼č oldu─čundan, birim testleri i├žerisinde de servis katman─▒n─▒n bu yap─▒larla etkile┼čiminin mocklanmas─▒ ve ilgili senaryonun testi s─▒ras─▒nda bu mock nesnelerin sergilemesi istenen davran─▒┼člar─▒n bu mock nesnelere ├Â─čretilmesi birim testlerin ├Âzellikle e─čitim b├Âl├╝mlerinin ├žok daha “verbose” bir hale d├Ân├╝┼čmesine neden olmaktad─▒r. Birim testlerindeki bu kalabal─▒kl─▒k ve alt d├╝zeyde daha fazla yap─▒ ile etkile┼čimin s├Âz konusu olmas─▒ da teste tabi tutulan s─▒n─▒f ve davran─▒┼č ├╝zerinde meydana gelen de─či┼čikliklerle birlikte testin de ├žok daha kolay bir bi├žimde k─▒r─▒lgan hale gelmesine kap─▒ aralamaktad─▒r.

├ľzetle s├Âylemek gerekirse ayr─▒ bir DAO/Repository katman─▒ olu┼čturmaktan ka├ž─▒narak kazan─▒lan zaman ve i┼č g├╝c├╝ projenin biraz ilerleyen safhalar─▒nda, farkl─▒ gereksinimlerin ortaya ├ž─▒kmas─▒, davran─▒┼člar─▒n ├že┼čitlenmesi ve de─či┼čmesi ile avantaj─▒n─▒ ├žok ├žabuk bi├žimde yitirebilmektedir.

Bu noktada akla Repository katman─▒n─▒ manuel olu┼čturmak yerine dinamik olarak ├╝retmeyi sa─člayan Spring Data gibi ├ž├Âz├╝mler kullanmak da gelebilir. Hatta bu ├ž├Âz├╝mlerin sundu─ču kabiliyetlerden etkilenerek, CRUD temelli uygulamalarda bu sefer de servis katman─▒n─▒ tamamen tasviye ederek, Controller katman─▒ndan do─črudan Repository katman─▒ ile eri┼čerek ├žal─▒┼čmak da tercih edilebilmektedir. Gelin bir sonraki yaz─▒m─▒zda da bu konu ├╝zerinde dural─▒m.

─░lgin├ž Bir Transaction Propagation Hikayesi 3

─░lgin├ž Bir Transaction Propagation Hikayesi┬ába┼čl─▒kl─▒ yaz─▒ dizisinin ikinci b├Âl├╝m├╝nde Foo, Bar ve Baz entity’lerinden hi├žbirinin mevcut durumda DB’ye insert edilemedi─čini s├Âylemi┼čtim. Ancak problemde hedeflenen Bar entity’sini persist eden ifadeyi try/catch blo─čuna alarak, m├╝mk├╝nse Baz ve Foo entity’lerinin Bar’─▒n insert i┼čleminden etkilenmeden DB’ye insert edilmesidir.

Bar entity’si ile ilgili, DB’den gelen constraint violation hatas─▒ JPA PersistenceContext’in PersistenceException f─▒rlatmas─▒na neden olmaktad─▒r. JPA’da f─▒rlat─▒lan PersistenceException’lar, birka├ž─▒ hari├ž (NoResultException, NonUniqueResultException, LockTimeoutException, ve QueryTimeoutException) aktif transaction’─▒n rollback’e set edilmesine neden olurlar. Dolay─▒s─▒ ile Baz entity’si de Bar ile ayn─▒ transaction’da persist edildi─či i├žin onun insert i┼člemi de Bar’la birlikte ba┼čar─▒s─▒z olmaktad─▒r. Ba┼čka bir ifade ile Baz, Bar’dan ba─č─▒ms─▒z DB’ye insert edilememektedir.

Ancak Foo entity’si i├žin durum b├Âyle olmaz zorunda de─čildir. Foo entity’sinin insert edildi─či transaction ile Bar ve Baz entity’lerinin insert edildikleri transaction birbirlerinden tamamen ba─č─▒ms─▒zd─▒rlar. Spring’in transaction y├Ânetim altyap─▒s─▒ bir JPA transaction’─▒ aktif iken, yeni bir JPA transaction’─▒ ba┼člatmak i├žin mevcut transaction’─▒ suspend ederek yeni transaction’─▒ ba┼člat─▒r. Yeni bir JPA transaction’─▒ ba┼člatmak demek eskisinden ba─č─▒ms─▒z yeni bir PersistenceContext (yani EntityManager) olu┼čturmak demektir.

Dolay─▒s─▒ ile ikinci PersistenceContext’de yap─▒lan i┼člemler ve burada meydana gelen hata ilk PersistenceContext’i hi├žbir ┼čekilde etkilemeyecektir. E─čer ikinci servis metodundan (transaction commit/rollback yapan middleware k─▒sm─▒ dahil) f─▒rlat─▒labilecek olas─▒ b├╝t├╝n exception’lar─▒ yakalayan bir try/catch blo─čunu foo() metodu i├žerisinde barService.bar() metodunu ├ža─č─▒rd─▒─č─▒m─▒z yere koyarsak, bar() metodu sonlan─▒rken ortaya ├ž─▒kabilecek her t├╝rl├╝ exception foo() metodu i├žerisinde yakalanacakt─▒r.

@Transactional(propagation=Propagation.REQUIRED)
public void foo() {
    em.persist(new Foo());
    try {
       barService.bar();
    } catch(Exception ex) {
        //ignore ex...
    }
}

B├Âylece foo() metodu ba┼čar─▒l─▒ bi├žimde sonlanacak ve transaction’da commit edilebilecektir. B├Âylece Foo entity’si de DB’ye insert edilmi┼č olacakt─▒r. Bu noktada BarService.bar() metodu i├žerisinde Bar entity’sinin persist edilmesi i┼člemini try/catch blo─ču i├žerisine almak gereksizdir diyebiliriz.

Spring ile ├žal─▒┼č─▒rken BarService.bar() metodunun i├žerisinde┬á unexpected rollback exception tetiklemeden sessiz bi├žimde transaction’─▒ rollback ettirmek de m├╝mk├╝nd├╝r.

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void bar() {
    em.persist(new Baz());
    try {
        em.persist(new Bar());
        em.flush();
    } catch(Exception ex) {
        //ignore ex...
        TransactionAspectSupport.currentTransactionStatus()
                 .setRollbackOnly();
    }
}

Bunun i├žin ├Âncelikle Bar entity’si EntityManager.persist() metodu ile persistent hale getirildikten sonra PersistenceContext ├╝zerinde hemen bir flush tetikleyerek insert SQL’inin DB’ye gitmesi sa─član─▒r. Bu a┼čamada constraint violation hatas─▒ nedeni ile PersistenceException meydana gelecektir, ama PersistenceException yakalan─▒p ignore edilmektedir. Ancak PersistenceContext’in art─▒k bar() metodu ba┼čar─▒l─▒ bi├žimde sonlansa bile Spring’in transaction y├Ânetim altyap─▒s─▒na transaction commit yapamayaca─č─▒, sadece rollback yapabilece─či belirtilmelidir. Bu da mevcut transaction’a kar┼č─▒l─▒k gelen TransactionStatus nesnesine eri┼čip bunun setRollbackOnly() metodunu ├ža─č─▒rarak olabilir. Art─▒k Spring, yakalanan exception ignore edilip bar() metodu ba┼čar─▒l─▒ sonlansa bile transaction’─▒ commit yapmayacak, dolay─▒s─▒ ile transaction y├Ânetim altyap─▒s─▒ da unexpected rollback exception ile kar┼č─▒la┼č─▒lmayacakt─▒r.

Spring Boot ve JSF

Java Server Faces (JSF)’i ba┼čar─▒s─▒z bir UI framework olarak niteledi─čimi de─či┼čik ortamlarda ve yaz─▒lar─▒mda belirtmi┼čimdir. Ancak ne yapal─▒m ki JSF s─▒k├ža kullan─▒lan bir UI framework olarak kar┼č─▒m─▒za ├ž─▒k─▒yor. Spring ekosisteminde JSF’i Spring ├ž├Âz├╝mleri ile entegre etmeye ├žal─▒┼čan arkada┼člardan zaman zaman sorular al─▒yorum.

Spring Boot ile geli┼čtirilen web uygulamalar─▒nda UI framework olarak JSF’i kullanmak m├╝mk├╝nd├╝r. Spring Boot ile ilgili verdi─čimiz kurumsal e─čitimimizde opsiyonel olarak Spring Boot JSF entegrasyonuna da de─činiyoruz. Bu yaz─▒mda da k─▒saca Spring Boot uygulamas─▒ i├žerisinde JSF kullanabilmek i├žin ne t├╝r ayarlar yapmak gerekti─činden bahsedece─čim.

Spring Boot uygulamas─▒ i├žerisinde JSF ile ├žal─▒┼čabilmek i├žin a┼ča─č─▒daki noktalar ├╝zerinde ayarlamalar ve custom kod yazma ihtiyac─▒ s├Âz konusudur. Bu ayarlar─▒ tespit etti─čim Spring Boot s├╝r├╝m├╝n├╝n 2.0.x,┬á JSF ref impl s├╝r├╝m├╝n├╝n ise 2.2.14 oldu─čunu da belirtmeliyim. Zaman i├žerisinde hem Spring Boot’un hem de JSF’in s├╝r├╝mlerinden kaynaklanan farkl─▒l─▒klar da s├Âz konusu olabilir. Ancak yine de a┼ča─č─▒daki ad─▒mlar genel olarak Spring Boot – JSF entegrasyonu i├žin temel te┼čkil edecektir.

  1. JSF ba─č─▒ml─▒l─▒klar─▒n─▒n pom.xml’e eklenmesi
  2. FacesServlet’in tan─▒mlanmas─▒
  3. ConfigureListener’─▒n tan─▒mlanmas─▒
  4. Spring Boot projesinin executable war’a d├Ân├╝┼čt├╝r├╝lmesi
  5. faces-config.xml i├žerisinde JSF EL Resolver tan─▒m─▒ ile managed bean ve managed property lookup’lar─▒ i├žin Spring Container’a bak─▒lmas─▒n─▒n sa─članmas─▒
  6. Ayr─▒ca JSF Backing Bean’lar─▒ Spring Managed yapacaksan─▒z custom ViewScope konfig├╝rasyonu da yap─▒lmal─▒d─▒r

├ľncelikle JSF ba─č─▒ml─▒l─▒klar─▒n─▒ pom.xml’e ekleyerek ba┼člayal─▒m.

<dependency>
    <groupId>com.sun.faces</groupId>
    <artifactId>jsf-api</artifactId>
    <version>2.2.14</version>
</dependency>
<dependency>
    <groupId>com.sun.faces</groupId>
    <artifactId>jsf-impl</artifactId>
    <version>2.2.14</version>
</dependency>

Ayr─▒ca JSF Container ├žal─▒┼čma zaman─▒nda JSP derleyicisine de ihtiya├ž duydu─ču i├žin pom.xml’e Tomcat’in JSP compiler’─▒n─▒ da eklemeliyiz.

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>

─░kinci ad─▒mda Spring Boot konfig├╝rasyon s─▒n─▒f─▒m─▒z i├žerisinde Java tabanl─▒ olarak FacesServlet’in tan─▒m─▒n─▒ yapal─▒m ve uzant─▒s─▒ *.xhtml olan web isteklerinde devreye girmesini sa─člayal─▒m.

@Configuration
public class SpringAppConfig implements ServletContextAware {
    @Bean
    public ServletRegistrationBean facesServletRegistration() {
        ServletRegistrationBean registration = 
            new ServletRegistrationBean(new FacesServlet(), "*.xhtml");
        registration.setLoadOnStartup(1);
        return registration;
    }

    ...
}

┼×imdi de benzer bi├žimde JSF referans implementation’a ait olan ConfigureListener’─▒n java tabanl─▒ konfig├╝rasyon ile devreye girmesini sa─člayal─▒m. ConfigureListener JSF ile ilgili konfig├╝rasyon bilgilerinin y├╝klenmesini ve ├žal─▒┼čma ortam─▒n─▒n aya─ča kald─▒r─▒lmas─▒n─▒ sa─člamaktad─▒r.

@Configuration
public class SpringAppConfig implements ServletContextAware {

    @Bean
    public ServletListenerRegistrationBean<ConfigureListener>     
                                               jsfConfigureListener() {
        return new ServletListenerRegistrationBean<ConfigureListener>(
                    new ConfigureListener());
    }

    ...
}

JSF konfig├╝rasyonunun JBoss, Tomcat gibi web container’lar i├žerisinde sa─čl─▒kl─▒ bi├žimde y├╝klenebilmesi i├žin a┼ča─č─▒daki servlet context parametre tan─▒m─▒n─▒n da yap─▒lmas─▒ gerekmektedir.

@Configuration
public class SpringAppConfig implements ServletContextAware {
    @Override
    public void setServletContext(ServletContext servletContext) {
        servletContext.setInitParameter(
            "com.sun.faces.forceLoadConfiguration", 
            Boolean.TRUE.toString());
    }

    ...
}

Spring Boot projelerinde JSF sayfalar─▒ sadece executable war dosyalar─▒ i├žerisinden eri┼čilebilmektedir. Dolay─▒s─▒ ile Spring Boot projesinin tipini executable war yapmam─▒z gerekmektedir. Bunun i├žin pom.xml’deki packaging eleman─▒n─▒n de─čerini a┼ča─č─▒daki gibi war yapmam─▒z yeterli olacakt─▒r.

<packaging>war</packaging>

Spring Boot projesinin executable war yap─▒lmas─▒na paralel olarak JSF sayfalar─▒n─▒ da src/main/webapp dizini alt─▒nda bir lokasyona koymam─▒z gerekmektedir.

Son olarak da faces-config.xml i├žerisinde JSF ELResolver olarak SpringBeanFacesELResolver ger├žekle┼čtirimini tan─▒mlayarak JSF managed bean ve managed property lookup’lar─▒ i├žin Spring ApplicationContext’e de bak─▒lmas─▒n─▒ sa─člayal─▒m.

<faces-config ...>
    <application>
        <el-resolver>
            org.springframework.web.jsf.el.SpringBeanFacesELResolver
        </el-resolver>
    </application>
</faces-config>

Bu a┼čamada Spring Boot projeniz i├žerisinde JSF ile ├žal─▒┼čmaya ba┼člayabilirsiniz. E─čer view scope JSF bean’lerini de Spring ApplicationContext i├žerisinde y├Ânetmek isterseniz bu durumda son ad─▒mda belirtti─čim custom view scope konfig├╝rasyonunu da yapman─▒z gerekecektir. Daha ├Ânceki yaz─▒lar─▒mdan birisinde JSF i├žin sa─čl─▒kl─▒ ├žal─▒┼čan custom view scope implemantasyonunu ve nas─▒l tan─▒mlanaca─č─▒n─▒ Harezmi’deki bir blog yaz─▒s─▒nda anlatm─▒┼čt─▒m.

JSF ile m├╝cadelenizde ba┼čar─▒lar… ­čśë

 

Ayr─▒ Bir Repository Katman─▒ ┼×art m─▒?

Kurumsal yaz─▒l─▒m sistemlerinde ├╝├ž katmanl─▒ mimari yakla┼č─▒m─▒ uygulamak “de facto” olmu┼čtur. Bu t├╝r uygulamalarda sunum (presentation), servis (service/business) ve depo (DAO/repository) ayr─▒ ayr─▒ g├Ârevlere sahip katmanlar olarak kar┼č─▒m─▒za ├ž─▒karlar.

Sunum katman─▒nda UI ile ilgili i┼člemler ger├žekle┼čtirilir. Bu katmanda kendi i├žinde aray├╝z (UI) ve d├Ân├╝┼č├╝m/kontrol (controller) ┼čeklinde alt katmanlara ayr─▒labilir. Dolay─▒s─▒ ile JavaEE d├╝nyas─▒nda ├╝├ž katmanl─▒ (3 tiered) mimariler ayn─▒ zamanda N katmanl─▒ (N tiered) olarak da adland─▒r─▒lmaktad─▒r. Sunum kataman─▒na gelen kullan─▒c─▒ istekleri gerekli d├Ân├╝┼č├╝m ve kontrollerin ard─▒ndan servis katman─▒na iletilir. Servis katman─▒ndan d├Ânen cevaplarda yine gerekli d├Ân├╝┼č├╝mlere tabi tutularak kullan─▒c─▒ya sunulur.

Servis katman─▒nda uygulaman─▒n i┼č mant─▒─č─▒ ile ilgili i┼člemler ve s├╝re├žler y├╝r├╝t├╝l├╝r. Ayr─▒ca servis katman─▒ kar┼č─▒m─▒za transaction y├Ânetimi, g├╝venlik kontrolleri, validasyon gibi middleware operasyonlar─▒n y├Ânetim yeri olarak da ├ž─▒kmaktad─▒r.

Depo (DAO/Repository) katman─▒nda ise veri eri┼čim i┼člemleri ger├žekle┼čtirilir. Servis katman─▒ i┼č mant─▒─č─▒n─▒ y├╝r├╝t├╝rken veri eri┼čimi, verinin saklanmas─▒ (persist edilmesi) ile ilgili ihtiya├žlar─▒n─▒ kar┼č─▒lamak i├žin de depo katman─▒na ba┼čvurmaktad─▒r.

Katmanl─▒ mimarinin en b├╝y├╝k avantaj─▒ soyutlama (encapsulation) ve mod├╝lerliktir. Her bir katman sadece bir alt─▒ndaki katman─▒ bilir ve sadece onunla ileti┼čimde olur. B├Âylece herhangi bir katmanda yap─▒lan de─či┼čikliklerin sistem i├žerisindeki etkisi s─▒n─▒rl─▒ bir alanda kalm─▒┼č olacakt─▒r. Bunun yan─▒ s─▒ra herhangi bir katmanda kullan─▒lan spesifik bir teknoloji, k├╝t├╝phane, framework veya veritaban─▒ o katman arkas─▒nda gizlenebilir ve gerekti─činde sistemin geri kalan─▒n─▒ etkilemeden uygun di─čer bir alternatifi ile de─či┼čtirilebilir.

ORM teknolojilerinin geli┼čmesi ve JPA’n─▒n ortaya ├ž─▒kmas─▒ ile birlikte ayr─▒ bir depo katman─▒n─▒n gerekli olup olmad─▒─č─▒ tart─▒┼č─▒l─▒r hale gelmi┼čtir. Uygulama i├žerisinde veri eri┼čim i┼člemleri i├žin JPA’y─▒ kullananlar i├žin ORM provider’─▒n halihaz─▒rda veritaban─▒ ba─č─▒ms─▒zl─▒─č─▒ sa─člad─▒─č─▒ ve SQL leh├že farkl─▒l─▒klar─▒ndan uygulaman─▒n geri kalan─▒n─▒ izole etti─či belirtilerek ayr─▒ bir repository aray├╝z├╝ ve implemantasyon s─▒n─▒f─▒ olu┼čturman─▒n fazlal─▒k oldu─ču iddia edilmektedir. Bu yakla┼č─▒m─▒ savunanlar do─črudan PersistenceContext yani EntityManager nesnesini servis katman─▒na enjekte ederek de ├žal─▒┼č─▒labilece─čini ifade ederler. Bu g├Âr├╝┼č├╝ savunanlara g├Âre EntityManager ├╝zerindeki operasyonlar─▒ ayr─▒ bir depo aray├╝z├╝ ve implemantasyonu ile gizlemenin ├žok da fazla bir getirisi yoktur.

Ger├žekten de entity s─▒n─▒flar─▒ i├žin olu┼čturulan CRUD aray├╝z ve s─▒n─▒flar─▒na bakt─▒─č─▒m─▒zda, tan─▒ml─▒ metotlar─▒n asl─▒nda JPA PersistenceContext API’sinde sunulan metotlardan ├žok da farkl─▒ olmad─▒─č─▒n─▒ s├Âyleyebiliriz. ├ľrne─čin, User entity s─▒n─▒f─▒ i├žin olu┼čturulan CRUD depo aray├╝z├╝nde genellikle a┼ča─č─▒daki ├Ârne─če benzer metotlar─▒n yer ald─▒─č─▒n─▒ g├Âr├╝r├╝z.

public interface UserRepository {    
    List<User> findAll();
    User findById(Long id);
    void create(User user);
    void update(User user);
    void delete(Long id);
}

Farkl─▒ entity’ler i├žin benzer depo aray├╝zlerini ve s─▒n─▒flar─▒ implement ettiklerini g├Ârenlerin projelerinde bu entity spesifik depo s─▒n─▒flar─▒n─▒ generic bir depo s─▒n─▒f─▒na d├Ân├╝┼čt├╝rmeleri de ├žo─ču zaman kar┼č─▒m─▒za ├ž─▒kan bir durumdur.

public interface GenericRepository {
    <T> List<T> findAll(Class<T> entityClass);
    <T> T findById(Class<T> entityClass, Long id);
    <T> void create(T entity);
    <T> void update(T entity);
    <T> void delete(Class<T> entityClass, Long id);
}

JPA EntityManager aray├╝z├╝nde de bu metotlara benzer metotlar yer almaktad─▒r.

public interface EntityManager {    
    <T> T find(Class<T> entityClass, Object primaryKey);
    void persist(Object entity);
    <T> merge(T entity);
    remove(Object entity);
}

findAll metodu da JPA’n─▒n TypedQuery aray├╝z├╝ ve JPQL kullan─▒larak rahatl─▒kla kar┼č─▒lanabilmektedir.

TypedQuery<User> query = em.createQuery("select u from User as u", User.class);
List<User> users = query.getResultList();

G├Âr├╝ld├╝─č├╝ ├╝zere, uygulama i├žin olu┼čturulan entity spesifik veya generic depo aray├╝z ve implemantasyon s─▒n─▒flar─▒ s├Âz konusu ise bunlar─▒n yapt─▒─č─▒ ├žok fazla bir┼čey olmayacak ve g├Ârevleri kabaca i┼či EntityManager’─▒n ilgili metoduna havale etmekle s─▒n─▒rl─▒ kalacakt─▒r. Dolay─▒s─▒ ile yukar─▒da bahsetti─čim g├Âr├╝┼č├╝ savunanlar bu t├╝r ayr─▒ bir depo katman─▒n─▒ tamamen tasviye edip servis katman─▒na EntityManager’─▒ enjekte ederek ├žal─▒┼čmay─▒ tercih etmektedirler.

Peki bu t├╝r bir tercihin muhtemel dezavantajlar─▒ nedir? Gelin bunu da bir sonraki yaz─▒m─▒zda tart─▒┼čal─▒m.

─░lgin├ž Bir Transaction Propagation Hikayesi 2

─░lgin├ž Bir Transaction Propagation Hikayesi┬áisimli blog yaz─▒m─▒z─▒n ilk b├Âl├╝m├╝nde Foo, Bar ve Baz entity’lerini insert eden FooService ve BarService bean’lerinin birbirlerini ├ža─č─▒r─▒rken, insert i┼člemlerini iki farkl─▒ transaction i├žerisinde yapmaya ├žal─▒┼čt─▒klar─▒ndan bahsetmi┼čtik. ├ľnce FooService.foo() metodu i├žerisinde Foo entity’si, ard─▒ndan da BarService.bar() metodu i├žerisinde s─▒ra ile Baz ve Bar entity’leri JPA EntityManager vas─▒tas─▒ ile persist ediliyorlard─▒. Bar entity’si i├žerisinde nullable=false ┼čeklinde tan─▒ml─▒ bir property nedeni ile Bar entity’sinin DB’ye insert edilmesi s─▒ras─▒nda bir constraint violation exception meydana geliyordu. Sorumuz da bu exception sonucunda, DB’de Foo, Baz ve Bar entity’lerinden hangilerinin insert edilip edilmeyece─či ┼čeklindeydi.

Asl─▒nda Bar’─▒n persist edilme b├Âl├╝m├╝nde bu senaryonun sonucunu etkilemeyen ama constraint violation’─▒n tam olarak meydana gelece─či noktay─▒ veritaban─▒ spesifik hale getiren eksik bir ifadenin oldu─čunu fark ettim.

JPA, PersistenceContext’de biriken entity state de─či┼čikliklerini (insert, update ve delete operasyonlar─▒) transaction commit an─▒nda topluca ger├žekle┼čtirir. Bu yakla┼č─▒ma “transaction write behind” ad─▒┬á da verilmektedir. Dolay─▒s─▒ ile EntityManager.persist() metodunun invoke edildi─či tam o anda, ger├žekten de DB’ye bir SQL insert ifadesi gitmeyebilir. Bu insert TX commit a┼čamas─▒na kadar ├Âtelenebilir. Sonu├ž olarak,┬áconstraint violation hatas─▒n─▒n, Bar entity’sinin persist edildi─či a┼ča─čaki kod blo─ču taraf─▒ndan catch edilmesi s├Âz konusu olmayabilir.

try {
    em.persist(new Bar());
} catch(Exception ex) {
//ignore ex...
}

Bu durumda constraint violation hatas─▒ bar() metodunun sonland─▒─č─▒ ve Spring TransactionManager bean’inin aktif transaction’─▒ commit etmeye ├žal─▒┼čt─▒─č─▒ anda meydana gelecektir.

“Transient entity’yi DB’ye insert edecek SQL insert ifadesinin tam olarak EntityManager.persist() metodu ├ža─čr─▒ld─▒─č─▒ vakit y├╝r├╝t├╝lmesi hangi durumda s├Âz konusu olmaktad─▒r?” gibi┬ábir soru sorarsak, buna cevab─▒m─▒z “DB’nin sentetik PK ├╝retme stratejisi olarak default y├Ânteminin identity veya autoincrement y├Ântemlerinden birisi olmas─▒ durumunda” ┼čeklinde olacakt─▒r. Spesifikasyon gere─či JPA, PersistenceContext’e eklenen transient nesnelere hemen bir PK de─čeri atamak zorundad─▒r. Sentetik PK ├╝retme stratejisinin identity veya autoincrement oldu─ču durumda PK de─čerini elde etmenin tek yolu entity’ye kar┼č─▒l─▒k gelen bir kayd─▒ DB’ye o anda insert etmektir. Bu durumda da constraint violation hatas─▒ tam olarak EntityManager.persist(new Bar()); ifadesinin ├ža─čr─▒ld─▒─č─▒ yerde olacak ve exception try..catch blo─ču taraf─▒ndan da yakalan─▒p ignore edilebilecektir.

DB’nin default sentetik PK ├╝retme stratejisi sequence, UUID vb olursa kay─▒t DB’ye insert edilmeden de rahatl─▒kla bir PK de─čeri ├╝retilebilece─či i├žin yeni persist edilen bir entity’ye ait insert SQL’i TX commit an─▒na kadar ├Âtelenir ve bu senaryodaki hata da try..catch blo─čuna d├╝┼čmeyecektir.

Sentetik PK ├╝retme stratejisinin sequence, UUID vb oldu─ču senaryolarda da EntityManager.persist() metodu ├ža─čr─▒l─▒r ├ža─čr─▒lmaz entity’ye ait SQL insert ifadesinin DB’ye yans─▒t─▒lmas─▒ da m├╝mk├╝nd├╝r. Bunun i├žin PersistenceContext’in flush() metodu kullan─▒l─▒r.

try{
    em.persist(new Bar());
    em.flush();
} catch(Exception ex) {
    //ignore ex...
}

Yukar─▒daki kod blo─čunda oldu─ču gibi persist() metot ├ža─čr─▒s─▒ndan hemen sonra yap─▒lacak bir flush() metot ├ža─čr─▒s─▒ PersistenceContext’de birikmi┼č┬ástate de─či┼čikliklerini topluca DB’ye yans─▒tacakt─▒r. B├Âylece Bar entity’sinin nullable=false tan─▒ml─▒ property’sinin NULL b─▒rak─▒lmas─▒ndan kaynakl─▒ constraint violation hatas─▒ da hemen o anda ortaya ├ž─▒kacak ve try..catch blo─ču taraf─▒ndan da handle edilebilecektir.

Ancak DB sentetik PK stratejisinin t├╝r├╝ veya persist() i┼čleminden sonra flush()’─▒n ├ža─čr─▒l─▒p ├ža─čr─▒lmamas─▒ senaryomuzda ortaya ├ž─▒kacak sonucu de─či┼čtirmemektedir. Exception ister persist an─▒nda, isterse TX commit an─▒nda f─▒rlat─▒ls─▒n bu senaryo sonucunda Foo, Baz ve Bar entity’lerinden hi├ž biri DB’ye insert edilmezler. Yani hem FooService.foo() metodunda ba┼člat─▒lan transaction, hem de BarService.bar() metodunda Propagation.REQUIRES_NEW ile tetiklenen yeni transaction’─▒n her ikisi de rollback olmaktad─▒r. Bunun nedeni de JPA’n─▒n, constraint violation hatas─▒ meydana geldi─či anda PersistenceContext’e ait aktif transaction’─▒ ilerleyen ad─▒mlarda sadece rollback yap─▒labilir diye i┼čaretlemesidir. Farz edelim ki EntityManager.persist() metodu ├ža─čr─▒ld─▒─č─▒ anda veya bu ifadeden sonra yer alan bir flush() metot ├ža─čr─▒s─▒ ile birlikte constraint violation hatas─▒ f─▒rlat─▒lm─▒┼č olsun. Hata bizim try..catch blo─čumuz taraf─▒ndan yakalan─▒p ignore edilse bile, JPA bu hata dolay─▒s─▒ ile transaction’─▒ rollback’e set etmektedir. Daha sonra bar() metodu ba┼čar─▒l─▒ bi├žimde sonlan─▒p Spring TransactionManager transaction’─▒ commit etmeye ├žal─▒┼čsa bile JPA/Hibernate’in fiziksel transaction’─▒ rollback’e i┼čaretlemesinden ├Ât├╝r├╝ Spring’in transaction’─▒ da fail eder ve commit a┼čamas─▒nda UnexpectedRollbackException ┼čeklinde bir hata f─▒rlat─▒r. Bu hata da bir ├╝stteki foo() metoduna ula┼č─▒r ve onun da d─▒┼č─▒na ├ž─▒karak RuntimeException ile sonlanmas─▒na neden olur. Bu durumda foo() metoduna ait Spring transaction’─▒ da rollback edilir ve sonu├ž olarak Foo entity’si de DB’ye insert edilememi┼č olunur.

BarService.bar() metodu i├žerisinde Bar ve Baz entity’lerinin constraint violation hatas─▒ meydana geldi─či m├╝ddet├že hi├žbir bi├žimde DB’ye insert edilmeleri m├╝mk├╝n olamaz. Ancak FooService.foo() metodu i├žerisindeki Foo entity’si i├žin ayn─▒ durum s├Âz konusu de─čildir. Yani Foo entity’si di─čerlerinden ba─č─▒ms─▒z olarak DB’ye insert edilebilir. Peki bunun i├žin ne yapabiliriz, hangi y├Ântemleri kullanabiliriz? Gelin bunu da bir sonraki yaz─▒m─▒zda ele alal─▒m…

─░lgin├ž Bir Transaction Propagation Hikayesi

A┼ča─č─▒daki ├Ârnekte Foo, Bar ve Baz ┼čeklinde ├╝├ž basit entity s─▒n─▒f g├Âr├╝yorsunuz. Foo ve Baz i├žerisinde PK d─▒┼č─▒nda hi├žbir property mevcut de─čilken, Bar s─▒n─▒f─▒nda ise not null ├Âzelli─činde bir name property’si tan─▒ml─▒. Ayr─▒ca bu entity’leri persist eden FooService ve BarService servis bean s─▒n─▒flar─▒m─▒z da var.

@Entity
public class Foo {
    @Id
    @GeneratedValue
    private Long id;
}

@Entity
public class Bar {
    @Id
    @GeneratedValue
    private Long id;
  
    @Column(nullable=false)
    private String name;
}

@Entity
public class Baz {
    @Id
    @GeneratedValue
    private Long id;
}

@Service
public class FooService {
    
    @Autowired
    private BarService barService;

    @PersistenceContext
    private EntityManager em;

    @Transactional(propagation=Propagation.REQUIRED)
    public void foo() {
        em.persist(new Foo());
        barService.bar();
    }
}

@Service
public class BarService {
    @PersistenceContext
    private EntityManager em;
    
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void bar() {
        em.persist(new Baz());
        try {
            em.persist(new Bar());
        } catch(Exception ex) {
            //ignore ex...
        }
    }
}

Dikkat ederseniz FooService’in transactional foo() metodu i├žerisinde EntityManager vas─▒tas─▒ ile bir Foo nesnesi persist edildikten sonra barService.bar() metodu ├ža─čr─▒l─▒yor.

BarService’in bar() metodu da transactional, ancak propagation kural─▒ olarak REQUIRES_NEW tan─▒ml─▒. Yani bar() metodu ├ža─čr─▒ld─▒─č─▒nda ortamda mevcut bir transaction olup olmamas─▒na bak─▒lmaks─▒z─▒n yeni bir transaction ba┼člat─▒lacak ve Bar entity’sinin persist i┼člemi bu yeni transaction i├žerisinde ger├žekle┼čtirilecek.

Propagation REQUIRES_NEW kural─▒na g├Âre, ortamda mevcut bir transaction varsa bu mevcut eski transaction, bar() metodu sonlanana de─čin suspend edilerek bekletilir, bar() metodu sonland─▒ktan sonra da kald─▒─č─▒ yerden devam eder.

BarService.bar() metodu i├žerisinde ├Ânce yeni bir Baz nesnesi, ard─▒ndan da benzer bi├žimde yeni bir Bar nesnesi yarat─▒l─▒p EntityManager.persist() metodu ile DB’ye insert ediliyor. Bar entity’sinin persist i┼člemi s─▒ras─▒nda meydana gelebilecek herhangi bir hata da yakalan─▒p ignore ediliyor. ├ç├╝nk├╝ Bar nesnesi i├žerisinde not null tan─▒ml─▒ bir name property’si var ve buna herhangi bir de─čer set edilmeden insert edilmeye ├žal─▒┼č─▒ld─▒─č─▒ i├žin insert SQL’i ├žal─▒┼čt─▒r─▒l─▒rken DB’den bir constraint violation hatas─▒ meydana gelecektir. Uygulama i├žerisinde bu hata try/catch blo─ču ile yakalan─▒p ignore ediliyor. Yani BarService.bar() metodunun ba┼čar─▒l─▒ bi├žimde sonlanmas─▒, b├Âylece Spring’in dekleratif transaction y├Ânetim kurallar─▒na g├Âre ba┼čar─▒l─▒ sonlanan metot i├žin de transaction commit tetiklenmesi isteniyor.

Bu noktada sizce tam olarak ne olur? BarService.bar() metodu ba┼čar─▒l─▒ sonland─▒─č─▒ vakit Spring transaction’─▒ commit edip Baz entity’si, ard─▒ndan da FooService.foo() metodu ba┼čar─▒l─▒ sonlan─▒p Foo entity’si DB’ye insert edilirler mi? Yoksa BarService.bar() metodu i├žerisinde Bar nesnesinin insertion i┼člemi s─▒ras─▒nda meydana gelen constraint violation hatas─▒ ile Baz entity’si de Bar ile ayn─▒ transaction i├žerisinde oldu─ču i├žin DB’ye eklenemez, ama FooService.foo() metodunun transaction’─▒ farkl─▒ oldu─ču i├žin Foo entity’si DB’ye insert edilir mi? Ya da bunlar─▒n d─▒┼č─▒nda bir sonu├žla m─▒ kar┼č─▒la┼č─▒r─▒z?

Cevap bir sonraki yaz─▒m─▒zda ­čÖé

 

ServiceLoader vs SpringFactoriesLoader, Hangisini Kullanal─▒m?

ServiceLoader, Java 1.6ÔÇÖdan bu yana sunulan ancak pek de bilinmeyen basit bir kabiliyettir. Biraz da SpringÔÇÖin g├Âlgesinde kalm─▒┼čt─▒r diyebiliriz. Sonu├žta ServiceLoader ile sunulan kabiliyeti de kapsayan ve ├žok daha fazlas─▒n─▒ sunan bir IoC containerÔÇÖ─▒n─▒z varsa uygulama i├žerisinde farkl─▒ servis ger├žekle┼čtirimlerini dinamik olarak y├╝kleme ve kullanma ihtiyac─▒ i├žin Spring ApplicationContext i├žerisinde bean tan─▒mlamamak ├žok daha tercih edilesi bir yakla┼č─▒md─▒r. Yine de nesneleri ApplicationContext i├žerisinde bean olarak tan─▒mlaman─▒n uygun veya m├╝mk├╝n olmad─▒─č─▒ bir tak─▒m senaryolar i├žin ServiceLoader tercih edilebilir.

Nedir ServiceLoader ile yap─▒lan ┼čey? ├ľncelikle ServiceLoader ile yarat─▒lacak olan servis nesnelerinin s─▒n─▒flar─▒n─▒n sahip olmas─▒ gereken interface tan─▒mlanmal─▒d─▒r. Sonu├žta ServiceLoader bu interfaceÔÇÖi parametre alarak sistemdeki servis ger├žekle┼čtirimlerini bulup, yarat─▒p uygulaman─▒n kullan─▒m─▒na sunmaktad─▒r.

ServiceLoader serviceLoader = ServiceLoader.load(FooService.class);

ServiceLoader.load(FooService.class) ile FooService aray├╝z├╝ne sahip servis ger├žekle┼čtirimlerini classpathÔÇÖden bulup y├╝klemeyi sa─člayacak yeni bir ServiceLoader nesnesi yaratm─▒┼č oluruz. Peki, ServiceLoader, FooService ger├žekle┼čtirimlerini nas─▒l buluyor? Bunun i├žin ServiceLoader, classpathÔÇÖdeki META-INF/services/com.example.FooService ┼čeklinde olu┼čturulmu┼č dosyalar─▒ tespit etmektedir. Bu dosyalar birden fazla olabilir. Her bir dosyan─▒n i├žerisinde de servis s─▒n─▒flar─▒n─▒n FQN ┼čeklinde isimleri yer almal─▒d─▒r.

com.example.demo.my.MyFooService
com.example.demo.your.YourFooService

Her bir dosya da da birden fazla ger├žekle┼čtirim yer alabilir. Her servisin FQNÔÇÖsi ayr─▒ bir sat─▒rda tan─▒mlan─▒r.

Iterator iterator = serviceLoader.iterator();

serviceLoader.iterator() metodu ile mevcut servis dosyalar─▒n─▒ lazy bi├žimde parse ederek servis nesnelerini olu┼čturacak bir Iterator elde edilir.

while(iterator.hasNext()) {
    FooService fooService = iterator.next();
}

iterator.hasNext() ve next() metotlar─▒ ile de servis s─▒n─▒flar─▒ y├╝klenir ve bu s─▒n─▒flar─▒n default no arg constructorÔÇÖlar─▒ ├ža─čr─▒larak servis nesneleri yarat─▒l─▒r ve uygulamaya d├Ân├╝l├╝r. Art─▒k uygulama FooService instanceÔÇÖlar─▒ ├╝zerinde istedi─či i┼člemi yapabilir.

E─čer uygulama farkl─▒ bir servis aray├╝z├╝ ├╝zerinden ba┼čka servis nesnelerini elde edecek ise bu durumda da yeni servis aray├╝z├╝ i├žin META-INF/services dizini alt─▒nda yeni dosyalar tan─▒mlan─▒p, benzer bi├žimde bu servis aray├╝z├╝ ├╝zerinden servis nesnelerinin olu┼čturulmas─▒ s├Âz konusudur.

Gelelim SpringÔÇÖin SpringFactoriesLoader s─▒n─▒f─▒na. ServiceLoader ile hemen hemen ayn─▒ i┼člemi yapmaktad─▒r. Ancak tek fark─▒ birden fazla servis aray├╝z├╝ i├žin ayr─▒ ayr─▒ servis dosyalar─▒ tan─▒mlatmak yerine, b├╝t├╝n servis ger├žekle┼čtirimlerini classpathÔÇÖde META-INF/spring.factories isimli bir dosya i├žerisinde toplamaya olanak sa─člamas─▒d─▒r. SpringÔÇÖin bu s─▒n─▒f─▒ frameworkÔÇÖ├╝n dahili kullan─▒m─▒ i├žin olsa bile rahatl─▒kla Spring enabled bir uygulamada da kullan─▒labilir.
com.example.demo.FooService=com.example.demo.my.MyFooService,com.example.demo.your.YourFooService

com.example.demo.BarService=com.example.demo.BarServiceImpl

spring.factories dosyas─▒ i├žerisinde servis ger├žekle┼čtirimleri servis aray├╝z├╝ key, servis ger├žekle┼čtirimleri de virg├╝llerle ayr─▒lm─▒┼č bi├žimde FQNÔÇÖli isimleri ile value olacak bi├žimde tan─▒mlanmal─▒d─▒r.

List services = SpringFactoriesLoader.loadFactories(
                  FooService.class, FooService.class.getClassLoader());

SpringFactoriesLoader.loadFactories() metodu ile belirtilen servis aray├╝z├╝ne veya s─▒n─▒f─▒na kar┼č─▒l─▒k gelen s─▒n─▒flar y├╝klendikten sonra, default no arg constructorÔÇÖlar─▒ ├ža─čr─▒larak servis instanceÔÇÖlar─▒ elde edilir ve uygulamaya bir liste ┼čeklinde d├Ân├╝l├╝r.

K─▒sacas─▒ Spring kullanan projelerde JavaÔÇÖn─▒n ServiceLoader mekanizmas─▒ ile ayr─▒ servis dosyalar─▒ ├╝zerinden ├žal─▒┼čmaya l├╝zum yoktur. SpringÔÇÖin SpringFactoriesLoader mekanizmas─▒ servis tan─▒mlar─▒n─▒n tek noktadan y├Ânetimi a├ž─▒s─▒ndan daha pratik bir yol sunmaktad─▒r.

JPA/Hibernate Pratikleri ve P├╝f Noktalar─▒

Yakla┼č─▒k 2004 y─▒l─▒ndan bu yana ├Ânce Hibernate sonra da JPA ├╝zerinde ├žal─▒┼č─▒yorum. O d├Ânemden bu yana pek ├žok projenin geli┼čtirilmesinde g├Ârev ald─▒m, dan─▒┼čmanl─▒k ve ment├Ârl├╝k yapt─▒m. Y─▒llar ├Ânce Ted Neward, ORM teknolojilerini bilgisayar bilimlerinin VietnamÔÇÖ─▒na benzetmi┼čti ve bunda da ├žok hakl─▒yd─▒. E─čer genel olarak ORM ├ž├Âz├╝mleri, ├Âzelde de JPA/Hibernate ile ilgili sa─člam bir teorik bilgiye sahip de─čilseniz projenizin tam manas─▒ ile bir batakl─▒─ča d├Ân├╝┼čmesi i┼čten bile de─čildir. B├╝t├╝n bu y─▒llar boyunca JPA/Hibernate ile ├žal─▒┼čmalar─▒m sonucu bir tak─▒m temel kullan─▒m pratikleri ve p├╝f noktalar belirledim. Dahil oldu─čum projelerde de bu pratiklerin uygulanmas─▒na, p├╝f noktalara dikkat edilmesine azami gayret g├Âsteriyorum ve sonu├ž olarak JPA/Hibernate ile ├žal─▒┼čmak ├žok daha verimli bir hal al─▒yor. A┼ča─č─▒da bu temel pratik ve p├╝f noktalardan akl─▒ma gelenlerin bir k─▒sm─▒n─▒ sizlerle payla┼čmak istedim. Umar─▒m i┼činize yarar!

  1. 1:1 ve M:1 ili┼čkiler de dahil olmak ├╝zere b├╝t├╝n ili┼čkileri LAZY tan─▒mlay─▒n.
  2. 1:M ili┼čkileri her zaman i├žin ├žift y├Ânl├╝ tan─▒mlay─▒n ve ili┼čkiyi sadece M:1 taraf─▒ndan y├Ânetin.
  3. M:N ili┼čkileri association table i├žin ayr─▒ bir entity tan─▒mlayarak iki tane ├žift y├Ânl├╝ 1:M ili┼čki ile ele al─▒n.
  4. Cascade tan─▒mlar─▒n─▒ sadece 1:M ve 1:1 ili┼čkilerde ve ger├žekten hangi i┼člemlerin cascade etmesini istiyorsan─▒z sadece onlar i├žin kullan─▒n.
  5. Embeddable yerine her zaman i├žin Entity kullan─▒n.
  6. Derin inheritance hiyerar┼čilerinden ka├ž─▒n─▒n, m├╝mk├╝n oldu─čunca b├╝t├╝n hiyerar┼či i├žin ÔÇťtek tabloÔÇŁ y├Ânetimini tercih edin.
  7. Bile┼čke PK y├Ânetiminden uzak durun
  8. N├╝merik PK de─čeri ile ├žal─▒┼č─▒lmas─▒ gerekiyorsa sentetik PK ├╝retme y├Ântemlerinden SEQUENCEÔÇÖi tercih edin.
  9. Entity s─▒n─▒flar─▒n equals ve hashCode metotlar─▒n─▒ implement ederken proxy ile ├žal─▒┼čma ihtimaline kar┼č─▒n attribute de─čerlerine her zaman getter metotlar vas─▒tas─▒ ile eri┼čin.
  10. HQL, Criteria ve Native SQL sorgular─▒ndan sadece senaryonun ihtiya├ž duydu─ču kadar veriyi i├žeren DTO nesneleri d├Ân├╝n, hi├žbir zaman Entity d├Ânmeyin.
  11. LazyInitializationException probleminin en iyi ├ž├Âz├╝m yolu ÔÇťfetch joinÔÇŁdir, Hibernate.initialize(), proxy nesnenin veya collectionÔÇÖ─▒n i├žerisine eri┼čim, OpenSessionInViewFiler, OpenEntityManagerInViewFilter gibi y├Ântemleri kesinlikle kullanmay─▒n.
  12. ├ťretim ortam─▒nda detached uninitialized lazy ili┼čkilere veya proxyÔÇÖlere eri┼čim s─▒ras─▒nda lazy hatas─▒ ile kar┼č─▒la┼čmamak i├žin hibernate.enable_lazy_load_no_trans property tan─▒m─▒n─▒n de─čerini TRUE yap─▒n.
  13. Entity d├╝zeyinde ikincil ├Ânbellek kullan─▒yorsan─▒z, bu entityÔÇÖnin 1:1 ve M:1 ili┼čkili hedef entityÔÇÖlerini de entity d├╝zeyinde ├Ânbellek kontrol├╝ne dahil etmeyi unutmay─▒n.
  14. ─░li┼čki d├╝zeyinde ikincil ├Ânbellek kullan─▒yorsan─▒z, ili┼čkinin hedef entityÔÇÖsini de entity d├╝zeyinde ├Ânbellek kontrol├╝ne dahil etmeyi unutmay─▒n.
  15. Sorgu sonu├žlar─▒n─▒n ├Ânbellekte tutulmas─▒ faydadan ├žok zarar getirebilir, bunu unutmay─▒n. Ger├žekten sorgu sonu├žlar─▒n─▒ ├Ânbelle─če dahil edecekseniz ve sorgu sonucunda entity d├Ân├╝l├╝yorsa hedef entityÔÇÖyi de ├Ânbellek kontrol├╝ne dahil etmeyi unutmay─▒n.

Eclipse Package Explorer’da Font B├╝y├╝kl├╝─č├╝

Uzun zamand─▒r geli┼čtirme platformu olarak Eclipse/Spring Tool Suite’i tercih ediyorum ve Kurumsal Java E─čitimleri’mizde de bunu kullan─▒yorum. E─čitimler s─▒ras─▒nda Eclipse i├žerisinde a├ž─▒lan editor’lerin fontlar─▒n─▒ Window>Preferences>Appearance>Colors and Fonts b├Âl├╝m├╝nden de─či┼čtirebiliyoruz. Ancak ge├žen bir e─čitimde projeksiyon cihaz─▒n─▒n netlik probleminden dolay─▒ Package Explorer’daki paket, s─▒n─▒f ve dosya isimlerinin de fontlar─▒n─▒ b├╝y├╝tmek gerekti. Eclipse’in ayarlar─▒n─▒ biraz kurcalad─▒ktan sonra b├Âyle bir┼čeyin Eclipse i├žerisinden m├╝mk├╝n olmad─▒─č─▒n─▒ anlad─▒m. K─▒sa bir “googling seans─▒” ard─▒ndan c├Âz├╝m├╝n plugins/org.eclipse.ui.themes_XXX/css alt─▒ndaki css dosyalar─▒n─▒ de─či┼čtirmekten ge├žti─čini anlad─▒m. Package Explorer’─▒n font ebat─▒n─▒ de─či┼čtirmek i├žin hangi temay─▒ kull─▒yor iseniz ona kar┼č─▒l─▒k gelen css dosyas─▒n─▒n i├žerisine

.MPart Tree { font-size: 16; }

┼čeklinde bir css kural─▒ eklemeniz yeterli olacakt─▒r. Bu i┼člemin ard─▒ndan Eclipse/STS’i kapat─▒p a├žmay─▒ unutmay─▒n ­čÖé