7 Ekim 2010 Perşembe

Ubuntu üzerine Oracle Client kurulumu ve SQL*Plus kullanımı

Bu yazımda sizlerle Oracle Client ve SQL*Plus arabiriminin Ubuntu 10 Server üzerine nasıl kurulduğunu/düzenlendiğini anlatacağım. Burdaki kurulum işlemi Oracle 10.2 sürümü içindir.

Paket yüklemeleri

1) Ubuntunun depolarında OLMAYAN kurulum paketini buradan indiriyoruz.
2) dpkg -i oracle-xe-client_10.2.0.1-1.0_i386.deb ile indirdiğimiz paketi kaynak listesine ekliyoruz.
3) sudo apt-get update ile paketleri güncellemiş oluyoruz (yeni eklediğimiz paket için zorunlu olarak yapmamız gerekiyor)
4) sudo aptitude search oracle komutunu çalıştırdığımızda dönecek olan listede oracle-xe-client olacak.
5) sudo apt-get install oracle-xe-client komutu ile programı kurabiliriz artık.

~/.profile dosyasının içerisine aşağıdaki satırları ekleyelim

export ORACLE_HOME=/usr/lib/oracle/xe/app/oracle/product/10.2.0/client
export NLS_LANG=`$ORACLE_HOME/bin/nls_lang.sh`
export SQLPATH=$ORACLE_HOME/sqlplus
export PATH=$ORACLE_HOME/bin:$PATH


Bağlantı ayarlarının yapılması

/etc/tnsnames.ora dosyasını oluşturup içine tns ayarlarını girelim.

Örn.:
10GTEST =
(DESCRIPTION =
(ADDRESS =
(PROTOCOL = TCP)
(HOST = 192.168.91.171)
(PORT = 1521)
)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = 10GTEST)
)
)

şimdi bilgisayarı kapatıp açalım.

SQL*Plus arabiriminin konsoldan çalıştırılması

sqlplus 10GTEST

artık konsoldan veritabanı işlemleri yapabiliriz.

Bazı düzenlemeler

1)

SQL*Plus komut satırında oldukça KULLANIŞSIZ bir arabirim. Bunu kullanışlı hale getirmek için rlwrap gibi bir programa ihtiyacımız var. rlwrap komut satır için kod tamamlama, history tutma, yön tuşları ile komutlar arasında geçiş işlemleri gibi işimize yarayacak pekçok şeyi sunuyor.

hemen sudo apt-get rlwrap ile yüklüyoruz.
 rlwrap sqlplus

şeklinde çağırıyoruz ve konsoldaki tuş kullanım rahatlığına kavuşuyoruz.

Ayrıca rlwrap ın sözlük desteği de bulunuyor. Sözlük desteği ile, daha önceden girdiğiniz keywordleri 1-2 karakter yazpı TAB tuşuna bastığınızda elde edebiliyorsunuz.

Diyelim $~/sql.dic diye bir dosyanız olsun ve bunun içerisi de şu şekilde olsun

select
from
where
recyclebin
purge

sqlplus komut satırında iken re yazıp TAB tuşuna basarsanız komut satırı sözlükteki recyclebin keywordunu getirecektir. rlwrap a sözlük kullanımını bildirmek içinse

rlwrap -f sql.dic sqlplus
dememiz yetecektir.

Ben daha kolay olsun diye sistemimi aşağıdaki şekilde ayarladım.

1) $ORACLE_HOME/bin dizinindeki sqlplus u sqlpuls_asil olarak değiştirdim
2) /usr/bin dizini altına sqlplus adında bir dosya oluşturdum (x+ özellikli), içeriği ise;

#!/bin/bash

case "$1" in
db1)
info="db1/passdb1@10GTEST"
;;

db2)
info="db2/passdb2@10GTEST"
;;
esac

if [[ -z "${info}" ]] ; then
echo $"Usage: sqlplus {db1|db2}"
else
cd $ORACLE_HOME/bin/
rlwrap -b "" -f $HOME/.sql.dic sqlplus_asil $info
cd -
fi

kullanımı ise;

sqlplus db1 //db1 e bağlanıyor
ya da
sqlplus db2 //db2 ye bağlanıyor



2)

SQL*Plus hazır halde çalıştırıldığında gerek prompt olsun gerek kolon genişliği gibi ayarlar olsun oldukça kullanışsız, bunları düzenlemek için normalde promptan set komutları girilebilir (bu her oturumda tekrar edilmeli). Her oturumda tekrar edecek olan set komutlarının önüne geçebilmek için bir yol var. $ORACLE_HOME/bin dizini altına login.sql adında bir dosya oluşturup içine de gerekli set komutlarını koyarsak, bu her oturum açıldığında işletilecektir.

