Tepe Menü

Ana Menü

Alt Menüler Kategoriler

İçerik

Özel Arama

HOŞGELDİNİZ , Toplam : 1527

 

YTL'ye geçiş ile beraber noktadan sonrası çok kritik oldu, özellikle finans uygulamalarında. Veritabanında decimal(20,2) tutar alanlarına gözümüz çoktan alıştı. 

Veritabanında decimal (numeric) olarak tanımladığımız alanları java uygulamasında BigDecimal ( java.math.BigDecimal) ile karşılarız.Finans uygulamalarında (örnek: para pul, faiz işlemleri..) BigDecimal çok kullanılır.

BigDecimal ile işlem yaparken dikkat etmemiz gereken bazı noktalar var. Bu yazıda bu detayların tamamına girebilmemiz mümkün değil. Sadece YTL değerini gösterirken kullandığımız format işlemi ile ilgili örnek geliştirmeye çalışacağız.




Finans uygulamalarında çarpma, bölme işlemlerinde işin içine yuvarlama (round) ve ondalıklı haneleri (scale) belirleme girer ki uygulama biraz daha karmaşıklaşır.

BigDecimal değerlerin okunabilirliğini artırmak için format işlemi uygulanır. Bu işleme dikkat etmek gerekir. Gerçek değer ile formatlanmış değer farklılaşabilir. Bunun nedeni format işleminin sadece double değerlere uygulanıyor olması. Formatlama istenirse BigDecimal değerinin doubleValue() kullanılarak double değere dönüştürülmesi gerekir. BigDecimal değeri double 'a dönüştürülürken küçük kayıpların yaşanabileceğini bilelim. Bu kayıplar küçük değerler için sözkonusu değildir.

Ayrıca hepimizin bildiği bir konuyu da hatırlatmak isterim. Java ve veritabanı uygulamalarında uygulamanın istediğimiz gibi davranması sürücü(driver) ye çok bağlıdır. Kullandığımız sürücünün kesinlikle güvenilir-stable- olması gerekir. Ayrıca çalıştığımız veritabanının özelliklerinin (neleri destekliyor, neleri desteklemiyor?) yeterli olması ve bu özelliklerin iyi bilinmesi gerekiyor. Bundan sonrası artık java uygulamasına yani sizin yazılımcılığınıza kalır.
Aşağıda konu ile ilgili iki örnek uygulamadan alıntı var. Önce veritabanına bakalım :) Hesap adında bir tablomuz olsun. Tablonun DDL'i:

create table Hesap 
(
 sube integer,
 hesapNo integer,
 bakiye decimal(
18,2)
)
Tabloya 2 tane kayıt ekleyelim.

insert into Hesap ( sube, hesapNo, bakiye ) values 123123019999999999999999.17)
insert into Hesap ( sube, hesapNo, bakiye ) values 123123029999999999999999.17)

select * from Hesap ifadesini çalıştırdığımızda 

 sube     hesapNo     bakiye             
 -------  ----------  -------------------
 123      12301       9999999999999999.17
 123      12302       9999999999999999.17

olduğunu görüyoruz. Ayrıca

select sum(bakiye) as toplamBakiye from Hesap

 ifadesini çalıştırdığımızda

 toplamBakiye        
 --------------------
 19999999999999998.34

Yukarıdaki işlemleri SQL aracımızda gerçekleştirdik. Henüz java uygulamasına bulaşmadık.   9999999999999999.17 sayısının tam sayı kısmı 16 hane ve ondalıklı kısmı 2 hane. Tabloya bu sayıyı içeren 2 kayıt ekledik. Bu alanların toplamı(SQL sum ile) bize 19999999999999998.34  değerini verir. Toplam değer 19999999999999998.34  yani tam sayı kısmı 17 hane ve ondalıklı kısmı 2 hane.

Ondalıklı toplamada toplanan sayıların ondalıklı kısmı 2 haneli ise toplam sayıda ondalıklı kısım hiçbir zaman 2 haneliden fazla olmaz. Bu çıkarma işlemi için de geçerlidir. Yukarıda anlatıldığı gibi çarpma ve bölme işleminde böyle olmaz :)

Ayrıca 16 haneli değerlerin toplamı da 17 haneli olabilir (kesin olur demiyorum, dikkat buyurun) Bu son derece normal değil mi? Bundan kaçma olabilir mi? Mümkün değil? Sadece uygulamalarımızda -gerek veritabanı tarafında gerekse java ya da benzeri programla dili ile geliştirdiğimiz uygulamalarda- veri tipimizi veriye uygun olarak seçmeliyiz.

Şimdi gelelim java uygulamalarına. Bunun için 2 tane test uygulaması geliştirdik ve Sybase 12.5 veritabanında test ettik. Sürücü olarak JDBC 3 destekleyen jconn3.jar kullanıyoruz.1. uygulama tablodaki kayıtları tek tek gezerek hem BigDecimal ile hem de double ile toplam oluşturuyor.  Uygulamanın ilgili kısmı :

  BigDecimal toplamBakiyeBigDecimal ;
  
BigDecimal hesapBakiyeBigDecimal ;
  
  double 
toplamBakiyeDouble 
  double 
hesapBakiyeDouble ;
  
  
String toplamBakiyeDoubleFormatted ;
  
  try 

   
   Class.forName(driverStr)
;
   
