Java Chapter 6
Java Chapter 6
Dolayısıyla DAO katmanında yer alan sınıflar veri tabanındaki yapıyla neredeyse çok
benzerlik göstermektedir. Çünkü, bu sınıflar veri tabanındaki veri modelini Java tarafında
modeller, yani bir bakıma veri tabanındaki veri modelinin eşleniği gibidir. İşte Java’daki
sınıflar ile veri tabanındaki tablolar arasındaki çift taraflı dönüştürülme işine ORM (Object
Relational Mapping) denir.
Aşağıdaki Java sınıfı ile ilişkisel veri tabanındaki tabloya ait DDL SQL komutunu incelediğiniz
de aslında her ikisinin de aynı varlığı aynı nesneyi modellediğini görebiliriz.
Java Kodu
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.first_name = fname;
this.last_name = lname;
[Link] = salary;
}
SQL Komutu
Görüldüğü gibi veri yapısı bütünüyle neredeyse aynıdır. Bu aslında tablodan nesneye,
nesneden tabloya otomatik bir dönüşüm yapılabileceğini gösterir.
ORM’nin Avantajları
- Java uygulaması içinde veri tabanı tablolarıyla direkt olarak SQL vasıtasıyla etkileşim
yerine bu tabloların Java’da karşılığı olarak yaratılmış nesneleriyle çalışmak hem
Nesneye Dayalı Programlama pratiğine daha uygundur.
- SQL çalıştırmak için gerekli olan birçok detayından bizi kurtarır. Daha basit şekilde
veri tabanı ile etkileşim kurarız. Bu etkileşimi Hibernate gibi ORM araçları bizim
sorumluluğumuzdan alır kendileri üstlenirler.
- ORM kütüphaneleri, dolayısıyla Hibernate alt yapı olarak JDBC API’yi kullanır.
- Hibernate ile veri tabanından bağımsız yeniden kullanılabilir kodlar yazılabilir.
Örneğin MySQL veri tabanından PostgreSQL veri tabanına geçişte kodlarınızda bir
değişiklik gerektirmez.
- Transaction yönetimi Hibernate tarafından otomatik olarak yürütülebilir.
- En önemlisi JDBC kullanılan projelerde projeye büyüdükçe iyice karmaşık bir kod
mimarisi oluşabilir. Yazılımcı kodladığı iş mantığı dışında bu durumlar içinde kod
düzeltmesi ve zaman harcamak zorunda kalır. Hibernate ile kurumsal projelerde
karmaşıklık azaltılır. Yazılımcının yazdığı iş mantığına odaklanması sağlanır. Hızlı bir
geliştirme yapmaya imkan verir.
Hibernate
2001 yılında ortaya çıkmıştır. Dediğimiz gibi görevi Java’daki veri tipleriyle, SQL veri tiplerinin
birbirine dönüşümünü sağlayarak Java sınıflarını veri tabanı tablolarına eşleştirir.
JDBC API da bildiğimiz üzere veri tabanı bağımsız bir şekilde çalışmayı sağlıyordu. JDBC ile
Java’da yazdığımız kodlar veri tabanı yönetim sisteminin ne olduğundan bağımsız bir şekilde
esnek yapıya sahipti. MySQL ile çalışan bir JDBC uygulaması yazdığınızda veri tabanı yönetim
sisteminizi PostgreSQL’e veya Oracle’e çevirdiğiniz de çok ta sorun yaşanmıyordu. Önceden
yazılmış olan JDBC kodları değişikliğe uğramıyordu. Fakat, JDBC ve veri tabanı arasında
Nesneye dayalı Programlama’nın ihtiyacı olan boşluğu dolduracak daha soyut bir ara katman
gerekiyordu. Hibernate ile bu katman sağlanmış oldu. Bugün bir çok kurumsal şirkette
Hibernate tercih edilmektedir. Hatta, JDBC ile Hibernate’in hibrit olduğu projelerde
mevcuttur. Not olarak belirtmekte fayda var, Hibernate üzerinden yalın SQL komutları da
çalıştırabiliyoruz.
Hibernate çok katmanlı bir mimariye sahiptir. Böylece, alt yapısında kullandığı kütüphaneleri
ve gereksinimleri yazılımı geliştiren kişinin bilmesine gerek bırakmaz. İyi izolasyon ve
soyutluluk sağlar.
Configuration Nesnesi
Hibernate uygulaması ayağa kalkarken bir kere oluşturulur. Uygulama çalıştığı süre boyunca
kullanılır. Configuration nesnesini doldurmak için bir konfigürasyon dosyası tanımlanır.
SessionFactory Nesnesi
Her veri tabanı bağlantı konfigürasyonu için aynı bir SessionFactory nesnesi oluşturulur.
Yani, Java uygulamamız hem MySQL’e hem de PostgreSQL veri tabanlarına erişmek isterse
bunu tek bir SessionFactory nesnesi üzerinden yapamaz. Bu nedenle her iki veri tabanı için
ayrı SessionFactory nesnesi oluşturulur.
Session Nesnesi
SessionFactory nesnesi kullanılarak Session nesnesi üretilir. Session nesnesi veri tabanıyla
olan kullanılabilir bağlantıyı ifade eder. Session nesnesi ile veri tabanı işlemlerini
bitirdiğimizde kapatmak gerekir. Session nesnesi ThreadSafe değildir. Session’la işimiz
bittiğinde kapatmakta fayda vardır.
Transaction Nesnesi
Transaction nesnesi JDBC ile JTA (Java Transaction API) alt yapısını kullanarak ilişkisel veri
tabanlarında Transaction yönetimini sağlar.
Query Nesnesi
Veri tabanı tablolarında veri sorgulamak, silmek, güncellemek ve veri eklemek gibi işlemleri
yapabilmesi sağlayan nesnedir. SQL ve HQL (Hibernate Query Language) dilleriyle veri
üzerinde işlem yapabilmeyi sağlar.
Criteria Nesnesi
Nesneye dayalı programlama yöntemiyle sorgu yazmadan, sorgu nesnelerini bir araya
getirerek veri tabanı tablolarında işlem yapabilmeyi sağlar. Query nesnesinin alternatifi
olarak kullanılabilir.
[Link]
Seçilmiş olan veri tabanı yönetim sistemine göre SQL üretmeyi sağlar.
[Link].driver_class
[Link]
[Link]
[Link].pool_size
Veri tabanı bağlantı havuzunun maksimum kaç bağlantı nesnesini tutabileceğini belirtir.
Biliyorsunuz Pooling diye bir tasarım deseni vardı. Böylece, kaynakların verimli kullanılmasını
sağlamış oluyorduk.
[Link]
Autocommit özelliği açılıp kapatılabilir. Autocommit açık olursa veri üzerinde yapılan
değişiklik otomatik olarak hemen veri tabanına yansır. JDBC'de de bunun örneğini yapmıştık.
Hibernate Entity sınıfları veri tabanı tarafındaki tabloları modelleyen Java sınıflarıdır. Java
uygulamasında kodlama yaparken bu sınıf tablo işlevini görecektir. Bu Entity sınıflarından
üretilen her nesne veri tabanı tablosunda bir kaydı ifade edecektir. Tablodaki her satırın
Java’da temsili bu sınıflara ait nesnelerdir.
Örnek Entity
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue
@Column(name = "emp_no")
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "salary")
private int salary;
public Employee() {}
Yukarıdaki örnekte “Employee” isminde bir sınıf oluşturup, veri tabanı tarafında
“EMPLOYEE” isimli tabloyu modelliyoruz. Böylece, Hibernate bu Entity sınıfını gördüğünde
bunun veri tabanında bir tablo ile temsil edildiğini anlayacaktır.
Sonrasında Java sınıfı içindeki değişkenlerimizin tabloda hangi sütunlara denk geldiğini
işaretliyoruz. Bunu yaparken @Column annotation’ı kullanıyoruz.
Hibernate’in kendine ait bir sorgulama dili vardır. Bu sorgulama diliyle yazılanlar en
nihayetinde veri tabanına iletilirken SQL cümlelerine çevrilirler.
HQL veya yalın SQL kullanarak sorgulama yapmak istemeyenler için alternatif olarak Criteria
Query API kullanarak tablolarla etkileşeme geçen kodlar yazabililer.
Criteria cr = [Link]([Link]);
List results = [Link]();
Criteria cr = [Link]([Link]);
[Link]([Link]("salary", 2000));
List results = [Link]();
Criteria cr = [Link]([Link]);
Criteria cr = [Link]([Link]);
Hibernate kütüphanesinde HQL ve Criteria Query dışında direkt olarak yalın SQL komutları
da işletebiliriz.
Hibernate kütüphanesinin bir ORM aracı olduğundan bahsetmiştik. ORM araçları ile veri
tabanı ile etkileşim sorumluluğunu devretmiş oluyoruz. Aynı zamanda veri tabanımızdaki
tabloları Java projesinde modelleyen sınıflar, ki biz bunlara Entity sınıfları diyoruz, elde etmiş
olduk. Böylece, veri tabanındaki veriyi Java tarafında modellemiş olduk. Bu sayede veri
tabanı sağlayıcımız değişse de ORM aracıyla yazdığımız kodlarda değişiklik yapmaya gerek
kalmayacaktı.
Hibernate kütüphanesi de Spring Boot projelerine dahil edilip kullanılabilir. Hatta, projelerde
ORM aracı olarak Hibernate tercih edilir. Spring Data kütüphanesi bu entegrasyonu sağlayan
kütüphanedir. “spring-boot-starter-data-jpa” kütüphanesi ile Hibernate kütüphanesini
projemize dahil etmiş oluruz.
<project xmlns="[Link]
xmlns:xsi="[Link]
xsi:schemaLocation="[Link]
[Link]
<modelVersion>4.0.0</modelVersion>
<groupId>chapter5-spring-hibernate</groupId>
<artifactId>chapter5-spring-hibernate</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>company-project</name>
<description>Company Project</description>
<packaging>jar</packaging>
<properties>
<[Link]>8</[Link]>
<[Link]>1.2.7</[Link]>
<[Link]>1.8.67</[Link]>
<[Link]>1.9.4</[Link]>
</properties>
<parent>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>[Link]</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>[Link]</groupId>
<artifactId>junit-vintage-
engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-data-
jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>[Link]</groupId>
<artifactId>commons-text</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<finalName>company-project</finalName>
<plugins>
<plugin>
<groupId>[Link]</groupId>
<artifactId>spring-boot-maven-
plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Ardından ilk işimiz veri tabanı ile konuşacak olan DAO katmanını şekillendirmek. Ama biz
istiyoruz ki Hibernate konfigürasyonumuzu kendimiz yapalım. Bu nedenle
“[Link]” Java paketi oluşturup paket altında “HibernateConfiguration”
isminde Java sınıfı oluşturuyoruz.
@Configuration
@Order(1)
@PropertySource({ "classpath:[Link]" })
public class HibernateConfig {
Yukarıdaki sınıfımız @Configuration etiketi ile işaretlendiği için Spring Framework tarafından
konfigürasyon sınıfı olarak algılanacaktır. Ayrıca, @Order(1) etiketi ile eğer projede birden
fazla konfigürasyon sınıfı varsa ilk önce bu konfigürasyon çalışmasını sağlıyoruz. @Order
etiketi ile sıralamayı belirleyebiliyoruz. @PropertySource etiketi ise Java projesinde yer alan
Resource dosyalarından belirttiğimiz dosyayı alıp okuyup “HibernateConfig” sınıfında
kullanabilmemizi sağlar. “[Link]” dosyasından bahsetmiştik. Şimdi bu
özellikler dosyasını dolduralım.
“src/main/resources” klasörü altına “[Link]” isminde bir dosya
oluşturuyoruz. Bu dosya anahtar-değer ikilisi şeklinde veriler saklayacaktır. Bu dosyanın içine
veri tabanı bağlantı bilgileri ve Hibernate ile ilgili özellikler tanımlanacaktır.
[Link]=jdbc:mysql://[Link]/S9HHYQdP81?useSSL=f
alse
[Link]=S9HHYQdP81
[Link]=7mR2jSrEgT
[Link]=[Link]
Görüldüğü üzere bağlantı URL’imiz mevcut ve JDBC örneğimizdeki gibi aynı formatta
olacaktır. Ardından, username ve password bilgilerini giriyoruz. Biliyoruz ki veri tabanı
bağlantısı için bir kullanıcı adı ve şifre gerekiyor. Ve hangi veri tabanı sürücüsünü
kullanacağımı belirtiyorum.
@Autowired
private Environment env;
@Bean
public DataSource dataSource() {
return dataSource;
}
“DriverManagerDataSource” tipinden bir nesne üretip bunu @Bean etiketiyle Spring Bean’i
olarak tanımlıyorum. Böylece, Spring Framework artık bu nesneyi Spring Context’e ekleyip
bir bağımlılık nesnesi olarak sunabilecektir. Veri tabanı bağlantısıyla ilgili verileri
“[Link]” içinden alıp nesneye tek tek setliyoruz. “[Link]”
dosyasındaki verilere erişmemizi sağlayacak “Environment” tipinde bir bağımlılık ekliyoruz.
@Autowired etiketi ile bağımlılığı sınıfa dahil ettik. “Environment” sınıfı Spring
Framework’ün bize sağladığı bir sınıftır. İçinde “[Link]” dosyasındaki
değerleri barındırır. Neden “[Link]” olduğunu nasıl anladı derseniz,
hatırlayacaksınız ki @PropertySource isimli etiket ile hangi Resource dosyasını okuyacağımızı
belirtmiştik.
Şimdi Hibernate ile ilgili konfigürasyonları yapmaya sıra geldi.
@Bean
public EntityManagerFactory entityManagerFactory() throws SQLException {
“EntityManagerFactory” nesnesi veri tabanı için bağlantı bilgileri kapsar. Ayrıca, Entity ve
veri tabanı tablolarına ait eşleştirmeyi tutar. Biliyoruz ki @Entity etiketiyle işaretlenmiş bir
sınıf veri tabanı tarafında bir tablo ile eşleştirilir. O tablonun Java tarafında bir sınıf modelini
temsil eder. Bu nedenle “EntityManagerFactory” aynı zamanda Entity sınıflarının hangi Java
paketi altında olduğunu bilmek ister. Bu nedenle Entity sınıflarının bulunduğu Java paketini
“EntityManagerFactory” nesnesine “setPackagesToScan” metoduyla setliyoruz.
Ayrıca Hibernate ile ilgili diğer ek özellikleri de “setJpaProperties” fonksiyonu ile setliyoruz.
Ardından, Hibernate Entity sınıflarının hangi paket altında olduğunu belirtmemiz gerekiyor.
“[Link]” paketi altına Hibernate Entity sınıflarını tanımlayacağız.
Bu nedenle paket ismini veriyoruz.
@Bean
@Primary
public SessionFactory getSessionFactory() throws SQLException {
if ([Link]([Link]) == null) {
throw new NullPointerException("factory is not a hibernate
factory");
}
return [Link]([Link]);
}
Böylece Hibernate kütüphanesini Spring Framework ile konfigüre edip kullanıma hazır hale
getirdik.
@Entity
@Table(name = "employees")
public class Employee implements Serializable{
@Id
@Column(name = "emp_no")
private Long empNo;
@Column(name = "first_name")
private String name;
@Column(name = "last_name")
private String lastName;
@Column(name = "gender")
private String gender;
@Column(name = "birth_date")
@Temporal([Link])
private Date birthDate;
@Column(name = "hire_date")
@Temporal([Link])
private Date hireDate;
@OneToMany(mappedBy = "employee", cascade = [Link], fetch
= [Link])
private List<Salary> salaries;
@Component
public class EmployeeDAO
{
@Autowired
private SessionFactory sessionFactory;
return [Link]();
}
return [Link]();
}
return [Link]();
}
@Service
public class EmployeeService {
@Autowired
private EmployeeDAO employeeDAO;
return [Link]();
}
return [Link]();
}
Spring Framework ile Java konsol uygulaması çalıştırmak için sınıfın “CommandLineRunner”
interface’den kalıtım alması gerekmektedir. Bunun için “ConsoleApplication” isminde bir
sınıf tasarlayıp bu interface’den kalıtım almasını sağlıyoruz.
@Component
public class ConsoleApplication implements CommandLineRunner {
@Autowired
private EmployeeService employeeService;
@Autowired
private TitleService titleService;
// Java codes
}