Benim kullandığım login.sql in içeriği ise;

/* SQL Plus parameters required for each session */
set timing on;
set linesize 150;
set pagesize 500;
set space 1;
set termout off
set sqlprompt "&&_CONNECT_IDENTIFIER'.'&&_USER > "
set pages 500 lines 150
set null empty;
set time on;
set trim on;
set wrap off;
set sqlnumber on;
set describe line on;
set termout on
def _editor=vim

artık sqlplus ı çalıştırdığımızda konsolda
SQL> yerine 13:24:33 10GTEST.db1 > ibaresini göreceğiz.

29 Temmuz 2010 Perşembe

GWT ve Ext (GXT) kullanarak geliştirdiğim GMVC kullanıma hazır

Java platformunda, Google Web Toolkit (GWT) mimarisi uzerine Ext bileşenleri kullanarak geliştirdigim Model-View-Controller (MVC) çatısı ve bu çatıyı gerçekleyen, örnek MovieClub projesini sizlere sunuyorum. Bu projede, GWT ile uygulama geliştirirken (ya da başlarken diyeyim) karşılaşılan bazı sıkıntıların çözümünü bulabilirsiniz. MovieClub projesi, adından da anlaşılacağı üzere DVD/VCD kiralama/satma işi yapan firmaların iş takibini yapıyor. Yukarıda da belirttiğim gibi GMVC çatısını gerçekliyor.

GWT ile ilgili forumlarda ağırlıklı olarak aşagidaki başlıklara dair sorular bulunuyor, projede bu soruların yanıtını veren çözumler gelistirdim. Ayrıca, projenin javadoc larını tamamen Türkçe hazırladım ki framework ü inceleyen arkadaşlar sıkıntı çekmesin.

Genel olarak yaşanan sıkıntılar;

  • Hibernate implementasyonu,

  • Login formu ve kullanıcı hak denetimlerinin yapılması,

  • RPC ile SuggestionBox bileşeni geliştirilmesi,

  • MVC çatısı ve cok katmanlı programlama,

  • Jasper Report kullanımı/entegrasyonu,

  • GWT de Modül yazimi/kullanımının örneği,

  • RPC servislerin kalıtımla elde edilebilmesi,

  • Olay temelli programlama/haberleşme,

  • Projenin Ant Script ile WAR dosyasının hazırlanması


Bağlantılar
Projenin Hostu için burayı
SVN adresi için burayı
Projeden goruntuler için burayı
WAR dosyasi için de burayı kullanabilirsiniz

10 Mayıs 2010 Pazartesi

Ortalama vade bulma

Toplu çek alış/verişlerde, birden fazla çek vermek yerine tek çek verildiğini görmüşsünüzdür. Bu yazıda sizlere java kodları ile ortalama vade nasıl bulunur bunu anlatacağım. Kodlamaya geçmeden önce, anlatımı somutlaştırmak adına üç ayrı senaryo oluşturarak hesaplamaları bunlar üzerinden yapacağım. Tüm senaryolar için, içinde bulunduğumuz tarihi : 04/03/2010 alalım ve bu tarihe BAZ TARİH diyelim.

Senaryo 1:
---------- --------
Vade Tutar
---------- --------
04/04/2010 100 TL
04/05/2010 100 TL
04/06/2010 100 TL
---------- --------
toplam : 300 TL
vadeler : 30 gün

Senaryo 2:
-------------------
Vade Tutar
---------- --------
04/04/2010 300 TL
04/05/2010 500 TL
04/06/2010 1000 TL
---------- --------
toplam : 1800 TL
vadeler : 30 gün

Senaryo 3:
-------------------
Vade Tutar
---------- --------
01/04/2010 600 TL
22/05/2010 300 TL
13/06/2010 500 TL
---------- --------
toplam : 1400 TL
vadeler : düzensiz
-------------------------------------------------------------------

İlk etapta ortalama vadenin nasıl bulunduğunu formül bazında görelim. (İsimlendirmeler terminolojiye uygun olmayabilir. Buradaki amacım, bir programcının ortalama vade hesabını nasıl yapacağını izah etmek. Bu yüzden isimlendirmelere çok özenmedim.)

