forked from burakbayramli/books
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchaphibernate.tex
1709 lines (1442 loc) · 73.1 KB
/
chaphibernate.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\movetooddpage
\chapter{Hibernate} \label{hibernate}
\thischapterhas{
\item Hibernate projesini kuruluşu, dizin yapısı, Ant build.xml
\item Öğe ve kolon eşlemesi
\item Nesneler arası ilişkiler
\item Sorgulama
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\versal{T}\textsc{artışmasız,} veri tabanına erişim, yâni bir kaydı eklemek,
silmek, güncellemek gibi işlemler, kurumsal uygulamaların en önemli bölümünü
teşkil eder. Yaygın bilinen bir istatistiğe göre, bir kurumsal uygulamanın
veriye erişiminde harcanan zaman, uygulamanın tamamında harcanan zamana oranla
\%80 kadar bir zaman teşkil etmektedir. Bu oran, çok büyük bir rakamdır ve
kurumsal uygulamalarda veriye erişimin önemine işaret eder.
Java dünyasında veriye erişim için en temel yöntem, JDBC (Java Database
Connectivity) adı verilen bir kütüphane üzerindendir. JDBC, Java ile veri
tabanlarına erişim için piyasaya çıkan ilk çözümdür. Çok temel olması sebebiyle,
JDBC'nin programcıya yapmasını izin verdiği işlemler veri tabanına yakın ve SQL
ile direk alâkalıdır: Sorgu işletmek, sorgulardan gelen sonuçları
listeleyebilmek, depolanmış işlemleri (stored procedure) çalıştırabilmek
gibi. Veriye erişim teknolojileri klasmanında JDBC, DB ve uygulama arasında {\em
çok ince bir tabakadır} denebilir. Veri, uygulamaya ``tablolar kümesi'' olarak
gözükür ve bu tablolar kümesi üzerindeki işlemler ile veri eklenir, güncellenir
ve ya silinir.
\section{Faydalar}
Kıyasla nesnesel programcılar, veriyi nesneler topluluğu olarak görmeye
alışkındırlar. Nesnesel tasarımı takip eden bir programcı için veri, bir nesne
içinde tutulabilen, işlenebilen, değiştirilebilen ve Java temel tipleri
üzerinden geri verilebilen bir kavram olmalıdır. Nesnesel dillerin izole etme
(encapsulation) gibi özellikleri, bu tür kodlama stilini ayrıca özendirmekte,
idare edilir hâle getirmektedir.
Kod temizliği açısından da nesne odaklı veri idaresinin daha makbul olduğu
açıktır. Uygulamamızın hem veri hem görsel tabakasında veriyi alışık olduğumuz
kodlama ve taşıyıcı birimler üzerinden görmek isteriz. Bu birimler, içinde get
ve set içeren basit Java nesneleri olacaktır. Bu basit Java nesne türüne yeni
literatürde POJO adı da verilmektedir. POJO kelimesinin açılımı \textbf{P}lain
\textbf{O}ld \textbf{J}ava \textbf{O}bject (Basit Java Nesneleri) olarak
bilinir. Örnek bir POJO aşağıda gösteriliyor.
\begin{lstlisting}[language=Java,frame=none]
public class Car {
public Car() { }
String licensePlate;
public String getLicensePlate() {
return licensePlate;
}
public void setLicensePlate(String newLicensePlate) {
this.licensePlate = newLicensePlate;
}
String description;
public String getDescription() {
return description;
}
public void setDescription(String newDescription) {
this.description = newDescription;
}
}
\end{lstlisting}
\PVerb!Car! nesnesi bir arabayı temsil etmektedir, ve veri olarak içinde
\PVerb!licensePlate! (plaka) ve \PVerb!description! (târif) öğelerini
taşımaktadır. Araba nesnesi, pür bir Java nesnesi olduğu için, her türlü Java
işlemine tâbi olabilir; Bir listeye eklenebilir, öğeleri dinamik olarak
sorgulanabilir, hatta başka bir JVM'e bile gönderilebilir. Yapılacak işlemler
Java programını yazan programcının hayali ile sınırlıdır.
\begin{figure}[!hbp]
\center{
\scalebox{1.2}{
\includegraphics{./images/mapping_er_obj_garage_car.eps}
}
}
\caption{İki Nesne ve Tablo İçeren bir Eşleme}
\end{figure}
Hibernate gibi teknolojilerin amacı, kurumsal programcıların veriye erişim
işlemlerini tamamen POJO'lar üzerinden yapmalarına imkan vermeleridir. Bir
POJO'nun veri tabanına nasıl eşlenmesi gerektiğini eşleme (mapping) dosyası
üzerinden anlayabilen Hibernate, bu noktadan sonra tüm gerekli SQL komutlarını
dinamik olarak arka planda kendisi üretebilecektir. Yâni bir tablo üzerinde JDBC
ile yaptığımız \PVerb!SELECT, UPDATE, INSERT, DELETE! işlemlerinin tümü, tek bir
eşleme dosyası ile Hibernate tarafından otomatik olarak yapılacaktır. Aşağıda
araba nesnemizi veri tabanı tablosuna eşleyen bir Hibernate dosyasını görüyoruz.
\begin{lstlisting}[language=Java,frame=none]
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.mycompany.kitapdemo.pojo">
<class name="Car" table="car">
<id name="licensePlate" column="license_plate">
<generator class="assigned"/>
</id>
<property name="description" column="description"/>
</class>
</hibernate-mapping>
\end{lstlisting}
Eşleme dosyasındaki \PVerb!Car! nesnesi, veri tabanındaki \PVerb!car! tablosuna
eşlenmektedir, ve tablonun her kolonunun hangi nesne öğesine eşlenmesi gerektiği
teker teker belirtilmiştir. Bundan sonra bir \PVerb!Car!'ı veri tabanına
eklemek, şu Hibernate komutlarından ibarettir.
\begin{lstlisting}[language=Java, frame=none]
Car car = new Car();
car.setLicensePlate(``52 THU 34'');
car.setDescription(``araba tanım 1'')
Session s = HibernateSession.openSession();
s.save(car);
\end{lstlisting}
Silmek için \PVerb!s.delete(car)!, güncellemek (ya da eklemek) için
\PVerb!s.saveOrUpdate(car)! kullanabiliriz.
Gördüğümüz gibi, her tablo için dört SQL komutu yazmak yerine tek bir eşleme
dosyası üzerinden Hibernate veri erişiminin faydaları açıktır. Kural \#1
bağlamında Hibernate veri erişimi daha kısa bir dille gerçekleştirilmiştir, ve
gene Kural \#1 bağlamında bir Hibernate eşleme dosyası daha rahat bakılabilir
bir koddur. Eğer, ileri bir zamanda \PVerb!car! tablo isminin değişmesi gerekse,
SQL şartlarında tüm \PVerb!SELECT, UPDATE, INSERT, DELETE! komutlarındaki tablo
isminin değiştirilmesi gerekecekti. Ya da, eğer bir kolon ismi değişse, o kolonu
kullanan tüm SQL'lerin değişmesi gerekecekti; Fakat Hibernate kullanıyorsak,
değişen kolonun sadece {\em nesne eşlemesini} değiştirerek, Hibernate üzerinden
veriye erişen işlem kodlarda tek bir satır değiştirmemiz gerekmez.
Bu bölümün geri kalanında, kuruluş ve ana Hibernate kavramlarını
işleyeceğiz. Eğer bu kavramları atlayıp, direk Hibernate'in nasıl kullanıldığını
öğrenmek istiyorsanız, tavsiyemiz \ref{hibernate:install:session} ve
\ref{hibernate:transaction} bölümlerine göz gezdirip, \ref{hibernate:crud}
bölümüne atlamanızdır.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Kurmak}
Hibernate üzerinden erişmek istediğimiz her nesne (POJO) için bir eşleme dosyası
yazılmalıdır. Fakat proje bazında, sadece bir kez yapılması gereken ayarlar
şunlardır. İlk önce, \PVerb!hibernate.cfg.xml!
gerekecek. \PVerb!hibernate.cfg.xml! dosyası, Hibernate'e
\begin{itemize}
\item Veri tabanına nasıl erişeceğini
\item Hangi önbellek paketini kullanacağını
\item Önbellekleme yönteminin ne olacağını
\item Hangi veri tabanı bağlantı havuzu (connection pool) kullanacağını
\end{itemize}
söyler. Ayar dosyasının \PVerb!CLASSPATH!'teki bir dizin içinde, ya da
\PVerb!CLASSPATH!'teki bir \PVerb!jar!ın en üst seviyesinde olması
gereklidir. Buna uygun \PVerb!jar! paketlemesi yapan örnek bir Ant
\PVerb!build.xml! script'ini, \PVerb!SimpleHibernate! projesinde bulabilirsiniz.
Altta \PVerb!Car! için hazırlanmış örnek bir \PVerb!hibernate.cfg.xml! dosyasını
gösteriyoruz.
\begin{lstlisting}[label=hibernate:cfg,numbers=left,numberstyle=\tiny,language=XML,caption=hibernate.cfg.xml]
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory name="foo">
<property name="hibernate.connection.driver_class">
org.gjt.mm.mysql.Driver
</property>
<property name="hibernate.connection.url">
jdbc:mysql://localhost:3306/cars
</property>
<property name="hibernate.connection.username">user</property>
<property name="hibernate.connection.password">pass</property>
<property name="hibernate.connection.provider_class">
org.hibernate.connection.C3P0ConnectionProvider
</property>
<property name="hibernate.c3p0.min_size">
5
</property>
<property name="hibernate.c3p0.max_size">
10
</property>
<property name="hibernate.c3p0.idle_test_period">
60
</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.cache.provider_class">
org.hibernate.cache.OSCacheProvider
</property>
<mapping resource="org/mycompany/kitapdemo/pojo/Car.hbm.xml"/>
<class-cache
class="org.mycompany.kitapdemo.pojo.Car"
region="Simple"
usage="read-write"/>
</session-factory>
</hibernate-configuration>
\end{lstlisting}
Satır satır açıklama:
\begin{itemize}
\item \textbf{10, 13}: 10. satırda verilen değer, Hibernate tarafından hangi
JDBC sürücüsünün kullanıldığını belirtiyor. Örnekte kullanılan değer,
\PVerb!org.gjt.mm.mysql.Driver! adlı MySQL sürücüsüdür. Sürücü parametresi,
direk JDBC'ye geçilen parametrelerden biridir (evet Hibernate kendi işleyişi
için, arka planda JDBC kullanıyor). MySQL sürücüsünün veri tabanına erişimi
için gerekli bilgiler 13. satırda verilmiş; Meselâ hangi makinaki hangi veri
tabanı bilgisi.
\item \textbf{15, 16}: Tabana bağlanmak için gerekli kullanıcı ismi ve
şifre.
\item \textbf{18}: Veri tabanı bağlantı havuzu (connection pool) paketi. Örnekte
seçilen paket, C3P0 adlı pakettir. Havuzlar hakkında detayları
\ref{hibernate:install:connpool} bölümünde bulabilirsiniz.
\item \textbf{20 - 28}: Bağlantı havuzu için gerekli parametreler (havuzun
olabilecek en az, ve en çok büyüklüğü).
\item \textbf{29 - 32}: Hangi veri tabanı için, nasıl SQL üretilmesi gerektiği de
burada belirtiliyor. Sürücü seçmek (MySQL, Oracle, vs), Hibernate'in nasıl SQL
üretmesini belirlemek için yeterli değildir. \PVerb!dialect! parametresi ile,
üretilen SQL'in hangi taban için olduğu kesin olarak Hibernate'e bildirmelidir.
\item \textbf{34}: Uygulamamızın kullanacağı POJO'ların listesi \PVerb!mapping
resource! ile Hibernate'e bildiriyoruz. Örneğimizde bir tane POJO
tanımlıyoruz: \PVerb!Car!. Hibernate, \PVerb!mapping resource!'da belirtilen
\PVerb!hbm.xml! dosyasını \PVerb!CLASSPATH!'te bulacak, eşleme tanımlarını
işleyecek ve POJO üzerinden SQL üretmeye hazır hâle getirecektir.
\item \textbf{35 - 38}: Önbellek ayarları. (Bu konunun detayları
\ref{hibernate:install:cache} bölümünde gösterilecektir).
\end{itemize}
\subsection{Bağlantı Havuzları (Connection Pools)} \label{hibernate:install:connpool}
Bir bilgi işlem uygulamasının yapabileceği en pahalı işlemlerden biri veri
tabanından bir bağlantı (connection) almaktır. Özellikle uygulamanız Oracle gibi
ciddi bir taban üzerinde çalışıyor ve bu tabanın kontrolü güvenliğe çok önem
veren bir admin'in de elinde ise, bağlantı alma sırasında birçok işlemden
(hafıza hazırlama, vs. gibi) geçileceği için, bağlantı alma işlemi kurumsal
uygulamalarda en pahalı ve yavaş işlemlerden biri hâline gelmektedir.
Bu yüzden, sistemin cevaplama zamanını (response time) hızlandırmayı amaçlayan
kurumsal programcılar her işlem (transaction) başında sürekli yeni bir bağlantı
açıp, işlem sonunda bu bağlantıyı kapatan kodlar yazmaktan kaçınırlar. Bunun
yerine, bağlantılar uygulama başında bir kez açılır ve bir tür {\em bağlantı
önbelleğinde} tutularak uygulamanın işleyişi boyunca açık tutulurlar. Bağlantı
önbellekleri, bir nevi havuzdur; Bağlantılar açılıp kapanmazlar, sadece bu ortak
havuzdan alınıp geri verilirler. Bu havuzun belli sayıda bağlantı içeren bir
havuz olması özellikle önemlidir, çünkü her taban, aynı anda, sadece belli
sayıda bağlantıya servis verebilir, ve optimal bağlantı sayısının üstünde servis
vermenin veri tabanınızın performans açısından kötü sonuçları olacaktır.
Piyasada Java/JDBC odaklı birçok havuz ürünü mevcuttur. DBCP, Proxool, C3P0 adlı
açık yazılım projeleri bu ürünlerden sadece birkaçıdır. Bahsedilen projelerin
her biri, Hibernate tarafından kullanılabilen
(\PVerb!hibernate.connection.provider_class! parametresi ile) projelerdir.
\subsection{Önbellek} \label{hibernate:install:cache}
Bir bilgi işlem uygulaması, eğer bir veri birimini tabandan {\em yazdığından}
daha fazla {\em okuyorsa}, o veri birimi tabandan servislemek yerine hafızadan
servislemek daha hızlı olacaktır. Önbellekleme (caching) tekniğinin altında
yatan fikir özet olarak budur. Bir bilgi işlem uygulamasının \%80 kadar
zamanının veri tabanında geçtiğini düşünürsek, disk'e ne kadar az gidersek veri
erişimini (ve bilahere uygulamanın bütününü) o kadar hızlandırmış oluruz.
Önbellek kullanımı, tek kullanıcılı ve çok kullanıcılı ortamlarda büyük
performans iyileştirmeleri getirebilir. Tek kullanıcılı ortamda, aynı veriye
birkaç kez erişen (aynı) kullanıcı, birinci kullanımdan sonra aynı veriye tekrar
eriştiğinde o veriyi önbellekten çok hızlı bir şekilde alabilmiş olur. Çok
kullanıcılı ortamda (aynı JVM'in birçok eşzamanlı kullanıcıya hizmet verdiği
şartlarda) önbellek kullanımı daha da avantajlıdır; Kullanıcı A'nın önbelleğe
getirmesini tetiklediği bir nesneyi, kullanıcı B olduğu gibi hafızadan
kullanabilecektir.
Hibernate, kontrolü altındaki tüm nesneleri programcıya hiçbir ek külfet
getirmeden programcının istediği herhangi bir önbellek paketi üzerinde tutmasına
izin vermektedir. Bu şimdiye kadar yalın JDBC teknikleri ile elimizde olmayan
müthiş bir esnekliktir. Düşünün: Daha önce \PVerb!HashMap! ya da elle yazılan
bir yapı üzerinde olan önbellekleme, artık, Hibernate'e verilen tek bir
parametre sayesinde otomatik olarak yapılıyor olacaktır.
Hibernate öncesi elimizle yapmamız gereken külfetli işlemler, pseudo kod olarak
altta gösterilmiştir:
\begin{lstlisting}[language=Java, frame=none]
def araba_yükle(licensePlate : String)
begin
Car tablosunda licensePlate kimlikli veriye eriş.
Bu veri, hafızadaki HashMap'te mevcut mu?
begin
Evet ise, hafızadakini döndür.
Hayir ise,
Tabandan yükle
HashMap'e yaz
Geri döndür
end
end
end
def araba_sil(licensePlate : String)
begin
Veri HashMap'te mevcut mu?
begin
Evet, hafizadakini sil, sonra tabandan sil
Hayir, tabandan sil
end
end
\end{lstlisting}
Hibernate, kontrolü altında olan nesnelerin nasıl ve ne zaman önbellekleceğini
önbellek paketlerine direk söyleyebilir, çünkü tabandan okuma, silme, yazma ve
güncelleme işlemlerinin tümü zaten Hibernate üzerinden yapılan
işlemlerdir. Hibernate, bu noktalara çengellenmiş olan önbellek kodları
sayesinde bir önbellek paketini programcıya hiçbir ek külfet getirmeden idare
edebilir. Yukarıda pseudo kod olarak gösterilen eylemlerin tamamı Hibernate için
gereksiz hâle gelmiştir (Kural \#2).
Ayrıca Hibernate, tek JVM'de işleyen türden önbellekleri kullanabildiği gibi,
{\em birden fazla JVM} üzerinde çalışan ve birbirini ağ üzerinden
güncelleyebilen {\em dağıtık önbellek} ürünlerini de kullanabilir. Dağıtık
önbellekler, ağ üzerinden birbirini senkronize edebilen ürünlerdir. Önbellek
kullanımı ve dağıtık önbellek konularını \ref{perf:opt:cache} bölümde daha
detaylı olarak göreceğiz.
\subsection{Örnek Proje Dizin Yapısı}
Bir Hibernate projesinde gereken minimal dosyalar ve dizin yapısı altta
belirtilmiştir.
\begin{lstlisting}[frame=none]
+- SimpleHibernate
| +- build
| +- lib
| | +- activation.jar
| | +- ant-antlr-1.6.2.jar
| | +- antlr-2.7.4.jar
| | +- c3p0-0.8.4.5.jar
| | +- c3p0.license.txt
| | +- cglib-full-2.0.2.jar
| | +- commons-collections.jar
| | .. ....
| | +- jaxen-1.1-beta-4.jar
| | +- junit-3.8.1.jar
| | +- log4j-1.2.9.jar
| | +- mysql-connector-java-3.0.16-ga-bin.jar
| | +- oscache-2.1.jar
| | +- xalan.jar
| | +- xml-apis.jar
| +- resources
| | +- hibernate.cfg.xml
| | +- log4j.properties
| | +- oscache.properties
| +- src
| | +- java
| | | +- org
| | | | +- mycompany
| | | | | +- kitapdemo
| | | | | | +- dao
| | | | | | | +- SimpleCarTest.java
| | | | | | +- pojo
| | | | | | | +- Car.hbm.xml
| | | | | | | +- Car.java
| | | | | | +- service
| | | | | | | +- HibernateSession.java
| | | | | | +- util
| | | | | | | +- AllTest.java
| | | | | | | +- ClassPathFile.java
| | | | | | | +- TestUtil.java
| | +- sql
| | | +- sample_data.sql
| | | +- tables_mysql.sql
| +- build.properties
| +- build.xml
\end{lstlisting}
Bu dizinler ve içerikleri, \PVerb!SimpleHibernate! projesinde
bulunabilir. Şimdi, bu dizinde bulunan birimleri görelim.
\subsection{Hibernate Session} \label{hibernate:install:session}
Hibernate ile veriye erişmek için, öncelikle bir \PVerb!org.hibernate.Session!
yaratıp bu nesne üzerinde \PVerb!openSession! metotunu çağırmak ve bir Hibernate
oturumu başlatmak gerekir. Örnek kodlarda görülen \PVerb!HibernateSession!
class'ı, içinde bir Hibernate \PVerb!SessionFactory! barındıran yardımcı bir
nesnedir. Bu yardımcı nesne örnek proje için yazılmış ve hazır olan bir koddur
ve projenize olduğu gibi eklenebilir.
Bir \PVerb!org.hibernate.Session! almak ve açmak (openSession) için
\PVerb!HibernateSession! nesnesindeki \PVerb!static! çağrıları
kullanabilirsiniz. \PVerb!HibernateSession! class'ının tüm metotları
\PVerb!static! metotlardır, bu yüzden \PVerb!HibernateSession!'ın bir nevi
Singleton nesnesi olduğu söylenebilir. Demek ki bir JVM içinde sadece bir aktif
\PVerb!HibernateSession! olabilir. \PVerb!HibernateSession!'daki static blok,
\PVerb!HibernateSession! kullanılır kullanılmaz hemen çağrılacağı için
\PVerb!hibernate.cfg.xml! dosyaları okunmaya başlar, ve referans edilen tüm
\PVerb!hbm.xml! dosyaları takip edilerek gereken eşlemeler hafızaya alınır,
işlenir, kontrol edilir ve önbellekte tutulur.
Static blok içinde yapılan diğer önemli bir işlem, bağlantı havuzu için `en az
gereken bağlantı (minimum connection count)' sayısı kadar bağlantının hemen
açılmasıdır. Bağlantı havuzlarını anlattığımız \ref{hibernate:install:connpool}
bölümünde bağlantı açmanın bir kurumsal uygulamada yapılabilecek en yavaş
işlemlerden biri olduğunu açıklamıştık. Bu sebeple, \PVerb!HibernateSession!'a
erişen {\em ilk kod parçasının} kullanıcıya dönük bir kod parçası olmaması iyi
olur. Niye? Çünkü, bir kullanıcının istediği basit bir sayfa/ekran için, 100
tane taban bağlantısının açılmasını beklediğini düşünün! Bu çok yavaş bir sayfa
yüklemesi olacaktır! Tabii ki aynı sayfa ikinci kez istendiğinde, cevap çok
çabuk dönecektir (gereken tüm bağlantılar artık açılmıştır), fakat o ilk yavaş
sayfa yüklemesi ile kullanıcının ağzında kötü bir tat bırakmamak gerekir. Bu
nedenle \PVerb!HibernateSession!'a erişen ilk kod parçasının kullanıcıya dönük
kodlardan ayrı, sadece {\em uygulama başlarken} çağrılacak {\em ayrı} bir kod
parçasından olmasına özen gösterin.
Uygulamanın geri kalan kısmında, \PVerb!HibernateSession! üzerinden aldığınız
\PVerb!Session! nesnesi ile istediğiniz veri tabanı işlemini
gerçekleştirebilirsiniz. Tek nesne yükleme, yazma, silme, ve sorgulama
metotlarının hepsi \PVerb!Session! üzerinde bulunmaktadır.
İşimiz bittikten sonra ise, \PVerb!HibernateSession.closeSession! kullanarak
\PVerb!Session!'ı kapatmamız gerekir, çünkü JDBC taban bağlantısını muhafaza
eden nesne \PVerb!Session! nesnesidir; \PVerb!close! çağrısı ile veri taban
bağlantısının bağlantı havuzuna dönmesini tetiklemiş oluruz. Eğer uygulama
içinde hiç \PVerb!close! çağrısı yapmamış olsaydık, uygulamamız bir süre sonra
havuzdaki tüm bağlantıları bitirecek ve uygulama işleyişine devam edemez hâle
gelecektir.
\subsection{Hibernate Transaction} \label{hibernate:transaction}
Veri tabanında okuma, yazma, silme işlemleri yapan bir kod parçası, işine
muhakkak \PVerb!beginTransaction()! ile başlamalı ve \PVerb!commitTransaction()!
ile işini bitmelidir. Özetle Hibernate ile her yaptığınız her işlemi alttaki kod
bloğuyla sarmanız yararlı olur.
\begin{lstlisting}[language=Java,frame=none]
try {
Session s = HibernateSession.openSession();
HibernateSession.beginTransaction();
// ...
// Hibernate işlemleri
// ...
HibernateSession.commitTransaction();
} finally {
HibernateSession.closeSession();
}
\end{lstlisting}
Kapanış \PVerb!closeSession! işleminin \PVerb!finally! içinde konması önemlidir,
çünkü Hibernate operasyonunuz sırasında exception atılsa da, atılmasa da
\PVerb!closeSession! çağrısının yapılması böylece garanti olacaktır.
\PVerb!Transaction! alâkalı çağrıların \PVerb!HibernateSession! vasıtası ile
yapıldığına dikkat ediniz. Diğer bazı Hibernate örneklerinde bir
\PVerb!Transaction! nesnesinin session'dan alınıp, üzerinde direk olarak
\PVerb!beginTransaction! ve \PVerb!commit! çağrılarının yapıldığını
görürüz. Biz, iki sebeple bunu yapmayarak, tüm \PVerb!Session! ve
\PVerb!Transaction! alâkalı işlemleri \PVerb!HibernateSession! üzerine aldık, çünkü
\begin{enumerate}
\item \PVerb!Session! ve \PVerb!Transaction! ile alakalı tüm işlemlerin tek
bir yerde, tek bir class üzerinden olması (kod idaresi ve temizliği
açısından).
\item \PVerb!Session! ve \PVerb!Transaction!'ın sadece bir thread'e bağlı
olmasını sağlamak
\end{enumerate}
\PVerb!Session! ve \PVerb!Transaction!'ı niye bir thread'e bağlamak istedik?
Çünkü içinde Hibernate işlemleri olan metotların birbirini zincirleme çağırması
gerektiğinde, her yeni çağrının yeni bir transaction ve session başlatmasını
engellemek istiyoruz. Bunun sebebi, kurumsal uygulamaların \%99'unda bir metot
diğer metotu çağırınca, her iki metotun da aynı transaction altında olmasının
çok yaygın olmasıdır. Örnek olarak, meselâ bir bankada iki hesabı arasında para
transferi yapan bir kullanıcının \PVerb!transferMoney! adlı bir çağrı yaptığını
düşünün:
\begin{lstlisting}[language=Java, frame=none]
AccountService service = ...
service.transferMoney(100, `22993002',`3399499'
\end{lstlisting}
Gereken class tanımları şöyle olsun:
\begin{lstlisting}[language=Java, frame=none]
public class AccountService {
public void transferMoney(double howMuch,
String accountFrom,
String accountTo) {
Account fromAccount = ...
Account toAccount = ...
fromAccount.subtract(howMuch);
toAccount.add(howMuch);
}
}
public class Account {
public void subtract(double howMuch) { .. }
public void add(double howmuch) { .. }
}
\end{lstlisting}
Bu kod'da görüldüğü gibi, bir hesaptan para eksiltmek ve yeni hesaba para
eklemek {\em aynı transaction altında} olmalıdır. Aksi takdirde \PVerb!subtract!
(eksiltme) işleminden sonra servis makinasının fişi çekilmiş olsa, ekleme
yapılmadan sistem çökmüş olacağı için para kaybolmuş olurdu! Bu yüzden
\PVerb!transferMoney! ile başlayan metot zincirinin tamamı aynı transaction
altında olmalıdır. Böylece bu transaction altındaki tüm işlemlerin ya hepsi {\em
başarısız} olur, ya da hepsi aynı anda {\em başarılı} olur.
İşte bu yüzden metot zinciri ile transaction sürekliliğini birbirine
bağlamalıyız. Bunu yapmanın en basit yolu ise, o anda işlemde olan thread ile
(\PVerb!Thread.currentThread()!) transaction'ı birbirine bağlamaktır. Bunu
\PVerb!HibernateSession! içinde \PVerb!ThreadLocal! kodlama kalıbını kullanarak
\cite[Sf. 301]{hibernatebook} yapabiliriz. Bu kalıp, o anki thread, Hibernate
\PVerb!Session! ve \PVerb!Transaction! arasında bir bağlantı
kurmaktadır. Böylece zincirleme birbirine çağrı yapan metotlar aynı
\PVerb!Thread! içinde olacağı için, aynı \PVerb!Transaction! ve \PVerb!Session!
kullanılabilmiş oluruz.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section[Kimlik İdaresi][KİMLİK İDARESİ]{Kimlik İdaresi}
İlişkisel veri tabanlarında bir satırı tekil (unique) olarak belirleyebilmek
için, anahtar görevini görecek ``bir ve ya daha fazla'' kolon tanımlaması
gerekir. Bu kolon(lar)a, ilişkisel (relational) veri tabanı dünyasında asal
anahtar (primary key) adı veriliyor. Tek bir satırı bulmak için SQL ile sorgu
yaparken \PVerb!WHERE! kısmında kullandığımız filtreleme kolonları, asal
anahtar kolonlarıdır.
Hibernate, bir tablodaki tek bir satırı tek bir nesneye eşlemek ister. Tablodaki
her kolon da, bir POJO üzerindeki öğe (attribute) ile eşlenecektir. Bu eşleme,
eşleme dosyasında \PVerb!property! kelimesi ile gerçekleştirilir. Fakat, asal
anahtarı oluşturan kolon ve onun eşlendiği nesne öğesi, ayrı bir şekilde
Hibernate'e bildirilmelidir. Çünkü Hibernate, bu öğeyi, diğerlerinden ayrı bir
şekilde, nesneyi bulma, tanımlamak için kullanılacak, yâni nesnenin tekilliğini
belirten bir öğe olacak kullanacaktır. Bu öğe, komut eşleme dosyasında
\PVerb!<id>! ile belirtilir. \PVerb!Car! örneğimizi ele alırsak:
\begin{lstlisting}[language=Java, frame=none]
<hibernate-mapping package="org.mycompany.kitapdemo.pojo">
<class name="Car" table="car">
<id name="licensePlate" column="license_plate">
<generator class="assigned"/>
</id>
<property name="description" column="description"/>
</class>
</hibernate-mapping>
\end{lstlisting}
Gösterilen \PVerb!Car! nesnesinde tekil anahtar, \PVerb!licensePlate! öğesidir,
onun eşlendiği kolon ise \PVerb!license_plate! kolonudur. \PVerb!<id
name="licensePlate"..>! komutunu kullanarak, Hibernate'e \PVerb!Car! nesnesinin
tekilliğini \PVerb!licensePlate! öğesi olduğunu bildirmiş olduk. Bu öğe,
\PVerb!Car! nesnesini veri tabanından yüklerken \PVerb!get! komutuna anahtar
olarak verilecek öğe olacaktır.
\begin{lstlisting}[language=Java, frame=none]
Car car = (Car) s.get(Car.class, "34 TTD 2202");
\end{lstlisting}
\subsection{Birden Fazla Kolon Anahtar İse}
Bazen bir tablodaki asal anahtar tek kolon değil, birden fazla kolon
olabilir. Bu durumda (SQL dünyasında) bir satırı tekil olarak bulabilmek için
asal anahtarı oluşturan tüm anahtarları \PVerb!WHERE! komutuna veririz.
Hibernate dünyasında, bir nesneyi bulmak için birden fazla öğe kimlik olarak
gerektiğinde, eşleme dosyasının anahtar belirtilen bölümünde \PVerb!<id>! yerine
\PVerb!<composite-id>! komutunu kullanmak gerekecektir. \PVerb!Person! örnek
class'ında bu tekniği görelim:
\begin{lstlisting}[language=XML,caption=Person.hbm.xml]
<class name="Person" table="person">
<composite-id name="id" class="Person\$Id">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
....
</class>
\end{lstlisting}
\begin{lstlisting}[language=Java,caption=Person.java]
package org.mycompany.kitapdemo.pojo;
public class Person {
public Person() { }
Id id;
public Id getId() {
return id;
}
public void setId(Id newId) {
this.id = newId;
}
/**
* Inline Id class
*
*/
public static class Id implements Serializable {
public Id(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public Id() {}
String firstName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String newFirstName) {
this.firstName = newFirstName;
}
String lastName;
public String getLastName() {
return lastName;
}
public void setLastName(String newLastName) {
this.lastName = newLastName;
}
}
// diğer öğeler ve onların get/set
// çağrıları buraya
// ...
}
\end{lstlisting}
Örneğimizde \PVerb!Person! asal anahtarı, \PVerb!firstName! (isim) ve
\PVerb!lastName! (soyisim) ikilisinden oluşmaktadır. Hibernate'e bu ikiliyi
bir asal anahtar olarak vermemiz için, \PVerb!firstName! ve \PVerb!lastName!'i
içeren \PVerb!Id! isminde yeni bir \textbf{iç class} (inner class) yaratmamız
gerekti. Bu class, daha sonra \PVerb!composite-id! komutuna parametre olarak
geçilmiştir.
\begin{quote}
\textbf{Not}: Kod idaresi açısından, \PVerb!Id! class'ını \PVerb!Person! içine
koyarak bir iç class hâline getirdik, ama \PVerb!Id!, apayrı bir class olarak
kendi \PVerb!.java! dosyasında da olabilirdi. Kodun düzenli olması bakımından
\PVerb!Person! ile alâkalı her şeyin tek yerde olmasını istediğimiz için, bu
kodlama şekilde daha faydalı bulduk.
\end{quote}
Eğer bir \PVerb!Person! nesnesini \PVerb!get! ile yüklemek istiyorsak, bir iç
class \PVerb!Id! nesnesi yaratmalı ve nesneyi parametre olarak \PVerb!get!
komutuna geçmeliyiz.
\begin{lstlisting}[language=Java, frame=none]
Session session = HibernateSession.openSession();
Person.Id id = new Person.Id(``ahmet'', ``yılmaz'');
Person p = (Person) session.get(Person.class, id);
...
\end{lstlisting}
\subsection{Anahtar Üretimi}
Anahtarlar konusunda diğer önemli bir husus, anahtar ``atama sorumluluğun'' kimde
olduğudur. Bazı uygulamalarda asal anahtar, verinin kendi içinden doğal olarak
çıkar. Meselâ veri operatörleri \PVerb!person! tablosuna veri eklerken, kişi
isim ve soyadına gireceklerdir, ve bunu yaparken bir asal anahtarı da otomatik
olarak eklemiş olacaklardır. Çünkü \PVerb!person! için asal anahtar,
\PVerb!firstName! ve \PVerb!lastName! kolonlarının bir kombinasyonudur.
Fakat bazı durumlarda asal anahtar verinin içinden çıkmayabilir. Bu şartlarda
her satırı tekil olarak kimlikleyebilmek için bir asal anahtar {\em üretmek}
gerekecektir.
Anahtar üretimi (ya da üretilmemesi gerektiğini) Hibernate'e \PVerb!<id>!
etiketi altında \PVerb!<generator! \PVerb!class=".."/>! komutu ile
tanımlıyoruz. Eğer bir anahtar verinin içinden doğal olarak çıkıyorsa, hiçbir
anahtar üretim eyleminin yapılmaması gerektiği Hibernate'e \PVerb!<generator
class="assigned"/>! ile bildirilir. Eğer ID'nin üretilmesi gerekiyorsa, en basit
yöntem olarak \PVerb!<generator class="increment"/>! kullanılır. Bu yöntem,
\PVerb!Integer! bazındaki bir ID öğesinin (ve kolonunun) her yeni değerini bir
öncekinden bir fazla olarak hesaplar ve tabana otomatik olarak
yazar. \PVerb!Increment! ile tanımlanan nesne öğesinin \PVerb!java.lang.Integer!
olması gerektiğini bir daha vurgulayalım.
Eğer id üretimini veri tabanına bırakmak istiyorsanız, Hibernate de
\PVerb!<generator class="native"/>! kullanabilirsiniz. Bu seçim ile, meselâ MySQL
üzerinde auto-increment id üretim yöntemi, Oracle üzerinde ise sequence ile id
üretimi yöntemi kullanılacaktır\footnote{Hibernate native tekniği kullanılınca
akılda tutulması gereken önemli bir nokta, Hibernate 2.0 versiyonunda native
kullanımıyla beraber unsaved-value=``0'' tanımının id etiketi içinde
kullanılmasıdır. Hibernate 3.0 ile bu tanımın yapılması gerekli değildir; Zaten
Kitabımız Hibernate 3.0 versiyonu baz alınarak yazılmıştır}.
ID üretmek için diğer yöntemler de mevcuttur. Bu ek metotları Hibernate sitesi
\url{www.hibernate.org} adresinden, ya da Hibernate yaratıcılarının kitabından
\cite{hibernatebook} bulabilirsiniz.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section[Dört İşlem][DÖRT İŞLEM]{Dört İşlem} \label{hibernate:crud}
Hibernate'i kurup, \PVerb!HibernateSession! üzerinden veri tabanına erişmeyi
hallettikten sonra, artık dört işlem ile tek bir nesneyi eklemeye, okumaya,
değiştirip yazmaya ve gerekirse silmeye hazırız. Bu işlemlere veri tabanı
dünyasında CRUD (\textbf{CR}eate \textbf{U}pdate \textbf{D}elete) işlemleri ismi
verilmektedir. Her ne kadar önceki bölümlerde Hibernate ile dört işlem kısmen
anlatılmış olsa da, toplu bir özeti bu bölümde vermeye çalışacağız.
Elimizde bir kimlik değeri var ve bu kimlik ile bir nesneyi bulup hafızaya
getirmek istiyorsak, bunu yapmak için \PVerb!Session! üzerinden \PVerb!get!
komutunu kullanırız.
\begin{lstlisting}[language=Java, frame=none]
Car car = (Car) s.get(Car.class, "34 TTD 2202");
\end{lstlisting}
Bir nesneyi bulup yükledikten sonra, onun üzerinde veri değişikliklerini,
nesnenin \PVerb!set! metotlarını çağırarak gerçekleştirebiliriz.
\begin{lstlisting}[language=Java, frame=none]
...
car.setDescription(``yeni araba'');
\end{lstlisting}
Fakat bu değişiklikler hâla veri tabanına yansımış değildir. Bu son aşamayı da
tamamlamak için, \PVerb!s.saveOrUpdate! metotunu çağırmamız gerekiyor.
\begin{lstlisting}[language=Java, frame=none]
...
s.saveOrUpdate(car);
\end{lstlisting}
Değişik bir senaryo, bir nesneyi yüklemeden, yeni bir nesneyi sıfırdan yaratıp
bu nesneyi veri tabanına ekleme senaryosudur. Bu senaryo için, \PVerb!new Car()!
kullanarak yeni nesneyi yaratıp, üzerinde aynı \PVerb!saveOrUpdate! çağrısını
yapmamız gerekir. \PVerb!saveOrUpdate! metotu akıllı bir metottur; Kendisine
verilen \PVerb!car! nesnesinin içine (verisine) bakarak, bu nesnenin yeni mi,
yoksa eskinin güncellenmiş hâli mi olup olmadığını anlama yeteneğine
sahiptir. Böylece aynı \PVerb!saveOrUpdate! çağrısı ile hem güncelleme hem
ekleme işlemini yapabiliyoruz, ve bu çağrıyı yapan uygulama kodlarımızın
nesnenin yeni mi eski mi olduğunu bilmesi gerekmiyor.
Bir nesneyi (ve o nesnenin eşlenmiş olduğu veri satırını) veri tabanından silmek
için, \PVerb!Session! üzerinde \PVerb!delete! çağrısı yapılır. Bu çağrıdan önce,
\PVerb!get! ile nesnenin yüklenmiş olması gerekmektedir (tabii perde arkasında
Hibernate gereken \PVerb!SELECT! işlemini sonraya bırakarak ekstra
\PVerb!SELECT!'ten bizi kurtarır). Bu kullanım şöyledir:
\begin{lstlisting}[language=Java, frame=none]
Car car = (Car) s.get(Car.class, "34 TTD 2202");
s.delete(car);
\end{lstlisting}
Tabii bu işlemlerden önce ve sonra \PVerb!HibernateSession! yardımcı nesnesi
üzerinden session alıp, bir transaction başlatıp bitirmeyi hatırlamamız
gerekiyor (bkz. \ref{hibernate:transaction}). Ancak bir transaction commit
edildikten sonra yaptığımız değişiklikler veri tabanına yansıyacaktır.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section[Nesneler Arası İlişkiler][NESNELER ARASI İLİŞKİLER]{Nesneler Arası İlişkiler}
Hibernate gibi bir Nesne-İlişkisel Eşleme (Object Relational Mapping -
\textbf{ORM}) aracının belki de en önemli faydalarından biri, iki tablo
arasındaki yabancı anahtarlar (foreign keys) üzerinden kurulmuş bir ilişkiyi,
direk nesnesel bir ilişki şekline dönüştürebilmesidir. Bunu yapabilmek için,
doğal olarak, eşleme dosyasında gerekli ayarları programcı verecektir. Fakat bu
komutlar verildikten sonra Hibernate, arka planda bir tablodan ötekine, yâni bir
nesneden ilişkili olduğu diğer nesneye atlamamızı sağlayacak SQL komutlarını
otomatik olarak üretecektir! Programcıya, Java seviyesinde sadece
\PVerb!nesne.getOtherObjectList()! gibi bir çağrı yapmak kalacaktır.
İki nesne (ya da tablo) arasında, nicelik (cardinality) açısından değişik 3 tür
ilişki olabilir. Tek nesne A'nın tek nesne B ile birebir, tek nesne A'nın birçok
B nesnesi ile bireçok, ve birçok A nesnesinin birçok B nesnesi arasında çokaçok
ilişkileri. Bu ilişki çeşitlerini ve Hibernate eşleme dosyasında nasıl temsil
edileceklerini teker teker görelim.
\subsection{Bire Bir (One to One) İlişki}
Veri tabanında tek bir satırın diğer tek bir satıra işaret etmesi, bire bir
türünden bir ilişkidir. Hibernate bu şekilde bir ilişkiyi, \PVerb!<many-to-one>!
etiketi ile destekler.
Bire bir ilişkisi, veri tabanı seviyesinde bir ya da daha fazla kolon üzerinden
gerçekleştirilebilir. Eğer işaret `edilen' tabloyu tekil olarak belirlemek için
birden fazla kolon gerekiyorsa, işaret eden taraf asal anahtarı oluşturan bu
kolonların hepsini kendi üzerinde taşımak zorundadır. Meselâ, asal anahtarı
\PVerb!name! ve \PVerb!person_lastname!'den oluşan \PVerb!person! tablosuna
işaret eden herhangi bir POJO eşleme tanımı, şunları içermek zorundadır.
\begin{lstlisting}[language=Java, frame=none]
<many-to-one name="person" class="Person" not-null="true">
<column name="name"/>
<column name="person_lastname"/>
</many-to-one>
\end{lstlisting}
POJO class'ı içinde, \PVerb!person! ilişkisine tekabül eden \PVerb!get! ve
\PVerb!set! metotlarını eklemeliyiz.
\begin{lstlisting}[language=Java, frame=none]
..
Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person newPerson) {
this.person = newPerson;
}
..
\end{lstlisting}
Bu ilişki örneğini \PVerb!HibernateComposite! projesinde bulabilirsiniz.
\begin{figure}[!hbp]
\center{
\scalebox{0.4}{
\includegraphics{./images/person_car_birebir.eps}
}
}
\caption{Person Car Bire Bir İlişkisi}
\end{figure}
\subsection{Bire Çok (One to Many) İlişki}
Kurumsal uygumalarda en çok lâzım olan ilişki türü, bire çok türden
ilişkidir. Örnek olarak gerçek hayattan iki nesneyi alalım: \PVerb!Garage!
(garaj) ve \PVerb!Car! (araba). Diyelim ki bir garajda birçok araba
durabilir. \PVerb!Garage! ve \PVerb!Car! arasındaki bu türden bir nicelik
ilişkisine, bire çok türden ilişki diyoruz. Bu ilişkide, tek \PVerb!Garage!
nesnesi (\PVerb!garage! tablosundaki tek bir satır) birçok \PVerb!car! nesnesi
ile (\PVerb!car!'da birçok satır) arasında bir ilişki olacaktır. Veri tabanında
bu ilişkiyi kurmanın yolu, bire çok ilişkinin \textbf{çok} tarafına,
\textbf{bir} tarafındaki asal anahtarı alıp, yabancı anahtar olarak
yerleştirmektir. Bu yapılınca, meselâ \PVerb!garage.id = 1! filtresi ile, 1
no'lu \PVerb!Garage! ile ilişkide olan tüm \PVerb!car! kolonlarını bulmak
mümkündür.
\begin{lstlisting}[language=Java, frame=none]
select * from car where garage_id = 1
\end{lstlisting}
ile ilişkideki tüm arabalar bulunabilir.
\begin{figure}[!hbp]
\center{
\scalebox{0.4}{
\includegraphics{./images/garage_car_bire_cok.eps}
}
}
\caption{\label{hibernate:iliskiler:garagecarbirecok} Garage ve Car Bire Çok
İlişkisi}
\end{figure}
\subsubsection{Ayarlar}
Hibernate'e bire çok türünden ilişkiyi yansıtmak için eşleme dosyasında
\textbf{bir} tarafında \PVerb!<set>! komutu kullanıyoruz. İçinde araba
nesnelerini tutacak olan bir \PVerb!java.util.Set! listesini de, \PVerb!Garage!
kodu üzerine yeni bir öğe olarak ekleyeceğiz. Örnek \PVerb!Garage.hbm.xml! ve
\PVerb!Car.hbm.xml! dosyalarını altta gösteriyoruz.
\begin{lstlisting}[language=XML,caption=Garage.hbm.xml]
<hibernate-mapping package="org.mycompany.kitapdemo.pojo">
<class name="Garage" table="garage">
<id name="garageId" column="id">
<generator class="increment"/>
</id>
...
<set name="cars"
inverse="true"
cascade="save-update, all-delete-orphan">
<key>
<column name="garage_id"/>
</key>
<one-to-many class="Car"/>
</set>
</class>
</hibernate-mapping>
\end{lstlisting}
\begin{lstlisting}[language=XML,caption=Car.hbm.xml]
<hibernate-mapping package="org.mycompany.kitapdemo.pojo">
<class name="Car" table="car">
<id name="licensePlate" column="license_plate">
<generator class="assigned"/>
</id>
...
<many-to-one name="garage"
column="garage_id"
class="Garage"
not-null="true"/>
</class>
</hibernate-mapping>
\end{lstlisting}
Java kodları da şöyle olacaktır.
\begin{lstlisting}[language=Java,frame=none]
public class Garage {
...
Set cars;
public Set getCars() {
return cars;
}
public void setCars(Set newCars) {
this.cars = newCars;
}
..
}
\end{lstlisting}
\begin{lstlisting}[language=Java,frame=none]
public class Car {
..
Garage garage;
public Garage getGarage() {
return garage;
}
public void setGarage(Garage newGarage) {
this.garage = newGarage;
}
..
}
\end{lstlisting}
\begin{quote}
\textbf{Not}: Hibernate eşleme ve Java seviyesindeki tip uyumları çok katı olarak
kontrol edilir; Meselâ eşlemede \PVerb!<set>! kullanıp POJO üzerinde
\PVerb!java.util.List! kullansaydınız (ki bu yanlış olurdu), Hibernate
uygulamanız başladığında bu hatayı bulacaktır.
\end{quote}
Artık bu eşleme üzerinden bir nesneden diğerine pür Java çağrıları kullanarak
geçebiliriz.
\begin{lstlisting}[language=Java, frame=none]
Set cars = garage.getCars();
\end{lstlisting}
\subsubsection{Cascade}
\PVerb!<set>! tanımı parçası olan \PVerb!cascade="all-delete-orphan"!, bire çok
ilişkiyi daha da güçlendirecek bir tanımdır. Cascade kelimesi İngilizce'de
``olayların önce birinin, sonra ötekisi olacak şekilde, sırayla olması''
anlamına gelir. \PVerb!all-delete-orphan! işleminin cascade şeklinde olmasının
istenmesi demek, ilişkinin \textbf{bir} tarafındaki nesne {\em silindiği} zaman
(\PVerb!delete!), bu silme işleminin \PVerb!<set>! içindeki tüm nesnelere etki
etmesi, yani ilişkide olan {\em diğer nesnelerin de silinmesi} anlamına
gelir. Bu çok güçlü bir ilişkidir, ve gerçek dünya uygulamalarının veri
modellerindeki her bire çok ilişki için uygun değildir. Sadece örnek amaçlı
olarak \PVerb!Garage! ile \PVerb!Car! arasında böyle bir ilişki kurduk, yoksa,
{\em normâl} fiziksel dünya şartlarında bir \PVerb!garaj! silinince içindeki
arabaların silinmesi yanlış olurdu.
\PVerb!Cascade! için kullanılmış diğer seçenek \PVerb!save-update!,
\PVerb!Garage!'da alınan listeye \PVerb!garage.getCars().add(car)! ile yeni bir
nesne eklenir eklenmez, bu eklemenin veri tabanına yansıtılmasını sağlar;
Böylece yeni \PVerb!Car! nesnesi üzerinde ayrıca bir
\PVerb!session.save! çağrısı yapılmasına gerek kalmaz.
\subsubsection{Eklemek}
İki nesne arasında bire çok ilişki kurulduğu zaman, eğer çok tarafındaki
nesnelerden tabana bir tane daha eklemek istersek, bunu bir tarafındaki nesneden
çok tarafı nesnesinin listesini alıp, o liste üzerinde ekleme yapmakla
halletmemiz gerekiyor. Ayrıca, yeni eklenen nesne üzerinde, geriye, bir
tarafındaki nesneye işaret eden referansa doğru referansı set etmeliyiz. Bir
\PVerb!Car! eklemek için alttaki örnek kodlar kullanılacaktır.
\begin{lstlisting}[language=Java, frame=none]
HibernateSession.beginTransaction();
Garage garage = (Garage) s.get(Garage.class, new Integer(1));
Car car = new Car();
car.setLicensePlate("falanfilan");
car.setDescription("falanfilan");
garage.getCars().add(car);
car.setGarage(garage);
HibernateSession.commitTransaction();
\end{lstlisting}
Fakat üzerine ekleme yaptığımız \PVerb!java.util.Set!, temel Java tiplerinden
biridir, o zaman üzerine ekleme yaptığımız anda Hibernate'in bu ekleme
işleminden nasıl haberi olmaktadır? CGLIB sayesinde: Bir eşleme dosyası ile
POJO'larımızı Hibernate'e tanıttığımızda, bu POJO'lar üstünde kullanılan