conn DriverManager.getConnection(url, user, password);
   
   
queryStr "select sube, hesapNo, bakiye from Hesap"
   
   
stmt conn.createStatement()
   
rs stmt.executeQuery(queryStr);
   
   
toplamBakiyeBigDecimal = new BigDecimal("0.0");
   
   while
(rs.next())
   {
    
    
/*
    System.out.print (rs.getInt("sube")+" "); 
    System.out.print(rs.getInt("hesapNo")+" ");
    System.out.println(rs.getBigDecimal("bakiye"));
    */
    
    
hesapBakiyeBigDecimal rs.getBigDecimal("bakiye")
    
toplamBakiyeBigDecimal toplamBakiyeBigDecimal.add(hesapBakiyeBigDecimal);
     
    
hesapBakiyeDouble hesapBakiyeBigDecimal.doubleValue()
    
toplamBakiyeDouble +hesapBakiyeDouble//toplamBakiyeDouble = toplamBakiyeDouble + hesapBakiyeDouble;  

    
   
}
   
   System.
out.println("Toplam Bakiye BigDecimal: " + toplamBakiyeBigDecimal);
   
System.out.println("Toplam Bakiye double(no format): " + toplamBakiyeDouble);
   
NumberFormat nf NumberFormat.getCurrencyInstance(Locale.getDefault());
   
toplamBakiyeDoubleFormatted nf.format(toplamBakiyeDouble)
   
System.out.println("Toplam Bakiye double(formatly): " + toplamBakiyeDoubleFormatted)

Uygulamayı çalıştırdığımızda elde ettiğimiz sonuç aşağıdadır.

Toplam Bakiye BigDecimal: 19999999999999998.34
Toplam Bakiye double(no format): 2.0E16
Toplam Bakiye double(formatlı): 20.000.000.000.000.000,00 YTL

Görüldüğü gibi BigDecimal değerinde sorun yok fakat double değerler gerçek değeri yansıtmıyor!!

2. uygulamada bu sefer select sum(bakiye) ile benzer işlemleri yineleyelim.

  BigDecimal toplamBakiyeBigDecimal ;
  double 
toplamBakiyeDouble ;
  
String toplamBakiyeDoubleFormatted 
  
  try 
{
   
   Class.forName(driverStr)
;
   
conn DriverManager.getConnection(url, user, password)
   
   
queryStr "select sum(bakiye) as toplamBakiyeBigDecimal from Hesap";
   
   
stmt conn.createStatement();
   
rs stmt.executeQuery(queryStr);
   
   
toplamBakiyeBigDecimal = new BigDecimal(" 0.0");
   
   while
(rs.next())
   {
    
    toplamBakiyeBigDecimal 
toplamBakiyeBigDecimal.add(rs.getBigDecimal("toplamBakiyeBigDecimal"));
        
   
}
   
   toplamBakiyeDouble 
toplamBakiyeBigDecimal.doubleValue();
   
   
System.out.println("Toplam Bakiye BigDecimal: " + toplamBakiyeBigDecimal)
   
System.out.println("Toplam Bakiye double(no format): " + toplamBakiyeDouble)
   
NumberFormat nf NumberFormat.getCurrencyInstance(Locale.getDefault());
   
toplamBakiyeDoubleFormatted nf.format(toplamBakiyeDouble)
   
System.out.println("Toplam Bakiye double(formatly): " + toplamBakiyeDoubleFormatted);

Toplam Bakiye BigDecimal: 19999999999999998.34
Toplam Bakiye double(no format): 2.0E16
Toplam Bakiye double(formatlı): 20.000.000.000.000.000,00 YTL

BigDecimalda yine sorun yok, fakat double değerlerde farklılaşma sözkonusu!!

BigDecimal değer double değere dönüştürülürken büyük değerler için küçük de olsa farklılaşmanın olduğunu gördük. Finans uygulamaları gibi kritik uygulamaların gerçek değerler üzerinden işlem yapması gerektiğini hep hatırlayalım.

Not : Bu yazı jdbc_tr yahoo grubunda bir soruya verdiğim cevabın düzenlenmiş halidir.



Murat ACAR
1973 İzmir doğumludur. 1996 Ege Üniversitesi Bilgisayar Mühendisliği mezunudur. Gelişim Platformu, Genç Girişimciler Kulübü ve Türkiye Bilişim Derneği üyesidir. Kurucuları arasında bulunduğu Java Teknoloji Derneğinde Yönetim Kurulu üyesidir. Genç Girişimciler Kulübü Bilişim Komisyonunda görev almaktadır. Uzun yıllar finans ve veritabanı işlemleri yoğun projelerde analiz, tasarım, geliştirme, test vb. aşamalarda çalışmıştır. Halen Proje Sorumlusu olarak çalıştığı IMKB Takasbak A.Ş. de Sermaye Piyasalarına yönelik projeler geliştirmektedir. Java ve Veritabanı Teknolojileri ile ilgili gelişmeleri takip etmekte ve bu teknolojiler ile ilgili çeşitli konularda eğitim ve seminerler vermektedir. Evli ve biri erkek biri kız iki çocuk babasıdır.

Sizden önce 1996 kişi okudu.

 
YORUMLAR

İlk yorumu yazan siz olmak ister misiniz?

 
SENDE YORUM EKLE

İsim(Rumuz)

:

Email

:

Yorum

:

Yan Bloklar

Footer