1 - Çek tutarları toplanarak toplam tutar bulunur (TT)
2 - Her bir çekin vadesinin baz tarihe olan uzaklığı gün cinsinden bulunur (VG)
3 - Bu gün farklarının tamamı toplanır ve vade günlerinin toplamı bulunur (TVG)
4 - Toplam Vade Günleri, Toplam Tutara bölünerek Ortak Çarpan bulunur (TVG/TT=OÇ)
5 - Herbir çek için; Ortak Çarpan, vade günü ve çekin tutarı çarpılarak Derece bulunur (D)
6 - Derece, Toplam Vade Gününe bölünerek Etki Mesafesi bulunur (D/OÇ=EM)
7 - Etki Mesafeleri toplanarak Baz Tarihe eklenir ve ortlama vade bulunmuş olur


Senaryo-1 çeklerini yukarıdaki formüle göre hesaplarsak,

1 - TT=300
2 - VG1=31, VG2=61, VG3=92
3 - TVG=184
4 - OÇ=0.61
5 - D1=1901.33, D2=3741.33, D3=5642.67
6 - EM1=10.33, EM2=20.33, EM3=30.67 (Toplam=61.33 gün eder)
7 - 04/03/2010 + 61.33 = 04/05/2010

Bu durumda:
Senaryo-1 in ortalama vadesi : 04/05/2010
Senaryo-2 nin ortalama vadesi : 16/05/2010
Senaryo-3 ün ortalama vadesi : 08/05/2010 olur

Java'da nasıl yapıldığına gelince de;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

public class Valor {

/*
* Girisi noktasi ve ornekleme
*/
public static void main(String[] args) {
Date base = new Date("2010/03/04");
List<Cheque> chequeList = new ArrayList<Cheque>();

chequeList.add(new Cheque( new Date("2010/04/01"), 600));
chequeList.add(new Cheque(new Date("2010/05/22 "), 300));
chequeList.add(new Cheque(new Date("2010/06/13"), 500));

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(sdf.format(getAvgDate(base, chequeList)));
}

/*
* Asil hesaplarin yapildigi kisim burasi
*/
private static Date getAvgDate(Date base, List<Cheque> chequeList) {
double totalAmount = 0;
double totalDays = 0;
int index = 0;
double totalEffects = 0;
List<Integer> dayDitanceList = new ArrayList<Integer>();

for (Cheque c: chequeList) {
int days = getDateDiff(c.maturity, base);

dayDitanceList.add(days);
totalDays += days;
totalAmount += c.amount;
}
double factor = totalDays / totalAmount;

for (Cheque c: chequeList) {
double degree = factor * c.amount * dayDitanceList.get(index);
double effect = degree / totalDays;
totalEffects += effect;
index++;
}

return addDate(base, new Double(totalEffects).intValue());
}

/*
* Iki tarih arasindaki gun farkini bulur
*/
private static int getDateDiff(Date big, Date little) {
long diff = big.getTime() - little.getTime();
return (int) (diff / (1000 * 60 * 60 * 24) + 1);
}

/*
* Verilen tarihe gun ekler
*/
private static Date addDate(Date date, int day) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DAY_OF_MONTH, day);
return cal.getTime();
}

/*
* Orneklemenin daha net okunabilmesi icin, cek bilgilerini tasiyan pojo
*/
private static class Cheque {

private Date maturity;
private double amount;

Cheque(Date maturity, double amount) {
this.maturity = maturity;
this.amount = amount;
}

}

}

12 Şubat 2010 Cuma

Mysql'de çok satırlı sorgular

Mysql konsolunda çok satırlı sorgular hazırlarken, uzun sorguların yazımı sıkıntı verebilir, ne de olsa tüm sorguyu tek satırda yazmanız gerekecektir. Bu sıkıntıdan kurtulmanın yolu, sorguyu bir editörde hazırladıktan sonra Mysql konsolunda yürütmek olabilir. Benzer şekilde Mysql çok satırlı sorgular için dahili vi editörünü sunuyor. Konsol kısmında iken \e; yazıp enter' a basarsanız size vi editörü açılacaktır. Aşağıda nasıl yapıldığının resmi var;



Burda da basit bir sorgu örneği;



Vi editöründen çıkarken:

Yazdığınız sorgunun yürütülmesini istiyorsanız;
:wq {enter}

Yazdığınız sorgunun yürütülmesini istemiyorsanız;
:q! {enter}

tuşlamanız yeterli.

9 Şubat 2010 Salı

PHP sayıyı yazıya çevirme

PHP kullanmaya başladım, yazdığım ilk ciddi kod parçasını sizlerle paylaşmak istiyorum. Bu kod parçası, verilen sayıyı yazıya çeviriyor. Fatura ve makbuzların alt kısımında gördüğümüz yalnız ikiyüzaltmışüç liradır. şeklindeki ifadeleri elde etmek için kullanılabilir.

Kod:

<?
function sayi2yazi($sayi) {
$birler = array("","bir","iki","üç","dört","beş","altı","yedi","sekiz",
"dokuz");
$onlar = array("","on","yirmi","otuz","kırk","elli","altmış","yetmiş",
"seksen","doksan");
$ustler= array("","bin","milyon","milyar","trilyon");

echo $sayi . " -> ";

$kalan = strlen($sayi) % 3;
if ($kalan != 0) $sayi = str_repeat("0", 3-$kalan) . $sayi;
$parcalar = str_split($sayi, 3);
$parca_adedi = sizeof($parcalar);

$sonuc= "";
for ($i = $parca_adedi; $i > 0; $i--) {

$p_yazi = "";
$parca = $parcalar[$i-1];

for ($j = 0; $j < strlen($parca); $j++) {
$bit = $parca[$j];
if ($bit != 0) {
switch ($j) {
case 0: {
if ($bit != 1) $p_yazi .= $birler[$bit];
$p_yazi .= "yüz";
break;
}
case 1: {
$p_yazi .= $onlar[$bit];
break;
}
case 2: {
$p_yazi .= $birler[$bit];
break;
}
}
}
}

if ($p_yazi=="bir" && $ustler[$parca_adedi-$i]=="bin")
$sonuc = $ustler[$parca_adedi - $i] . $sonuc;
else
$sonuc = $p_yazi . $ustler[$parca_adedi - $i] . $sonuc;

}
return $sonuc . "\n";
}

echo sayi2yazi(1);
echo sayi2yazi(101);
echo sayi2yazi(101101);
echo sayi2yazi(1001);
echo sayi2yazi(10010);
echo sayi2yazi(120230123);
echo sayi2yazi(712328);
echo sayi2yazi(42340738);
echo sayi2yazi(10101078);
echo sayi2yazi(23474854234234);
echo sayi2yazi(2347485423423);
echo sayi2yazi(72238);
?>

Ekran çıktısı:

mdpinar@mdpinar-desktop:~/php$ php sayi2yazi.php
1 -> bir
101 -> yüzbir
101101 -> yüzbirbinyüzbir
1001 -> binbir
10010 -> onbinon
120230123 -> yüzyirmimilyonikiyüzotuzbinyüzyirmiüç
712328 -> yediyüzonikibinüçyüzyirmisekiz
42340738 -> kırkikimilyonüçyüzkırkbinyediyüzotuzsekiz
10101078 -> onmilyonyüzbirbinyetmişsekiz
23474854234200 -> yirmiüçtrilyondörtyüzyetmişdörtmilyarsekizyüzellidörtmilyonikiyüzotuzdörtbinikiyüz
2347485423420 -> ikitrilyonüçyüzkırkyedimilyardörtyüzseksenbeşmilyondörtyüzyirmiüçbindörtyüzyirmi
72238 -> yetmişikibinikiyüzotuzsekiz

26 Ocak 2010 Salı

Hibernate konfigurasyonu

Yeni geliştirmek istediğiniz projede Seam, Spring... gibi genel kabül görmüş framework'lerden birisini kullanmak yerine kendi araç setinizi seçmek isterseniz ve projenizde veritabanı işlemleri için ORM (Object Relation Mapping) araçlarını kullanmak isterseniz neler yapmalısınız?

Bu yazımda, kısaca Java'da kalıcılık API'sini tanıtmak ve ORM araçlarından en popüler olanı Hibernate ile ilgili giriş düzeyinde bir örnek paylaşmak istiyorum.

JPA (Java Persistence API), JCP (Java Community Process - Java teknolojileri için spesifikasyon belirleyen gurup) tarafından spec'leri belirlenmiş, kalıcılık/ORM araçlarının manifestosu olan bir API'dir. Her JCP spec'inin bir RI (Reference Implementation) vardır, JPA' nın da refrence implementation' u Oracle' ın Toplink' idir. Fakat yaygın kullanım olarak Hibernate tercih ediliyor.

Normalde JCP de işler şu sırayla işler;
- Community'nin ihtiyaçlarına göre spec'ler hazırlanır (JPA, JMS, JSF, JTA ...)
- Bu spec'lere uygun referans gerçekleştirimleri -RI- yapılır (JPA için Oracle Toplink'tir)
- Daha özel ihtiyaçlara göre farklı gerçekleştirimler yapılır (Hibernate, OpenJPA, IBatis ...)

Fakat ORM araçları için bu adımlar biraz ters işledi, şöyle ki; JPA spec'leri yokken Hibernate yoğun olarak kullanılıyordu. Bir anlamda işin bürokrasisi için JPA spec'leri Hibernate'den sonra hazırlandı.

Tüm spec'lerde olduğu gibi JPA'da tek başına kullanılamaz. Verilerinizi işlemek için gerçekleştirim API' lerine ihtiyaç duyarsınız. Aşağıda hibernate özelinde bunları görebilirsiniz.

Geliştireceğiniz projeye başlamadan önce classpath de bulunması gereken bazı jar dosyaları var.

Zorunlu olanlar:
hibernate3.jar
antlr-2.7.6.jar
common-collections-3.1.jar
dom4j-1.6.1.jar
javasist-3.9.0.GA.jar
jta-1.1.jar
cglib.jar

Seçeneğe bağlı zorunlu olanlar:
ejb3-persistence.jar (EJB kullanacaksanız)
javax.persistence-1.xx.x.xxxxxxxxxx.jar

Seçeneğe göre zorunlu olanlar:
mysql,
hsql,
oracle,
mssql driver larından birisinin jar dosyası

Annotation'lar ile setup yapılacaksa, seçimlik:
hibernate-annototaions.jar

Kolon geçerlilik denetimleri yapılacaksa, seçimlik:
hibernate-validator.jar

Gelişmiş arama yapısı kullanılacaksa, seçimlik:
hibernate-search.jar
lucene-core-x.y.z.jar

Hibernate ORM ayarları için iki seçenek sunar, xml ile ve annotation' lar ile. Ben, daha yeni olduğu için annotation' ları kullanacağım.

İlk önce, projemizin root klasöründe hibernate.cfg.xml isimli master setup dosyamız olmalı. Bu dosya içerisinde, veritabanı ve Hibernate için üst düzey seçenekler ve değerleri yer alır.
Kullanıcı adı, şifre, veritabanı bağlantı url'i, db sürücüsü, kullanılacak dialect, vs... gibi. Mysql e bağlanmak için basit olarak aşağıdaki gibi bir konfigurasyon dosyası kullanılabilir.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/deneme</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">12345</property>
<property name="hibernate.show_sql">false</property>
<property name="hibernate.hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>

Bu xml konfigurasyonunda kullanılan seçenekler ve değerleri ile ilgili daha ayrıntılı bilgiler başka bir yazının konusu. Unutmadan, konfigurasyonu sadece xml dosyası ile yapmak zorunda değilsiniz,
properties dosyası kullanılabilir veya kodlama yolunu da seçebilirsiniz ki bu müdaheleleri zorlaştırdığı için pek tercih edilmiyor.

Modellerin eşlenmesi, kolonların ve ilişkilerin belirlenmesi için annotation kullanacağız. Aşağıda iki model sınıfımız ve bu iki sınıfın nasıl ORM yapıldığını göreceğiz.

User modeli

@Entity
@Table(name="USER")
public class User implements Serializable {

private static final long serialVersionUID = 1L;

@Id @GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="ID")
private Long id;

@Column(name="NAME", length=30, nullable=false, unique=true)
private String name;

@Column(name="PASSWORD", length=32, nullable=false)
private String password;

@Column(name="REG_DATE")
@Temporal(TemporalType.TIMESTAMP)
private Date registrationDate = new Date();

@Column(name="IS_ADMIN")
private Boolean isAdmin = Boolean.FALSE;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public Boolean getIsAdmin() {
return isAdmin;
}

public void setIsAdmin(Boolean isAdmin) {
this.isAdmin = isAdmin;
}

public Date getRegistrationDate() {
return registrationDate;
}

public void setRegistrationDate(Date registrationDate) {
this.registrationDate = registrationDate;
}

}


UserRole modeli

@Entity
@Table(name="USER_ROLE")
public class UserRole implements Serializable {

private static final long serialVersionUID = 1L;

@Id @GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="ID")
private Long id;

@Column(name="INDEX_COL")
private Integer indexCol;

@ManyToOne
@JoinColumn(name="USER_ID")
private User user;

@ManyToOne
@JoinColumn(name="ROLE_ID")
private Role role;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

public Role getRole() {
return role;
}

public void setRole(Role role) {
this.role = role;
}

public Integer getIndexCol() {
return indexCol;
}

}


Model tanıtımlarımızda kullandığımız annotation' ları kısaca açıklayalım.


@Entity -> Model sınıf olduğunu belirtir (3 tane olan EJB lerden birisi budur, dğerleri ise Oturum ve Mesaj Güdümlü Bean'lardır)
@Table -> Tablo ve parentezlerden sonra da özelliklerini belirtir
@Id -> Modelin ID alanını belirtir
@GeneratedValue -> ID alanının farklılaştırılma seçeneğini belirtir, strategy=GenerationType.AUTO kullanarak otomatik artan bir ID alanına sahip oluruz
@Column -> Kolon ve parentezlerden sonra da özelliklerini belirtir
@Temporal -> Tarihin şeklini belirtir
@ManyToOne -> Tablolar arası ilişkilerden ÇokaBir'i belirtir, LookUp table yapısı
@JoinColumn -> İlişkinin hangi kolon üzerinden olacağını ve yapısını belirtir

Bu ve diğer annotation'lar başka bir yazının konusu. Burda dikkatinizi çekmek istediğim önemli bir nokta var. Kolon türleri hep Wrapper Class olarak belirlenmiş, primitif olarak belirlenemez miydi? Evet belirlenebilirdi fakat veritabanından dönen değerler her zaman dolu olmayabilir bazen NULL değeri elde ederiz. Bu durumda primitif değişkene NULL atamaya kalkmış oluruz ki bu hataya sebep olur. Bu yüzden kolon türlerini her zaman Wrapper sınıflardan seçin.

Tanımladığımız bu modellerin Hibernate bildirim için yine yaygın olarak aşağıdaki gibi bir yol izleyeceğiz.

Genel tercih olarak projemize DBUtils isimli bir sınıf ve içine de şu satırları ekleyelim.

//database oturum yöneticisi
private static SessionFactory sessionFactory = null;

//singleton patterni yapısında tasarlayalım ki, fazladan instance ın önüne geçelim,
//bu metodu projeniz startup olurken bir kere çağırmalısınız
public static void init() {

if (sessionFactory == null) {
try {
//oturum yöneticisine kullanacağımız model sınıflarımızı bildirerek konfigure ediyoruz
sessionFactory = new AnnotationConfiguration()
.addAnnotatedClass(User.class)
.addAnnotatedClass(UserRole.class)
.configure()
.buildSessionFactory(); //root klasördeki xml/properties dosyamıza otomatik olarak bakacak

//bağlantı işlemi başarılı olmuş mu, kontrol ediyoruz
try {
Transaction tx = getSession().beginTransaction();
if (tx.isActive()) tx.rollback();
System.out.println("Database connection successful.");
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("Factory not null!");
}

}

//oturum taleplerini tek bir noktadan idare edebilmek için bu sınıf içerisinde talepleri karşılıyoruz
public static Session getSession() {
Session session = null;
try {
//her talepte yeni bir oturum açılır, oturum ile connection u karıştırmamalıyız
session = sessionFactory.openSession();
} catch(Exception e) {
System.out.println("Session getting error!");
}
return session;
}

Bu işlemlerden sonra artık projemizin herhangi bir yerinden istediğimiz gibi CRUD (Create, Read, Update, Delete) işlemleri yapabiliriz.

Ekleme/Güncelleme
Session session = DBUtils.getSession();
User user = new User("Mustafa", "12345", false);
session.saveOrUpdate(user);
session.close();

Tekli Okuma
Session session = DBUtils.getSession();
User user = (User) session.load(User.class, 3L);
session.close();

Liste şeklinde okuma
Session session = DBUtils.getSession();
List userList = (List) session.createQuery("from User u").list();
session.close();

Silme
Session session = DBUtils.getSession();
User user = (User) session.load(User.class, 3L);
session.remove(user); //veya session.remove(session.load(User.class, 3L));
session.close();

Son olarak;
Hibernate'in hedefi sadece CRUD işlemleridir, daha fazlası için uygun değildir, örneğin büyük mitarda verileri transfer etme ve işleme gibi.

20 Ocak 2010 Çarşamba

Hibernate'de equals metodu

Hibernate entity lerinde class ların equals metodlarını ezmeleri neden önemli? Kısaca; "equals metodu ezmezseniz, List ve Map yapıları üzerindeki entitylerden var olduğunu bildiklerinize ulaşamazsınız!" denilebilir.

Örneklersek; Arac isimli bir modelimiz olsun.

Arac sınıfımız
public class Arac implements Serializable {

private Long id;
private String plaka;

public Arac() {

}

public Arac(Long id, String plaka) {
super();
this.id = id;
this.plaka = plaka;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getPlaka() {
return plaka;
}

public void setPlaka(String plaka) {
this.plaka = plaka;
}

}

Arac tablosu verileri
ID  PLAKA
--- -----------
1 34-AB-034
2 34-BC-035
3 34-CD-036
4 34-DE-037
5 34-EF-038
6 34-FG-039
7 34-GH-040

Projemizde, tüm araçları içeren bir List/Map kullandığımızı düşünelim.

List<Arac> aracList = new ArrayList<Arac>();

//hibernate ile çektiğimiz araçları listeye dolduralım

//listeden aradığımız (ve var olduğunu kesin olarak bildiğimiz) aracı istiyoruz
Arac bulunan = aracList.get(new Arac(1L, "34-AB-034"));

Normalde üst kısımda aradığımız aracı bulurduk fakat hibernate kanalı ile elde ettiğimiz modeller için hashcode, her talepte yeniden oluşturuluyor! Tüm nesnelerin temeli olan Object sınıfındaki equals metodu eşitlik karşılaştırmasında hashcode değerine baktığı için equals değeri bu durumda false dönüyor. List ve Map yapılarında bir nesne aranırken equals metodundan faydalanılıyor. Eğer aranan ile bakılanın equals metodları true dönerse aranılan bulunmuş oluyor ve döngü sonlandırılıyor aksi halde aranılan bulunamıyor.

Özetlersek; List ve Map gibi veri yapıları içerisine hibernate kanalı ile doldurduğumuz nesnelerin equals metodunu uygun bir biçimde ezmezsek, List/Map üzerinde yapacağımız aramalarda aradığımızı bulamayabiliriz. En uygun ezme yolunun, List/Map e dolduracağımız entity nin ID alanı kıyaslaması olduğunu düşünüyorum.

Örnek:
 public boolean equals(Object obj) {
if (obj != null && obj instanceof Arac) {
if (((Arac) obj).getId().equals(this.id)) {
return true;
}
}
return super.equals(obj);
}

Equals metodunu ezmek yerine hashcode metodu da ezilebilirdi. Hash algoritmaları yazmak equals metodunu ezmekten daha zahmetlidir. hashcode metodunu ezme seçeneğine, gerçekten daha verimli ve lazım olduğunu düşündüğünüz durumlarda bakın derim.

15 Ocak 2010 Cuma

Crontab periyod eşitsizliği

Posix sistemlerde yaygın olarak kullanılan ve oldukça da başarılı olan Crontab hizmeti, belirlenen (ya da beklenen) periyodlar için tam da beklendiği gibi davranmayabiliyor. Vereceğimiz periyod bölünmeye dayalı olacaksa bölmek istediğimiz hanenin üst limiti önemli oluyor. Bildiğimiz gibi bu değerler; saniye ve dakika haneleri için 60, saat hanesi için 24 gibi (aslında bu değerler yerine 0 kabul ediyoruz), aynı şekilde hafta ve ay değerlerinin de üst limitlerine göre bölümleme yapmamız önemli oluyor.

Atlamadan eklemek isterim ki, posix sistem crontablarında saniye hanesi yok! Java' da geliştirilmiş olan Quartz projesinde saniye eklenmiş durumda. Bu yüzden 5 haneli crontablar yerine 6 haneli olanlarını örnekleyeceğim.

Üst limitler neden önemli? Bunun için bir iki örneğe bakmamız daha iyi olacaktır.

Aşağıda verilen örneklerde periyodların tamamı eşit zaman aralıkları ile TETİKLENECEKTİR.

Her 10 dakikada bir:
* 0/10 * * * ?

Her 3 saatte bir:
* * 0/3 * * ?

Aşağıda verilen örneklerde ise periyodların tamamı eşit zaman aralıkları ile TETİKLENMEYECEKTİR.

Her 7 dakikada bir:
* 0/7 * * * ?

Her 40 saniyede bir: (bu 40. ve 0. saniyelerde yani 20 ve 40 sn aralıklarla tetiklenecek)
0/40 * * * * ?

Crontab hanelerini bölmek istediğimiz değer, ilgili hanenin makisimum değerini kalansız böldüğünde aralıklar eşit oluyor. Yine örnek verecek olursak; Dakika/Saniye hanesini 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30 gibi sayılar maksimum değeri olan 60 ı tam bölecek ve bu şekilde verilen bir periyod eşit zamanlarda tetiklenecektir.

Üst limitlerde saniye ve dakika için 60, saat içinse 24 olmadığını biliyorum, fakat crontab davranışında son tetikleme 0 içinde yapılıyor bu yüzden saniye ve dakika için 60 ve saat içinde 24 olarak değerlendirebiliriz.

Yukarıda belirttiğim gibi; Java için geliştirilmiş olan Quartz API, posix lerdeki crontab mantığını gerçeklediği için benzer durumla karşılaşacaksınız, şaşırmayın.

Son bir küçük uyarı; bölümleme yaparken
0/10 -> her 10. da bir demek (10. da bir kez tetiklenir)
*/10 -> 10. nun her birinde demek oluyor (10. da defalarca kez tetiklenir)

14 Ocak 2010 Perşembe

MVC vs MVP

Uzun zamandır kullanımda olup ve birçok projenin de anaçatısı durumunda olan MVC (Model-View-Controller) desenine rakip olarak sahneye çıkan ve Google tarafından da ateşli bir biçimde savunulan MVP (Model-View-Presenter) deseni ile ilgili bir mücadele yaşanıyor forumlarda.

Google tarafından hazırlanan sunumlara baktığımızda MVP için;
  • GWT dünyasında testleri kolaylaştırdığını
  • View ile Controller bağlantısının olmadığını
  • Daha rahat kod geliştirildiğini
Yine aynı sunumlarda MVC için ise;
  • GWT dünyasında testleri zorlaştırdığını
  • View ile Controller bağlantısının olduğunu (Katı Bağ oluşturuyor diye)
  • 1980 lerden kalma olduğunu
söylüyorlar.

Google'ın değerlendirmeleri pek insaflıymış gibi gelmedi bana. Evet GWT framework'ünde testleri kolaylaştırdığı doğru olabilir fakat MVC' de View ile Controller' un doğrudan bağlantısı olmak zorunda değil, bu tarz bir kullanım sadece programcının tercihi olabilir. Ayrıca bir teknolojinin eski olması onun kötü olduğu anlamına gelseydi bugün kullandığımız ilişkisel veritabanlarının en berbat teknolojiler olaması gerekirdi zira 1970 li yıllarda geliştirilmişti. Benzer şekilde C dili ve HTML de çok eski zamanlara ait olmalarına rağmen günümüzde yaygın olarak kullanılmakta.

MVP desenindeki Presenter, Controller ile hemen hemen aynı yapıya sahip, tek fark; MVP'de bulunan EventBus sınıfı. Bu sınıf MVP katmanları arasında haberleşmeden sorumlu. Model, View'i, View'de Presenter'ı tanımıyor. Soyutlama açısından hoş bir yaklaşım. MVP sınıflarındaki tüm olaylar EventBus tarafından handle edilidiği için dispatch da bu sınıfın denetiminde oluyor. Özetle, kimse kimseyi tanımıyor ama EventBus herkesi tanıyor ve haberleştiriyor. Bunun tek dezavantajı ise fazladan bir sınıfımızın olması.

MVP'deki EventBus işlevini MVC' deki Controller sınıfı pekala yapabilir. MVC'nin gerçekleştirimine göre, View ve Model sınıfları birbirlerini tanımayabilir ve bütün denetim Controller' da olabilir. Zaten adından da anlaşıldığı gibi Controller sınıfı herşeyi kontrol eden ve iş mantıklarının yer aldığı sınıflar olmalıdır.

Bence, kullanılan dilin imkanları ve programcının tercihlerine göre MVP olsun MVC olsun çok işe yarayabilecekleri gibi bir çok sıkıntının kaynağı da olabilirler. MVC nin en büyük avantajı, genel kabul görmüş olması ve defalarca kendisini ispat etmiş olmasıdır. MVP henüz yeni olduğu için kabul zamanına ihtiyacı var. Ayrıca MVP'nin tercih edilebilmesi için daha net kazanımlar sunması lazım. Controller'a ait bir kısım görevleri ayrı bir sınıfta ele almak ve Model ile View arası iletişim için olay temelli haberleşme yapmak çok cazip bir kazanım gibi durmuyor.

13 Ocak 2010 Çarşamba

Selam Dünya!

Herhangi bir dile başlayan bir programcının ilk denediği, HelloWorld örneği ile blog yayınıma başlamak istiyorum.
public class HelloWorld {

public static void main(String[] args) {
System.out.println("Selam dünya...");
}

}

Ciddi anlamda zaman darlığı yaşamadığım sürece, bu blogda java, java teknolojileri ve kendime dair bilgileri paylaşacağım. Şimdiden belirtmek isterim ki; burada yazacağım içerikler iddia değildir, sadece ben öyle bildiğim için öyle yazdığım şeylerdir.

Yazdıklarımın birilerinin işine yaraması ve yapılacak eleştirilerin yapıcı olması umuduyla Selam Dünya...