7. [TD]: TD sınıfının [DAO] katmanının API ve JDBC ile uygulanması
Anahtar kelimeler: ilişkisel veritabanları, API, JDBC, SQLException.
Uygulamamızın katmanlı mimarisine tekrar göz atalım:
![]() |
Seçim için gerekli veriler bir veritabanına kaydedilir MySQL [dbelections]
7.1. Support
![]() |
[support / chap-07] [1] klasörü şunları içerir:
- bu bölümün Eclipse projeleri [2];
- SQL veritabanını oluşturan komut dosyası: MySQL, [dbelections], [3];
7.2. [dbelections] veritabanı
Yapılması gereken iş: 6.4.2. paragrafında izlenen prosedürü takip ederek MySQL ve [dblelections] veritabanlarını oluşturun.
[dbelections] veritabanı, iki tablo içeren bir MySQL veritabanıdır:
![]() |
[conf] tablosu, seçimle ilgili yapılandırma bilgilerini içerir:
![]() |
- [id]: otomatik artan birincil anahtar;
- [version]: kaydın sürüm numarası – burada göz ardı edilebilir;
- [sap]: doldurulacak koltuk sayısı;
- [seuilelectoral]: bir listenin elenmesi için alt sınır;
İçeriği şu şekildedir:
![]() |
[listes] tablosu, seçim aday listelerini içerir:
![]() |
- [id]: otomatik artan birincil anahtar;
- [version]: kaydın sürüm numarası – burada göz ardı edilebilir;
- [nom]: listenin adı;
- [voix]: listenin adı – ancak kullanıcı [présentation] katmanına giriş yaptıktan sonra bilinir;
- [sieges]: kazanılan sandalye sayısı - yalnızca [métier] katmanının hesaplanmasından sonra bilinir;
- [elimine]: liste elenmişse 1, aksi takdirde 0 - yalnızca [métier] katmanının hesaplanmasından sonra bilinir;
[listes] tablosunun içeriği şöyledir:
![]() |
[dbelections] veritabanını oluşturmak için kullanılan SQL komut dosyası, [dbelections.sql] adını taşır ve sunucuda bulunur. Kod şöyledir:
-- phpMyAdmin SQL Dump
-- sürüm 4.0.4
-- http://www.phpmyadmin.net
--
-- İstemci: localhost
-- Oluşturulma tarihi: 11 Mart 2015 Çarşamba, 12:20
-- Sunucu sürümü: 5.6.12-log
-- PHP sürümü: 5.4.12
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
--
-- Veritabanı: `dbelections`
--
CREATE DATABASE IF NOT EXISTS `dbelections` DEFAULT CHARACTER SET utf8 COLLATE utf8_swedish_ci;
USE `dbelections`;
-- --------------------------------------------------------
--
-- `conf` tablosunun yapısı
--
CREATE TABLE IF NOT EXISTS `conf` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`version` int(11) NOT NULL DEFAULT '1',
`sap` tinyint(4) NOT NULL,
`seuilelectoral` double NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci AUTO_INCREMENT=2 ;
--
-- `conf` tablosunun içeriği
--
INSERT INTO `conf` (`id`, `version`, `sap`, `seuilelectoral`) VALUES
(1, 1, 6, 0.05);
-- --------------------------------------------------------
--
-- `listes` tablosunun yapısı
--
CREATE TABLE IF NOT EXISTS `listes` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`version` int(11) NOT NULL DEFAULT '1',
`nom` varchar(20) COLLATE utf8_swedish_ci NOT NULL,
`voix` int(11) NOT NULL,
`sieges` int(11) NOT NULL,
`elimine` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `nom` (`nom`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci AUTO_INCREMENT=8 ;
--
-- `listes` tablosunun içeriği
--
INSERT INTO `listes` (`id`, `version`, `nom`, `voix`, `sieges`, `elimine`) VALUES
(1, 21, 'A', 10, 1, 0),
(2, 22, 'B', 20, 2, 0),
(3, 21, 'C', 30, 3, 0),
(4, 13, 'D', 40, 3, 0),
(5, 17, 'E', 50, 6, 0),
(6, 18, 'F', 60, 1, 0),
(7, 19, 'G', 70, 2, 0);
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
7.3. Eclipse projesi
![]() |
[DAO] katmanının Eclipse projesi şu şekilde olacaktır:
![]() |
- [elections.dao.entities] paketi, [DAO] katmanı tarafından işlenen nesneleri içerir;
- [elections.dao.service] paketi, [DAO] katmanının uygulamasını içerir;
- [elections.dao.config] paketi, [DAO] katmanının yapılandırmasını içerir;
- [elections.dao.junit] paketi, projenin JUnit test sınıfını içerir;
- [elections.dao.console] paketi, bir test çalıştırma sınıfı içerir;
7.4. Maven proje yapılandırması
![]() |
Maven projesini yapılandıran [pom.xml] dosyası şöyledir:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.elections</groupId>
<artifactId>elections-dao-jdbc-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- Tomcat Jdbc -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency>
<!-- kütüphane jSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Testi -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Günlüğü -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
</dependencies>
<!-- eklentiler -->
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>config.AppConfig</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- proje artefaktını yerel Maven deposuna yüklemek için -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
Bu dosya, 6.5.1. paragrafında açıklanan dosyaya benzerdir. Dosyada aşağıdaki değişiklikler yapılmıştır:
- 8-12. satırlar: Bir üst Maven projesi tanımlanmıştır. [spring-boot-starter-parent] projesi (10. satır), sürümleriyle birlikte çok sayıda bağımlılık tanımlamaktadır. Bunlardan birini kullandığımızda (19-57. satırlar), sürümünü belirtmeye gerek kalmaz; çünkü bu sürüm, üst Maven projesinde tanımlanmıştır;
- satır 40-51: projenin test sınıfı için gerekli bağımlılıklar. Bu bağımlılıklar, [<scope>test</scope>] özniteliğine sahiptir; bu, bunların yalnızca [src / test / java] klasöründeki sınıflar için gerekli olduğu anlamına gelir. Bu bağımlılıklar, projenin nihai arşivine dahil edilmeyecektir;
- 53-56. satırlar: [spring-boot-starter-logging] kütüphanesi, Spring tarafından ekrana günlük kaydı yapmak için kullanılacaktır;
- 14-17. satırlar: Maven yapılandırma özellikleri:
- 15. satır: kaynak dosyaların UTF-8 ile kodlandığını belirtir;
- satır 16: kullanılacak derleyicinin 1.8 sürümü olması gerektiğini belirtir;
7.5. [DAO] katmanındaki varlıklar
![]() |
- [ElectionsConfig], [CONF] tablosundaki bir satıra ait nesne modelidir;
- [ListeElectorale], [LISTES] tablosundaki bir satıra ait nesne modelidir;
- [AbstactEntity], önceki iki sınıfın üst sınıfıdır. Bu sınıf, her iki sınıfa ortak olan [id, version] alanlarını bir araya getirir;
- [ElectionsException] bir istisna sınıfıdır;
7.5.1. [ElectionsException] sınıfı
![]() |
[ElectionsException] sınıfı 4.3. paragrafında açıklanmıştır. Kodunu tekrar veriyoruz:
package ...;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
// Elections uygulaması için istisna sınıfı
// istisna kontrolsüzdür
public class ElectionsException extends RuntimeException implements Serializable {
// seri numarası ID
private static final long serialVersionUID = 1L;
// yerel alanlar
private int code;
private List<String> erreurs;
// oluşturucular
public ElectionsException() {
super();
}
public ElectionsException(int code, Throwable e) {
// üst
super(e);
// yerel
this.code = code;
this.erreurs = getErreursForException(e);
}
public ElectionsException(int code, String message, Throwable e) {
// üst
super(message,e);
// yerel
this.code = code;
this.erreurs = getErreursForException(e);
}
public ElectionsException(int code, String message) {
// üst
super(message);
// yerel
this.code = code;
List<String> erreurs = new ArrayList<>();
erreurs.add(message);
this.erreurs = erreurs;
}
public ElectionsException(int code, List<String> erreurs) {
// üst
super();
// yerel
this.code = code;
this.erreurs = erreurs;
}
// bir istisnaya ait hata mesajları listesi
private List<String> getErreursForException(Throwable th) {
// istisnanın hata mesajları listesi alınır
Throwable cause = th;
List<String> erreurs = new ArrayList<>();
while (cause != null) {
// mesaj, yalnızca !=null ve boş değilse alınır
String message = cause.getMessage();
if (message != null) {
message = message.trim();
if (message.length() != 0) {
erreurs.add(message);
}
}
// aşağıdaki neden
cause = cause.getCause();
}
return erreurs;
}
// getter ve setter'lar
...
}
Bir [ElectionsException] nesnesi iki bilgiyle tanımlanır:
- 16. satır: bir hata kodu;
- 17. satır: meydana gelen istisna yığınıyla ilişkili hata mesajları listesi;
- sınıfın 5 yapıcı işlevi vardır (satır 20, 24, 32, 40, 50);
- satır 59-76: [getErreursForException] yöntemi, istisna yığınındaki hata mesajlarını almayı sağlar;
7.5.2. [AbstractEntity] sınıfı
![]() |
[AbstractEntity] sınıfı şu şekildedir:
package elections.dao.entities;
import java.io.Serializable;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public abstract class AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
// alanlar
protected Long id;
protected Long version;
// yapıcılar
public AbstractEntity() {
}
public AbstractEntity(Long id, Long version) {
this.id = id;
this.version = version;
}
// imza
public String toString() {
try {
return new ObjectMapper().writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
// getter ve setter'lar
...
}
Bu sınıf, [ID, NOM] alanlarını [CONF] ve [LISTES] tablolarının satırlarından (8-9. satırlar) depolar.
- 8. satır: Sınıf, örneklenemeyeceğini belirten [Abstract] özniteliğine sahiptir. Bu sınıf yalnızca türetilebilir;
- 26-33. satırlar: nesnenin jSON imzası;
- 28. satır: [this]'ten jSON dizesi döndürülür. Çalışma sırasında [this], [AbstactEntity]'ten türetilmiş bir nesneyi temsil ediyorsa, türetilmiş nesnenin jSON dizesi elde edilir. Böylece, türetilmiş sınıfların bir [toString] yöntemi tanımlamasına gerek kalmaz. Üst sınıftaki yöntem yeterlidir;
7.5.3. [ElectionsConfig] sınıfı
![]() |
[ElectionsConfig] sınıfı şu şekildedir:
package elections.dao.entities;
public class ElectionsConfig extends AbstractEntity {
private static final long serialVersionUID = 1L;
// alanlar
private int nbSiegesAPourvoir;
private double seuilElectoral;
// yapıcılar
public ElectionsConfig() {
}
public ElectionsConfig(Long id, Long version, int nbSiegesAPourvoir, double seuilElectoral) {
// üst sınıf
super(id, version);
// yerel alanlar
this.nbSiegesAPourvoir = nbSiegesAPourvoir;
this.seuilElectoral = seuilElectoral;
}
// getter ve setter'lar
...
}
- 4. satır: sınıf, [AbstractEntity] sınıfını genişletir;
- 8-9. satırlar: [CONF] tablosundaki [SAP, SEUILELECTORAL] sütunlarının bilgilerini depolar;
7.5.4. [ListeElectorale] sınıfı
![]() |
[ListeElectorale] sınıfı şu şekildedir:
package elections.dao.entities;
public class ListeElectorale extends AbstractEntity {
// alanlar
private String nom;
private int voix;
private int sieges;
private boolean elimine;
// yapıcılar
public ListeElectorale() {
}
public ListeElectorale(String nom, int voix, int sieges, boolean elimine) {
// üst sınıf
super();
// yerel alanlar
initNom(nom);
initVoix(voix);
initSieges(sieges);
this.elimine=elimine;
}
public ListeElectorale(Long id, Long version, String nom, int voix, int sieges, boolean elimine) {
// üst sınıf
super(id, version);
// yerel alanlar
initNom(nom);
initVoix(voix);
initSieges(sieges);
this.elimine=elimine;
}
// özel yöntemler
private void initNom(String nom) {
this.nom = nom.trim();
if ("".equals(nom)) {
throw new ElectionsException(10, "Le nom ne peut être vide");
}
}
private void initVoix(int voix) {
this.voix = voix;
if (voix < 0) {
throw new ElectionsException(11, "Le nombre de voix ne peut être <0");
}
}
private void initSieges(int sieges) {
this.sieges = sieges;
if (sieges < 0) {
throw new ElectionsException(12, "Le nombre de sièges ne peut être <0");
}
}
// alıcı ve ayarlayıcılar
public String getNom() {
return nom;
}
public void setNom(String nom) {
initNom(nom);
}
public int getVoix() {
return voix;
}
public void setVoix(int voix) {
initVoix(voix);
}
public int getSieges() {
return sieges;
}
public void setSieges(int sieges) {
initSieges(sieges);
}
public boolean isElimine() {
return elimine;
}
public void setElimine(boolean elimine) {
this.elimine = elimine;
}
}
- 3. satır: sınıf, [AbstractEntity] sınıfını genişletir;
- 6-9. satırlar: sınıf, [LISTES] tablosunun [NOM, VOIX, SIEGES, ELIMINE] sütunlarını depolar;
7.6. [DAO] katmanının Spring yapılandırması
![]() |
![]() |
[AppConfig] sınıfı, veritabanına erişimi aşağıdaki şekilde yapılandıran bir Spring yapılandırma sınıfıdır:
package elections.dao.config;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan(basePackages = { "elections.dao.service" })
@EnableCaching
public class AppConfig {
// sabitler
public final static String URL = "jdbc:mysql://localhost:3306/dbelections";
public final static String USER = "root";
public final static String PASSWD = "";
public final static String DRIVER_CLASSNAME = "com.mysql.jdbc.Driver";
public final static String SELECT_LISTES = "SELECT ID, VERSION, NOM, VOIX, SIEGES, ELIMINE FROM LISTES";
public final static String SELECT_CONF = "SELECT ID, VERSION, SAP, SEUILELECTORAL, SAP FROM CONF";
public final static String UPDATE_LISTES = "UPDATE LISTES SET VOIX=?, SIEGES=?, ELIMINE=? WHERE ID=?";
@Bean
public DataSource dataSource() {
// veri kaynağı TomcatJdbc
DataSource dataSource = new DataSource();
// erişim yapılandırması JDBC
dataSource.setDriverClassName(DRIVER_CLASSNAME);
dataSource.setUsername(USER);
dataSource.setPassword(PASSWD);
dataSource.setUrl(URL);
// başlangıçta açık bir bağlantı
dataSource.setInitialSize(1);
// sonuç
return dataSource;
}
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("electionsConfig");
}
}
- 25-38. satırlar: BD'e erişim, [tomcat-jdbc] veri kaynağı aracılığıyla sağlanacaktır. Bu tür bir kaynak, 6.5. paragrafta kullanılmış ve açıklanmıştır;
- 17-23. satırlar: projedeki tüm sınıflar tarafından erişilebilen bir dizi statik sabit;
- 13. satır: [@Configuration] anotasyonu, [AppConfig] sınıfını bir Spring yapılandırma sınıfı haline getirir;
- 11. satır: [@ComponentScan] anotasyonu, Spring nesnelerinin bulunabileceği paketleri belirtir. Burada, [dao] paketinde bir Spring nesnesi tanımlayacağız. [@ComponentScan] anotasyonu, sınıfı bir yapılandırma sınıfı haline getirir ve bu sayede [@Configuration] anotasyonunu eklememize gerek kalmaz;
- 12. satır, önbellek yönetimini etkinleştirir. Prensip şöyledir:
- M yöntemine [@Cacheable('nom_du_cache')] anotasyonu eklenir. 'önbellek_adı', 41. satırda kullanılan addır;
- M yöntemi ilk kez çağrıldığında, sonuçları döndürülür ve aynı zamanda anotasyonla belirtilen önbelleğe kaydedilir;
- M yöntemi, ilk seferkiyle aynı parametrelerle sonraki seferler çağrıldığında, yöntem çalıştırılmaz ve Spring sadece önbelleğe alınmış değerleri döndürür;
7.7. Günlüklerin yapılandırılması
![]() |
Günlük kütüphaneleri, [pom.xml]'teki aşağıdaki bağımlılıkla tanımlanır:
<!-- Spring Boot Günlüğü -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
Bu bağımlılık, aşağıdaki kütüphaneleri getirir:
![]() |
Günlükleri sağlayacak olan kütüphane [logback]'tir. Bu kütüphane, iki dosya ile yapılandırılır:
- [logback.xml], kodun ana dalı için;
- [logback-test.xml], kodun test dalı için. Bu dosya yoksa, bir önceki dosya kullanılır;
Bu iki dosya, projenin [Classpath] dizininde bulunmalıdır. Bu nedenle, dosyalar şu klasöre yerleştirilmiştir:
- [src / main / ressources], kodun ana dalı için;
- [src / test / ressources], kodun test dalı için;
Dosyaların içeriği her ikisinde de aynıdır:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- kodlayıcılara varsayılan olarak tür atanır
ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- günlük seviyesi kontrolü -->
<root level="info"> <!-- bilgi, hata ayıklama, uyarı -->
<appender-ref ref="STDOUT" />
</root>
</configuration>
Her şey 12. satırda gerçekleşir; burada istenen bilgi düzeyi belirlenir:
- [debug]: en ayrıntılı seviye;
- [off]: günlük yok;
- [info]: normal günlük seviyesi;
- [warn]: [info] seviyesiyle aynı, ayrıca uyarı mesajları (warning) da içerir. Bu mesajlar bir hata olasılığını gösterir;
Çalıştırma sırasında hatalar ortaya çıkar çıkmaz [debug] moduna geçin.
7.8. [DAO] katmanının uygulanması
![]() |
![]() |
[DAO] katmanının [IDao] arayüzü aşağıdaki gibidir:
package istia.st.elections.webapi.client.dao;
import istia.st.elections.webapi.client.entities.ElectionsConfig;
import istia.st.elections.webapi.client.entities.ListeElectorale;
public interface IDao {
// seçim yapılandırması
public ElectionsConfig getElectionsConfig();
// yarışan listeler
public ListeElectorale[] getListesElectorales();
// seçim sonuçlarının kaydedilmesi
public void setListesElectorales(ListeElectorale[] listesElectorales);
}
[dao] katmanını bir veritabanıyla uygulayan [ElectionsDaoJdbc] sınıfının iskeleti şu şekilde olacaktır:
package elections.dao.service;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import elections.dao.entities.ElectionsConfig;
import elections.dao.entities.ElectionsException;
import elections.dao.entities.ListeElectorale;
@Component
@SuppressWarnings("unused")
public class ElectionDaoJdbc implements IElectionsDao {
@Autowired
private DataSource dataSource;
@Cacheable("electionsConfig")
// seçim yapılandırmasının alınması
public ElectionsConfig getElectionsConfig() {
throw new RuntimeException("[getElectionsConfig] not yet implemented");
}
// listelerin alınması
public ListeElectorale[] getListesElectorales() {
throw new RuntimeException("[getListesElectorales] not yet implemented");
}
// listelerin değiştirilmesi [voix, sieges, elimine]
public void setListesElectorales(ListeElectorale[] listesElectorales) {
throw new RuntimeException("[setListesElectorales] not yet implemented");
}
// -------------------- özel yöntemler
// finally yönetimi
private ElectionsException doFinally(int code, ResultSet rs, PreparedStatement ps, Connection connexion) {
// kapanış ResultSet
if (rs != null) {
try {
rs.close();
} catch (SQLException e1) {
}
}
// kapatma [PreparedStatement]
if (ps != null) {
try {
ps.close();
} catch (SQLException e2) {
}
}
// bağlantıyı kapat
if (connexion != null) {
try {
connexion.close();
} catch (SQLException e3) {
// bir [ElectionsException] döndürülür
return new ElectionsException(code, e3);
}
}
// istisna yok
return null;
}
// yakalama yönetimi
private ElectionsException doCatchException(int code1, int code2, Connection connexion, Throwable th) {
// [ElectionsException] oluşturulur
ElectionsException ex1 = new ElectionsException(code1, th);
// işlem iptali
try {
if (connexion != null) {
connexion.rollback();
}
} catch (SQLException e2) {
}
// istisna döndürülür
return ex1;
}
}
- 24. satır: [@Cacheable] anotasyonu, [getElectionsConfig] yönteminin sonuçlarının bellekte saklanmasını ("cache") talep eden bir Spring anotasyonudur. [CONF] tablosunun içeriği hiçbir zaman değişmediği için bunu burada yapmak mümkündür. Bu anotasyonu [getListesElectorales] yöntemine eklemek mümkün olmazdı, çünkü [LISTES] tablosunun içeriği zamanla değişir;
- [doCatchException] ve [doFinally] yöntemleri, [ElectionsException] türünde bir değer döndürür. [doFinally] yöntemi, kaynakların serbest bırakılması hatasız gerçekleştiyse bir null işaretçisi döndürür;
Yapılacak çalışma: 6.5.2. paragrafında incelenen [IntroJdbc02] sınıfından esinlenerek [ElectionDaoJdbc] sınıfını yazınız.
7.9. [Main] test sınıfı
![]() |
![]() |
[Main] sınıfı şu şekildedir:
package elections.dao.console;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import elections.dao.config.AppConfig;
import elections.dao.entities.ElectionsConfig;
import elections.dao.entities.ElectionsException;
import elections.dao.entities.ListeElectorale;
import elections.dao.service.IElectionsDao;
public class Main {
// veri kaynağı
private static IElectionsDao dao;
public static void main(String[] args) {
// Spring bağlamı oluşturulduktan sonra [DAO] katman referansı alınır
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class)) {
// veri kaynağının alınması
dao = ctx.getBean(IElectionsDao.class);
} catch (Exception e) {
System.out.println("Les erreurs suivantes se sont produites lors de l'initialisation du contexte Spring -------");
for (String erreur : getErreursForThrowable(e)) {
System.out.println(erreur);
}
// son
return;
}
// BD'in okunması
ElectionsConfig electionsConfig = null;
ListeElectorale[] listes;
try {
// iki tablonun içeriği
electionsConfig = dao.getElectionsConfig();
listes = dao.getListesElectorales();
} catch (ElectionsException e) {
System.out.println("Les erreurs suivantes se sont produites lors de la lecture des tables ----------");
for (String erreur : e.getErreurs()) {
System.out.println(erreur);
}
// son
return;
}
// her şey yolunda gitti - görüntüleme
System.out.println(String.format("Nombre de sièges à pourvoir : %d", electionsConfig.getNbSiegesAPourvoir()));
System.out.println(String.format("Seuil électoral : %5.2f", electionsConfig.getSeuilElectoral()));
System.out.println("Listes candidates----------------");
for (ListeElectorale liste : listes) {
System.out.println(liste);
}
}
// özel yöntemler ------------------
private static List<String> getErreursForThrowable(Throwable th) {
// istisnanın hata mesajları listesi alınır
Throwable cause = th;
List<String> erreurs = new ArrayList<>();
while (cause != null) {
// mesajı yalnızca !=null ve boş değilse alıyoruz
String message = cause.getMessage();
if (message != null) {
message = message.trim();
if (message.length() != 0) {
erreurs.add(message);
}
}
// sonraki neden
cause = cause.getCause();
}
return erreurs;
}
}
- 21-31. satırlar: [DAO] katmanından bir referans alınır;
- 21. satır: try_with_resources adlı bir sözdizimi kullanılır. Sözdizimi şöyledir:
try (T ressource=expression) {
// kaynak kullanımı
...
}
- (devam)
- 1. satırdaki T türü, [java.lang.AutoCloseable] arayüzünü uygulamalıdır;
- 1-4. satırlardan oluşan bloğun çıkışında, istisna olsun ya da olmasın, [java.lang.AutoCloseable] türündeki kaynak otomatik olarak serbest bırakılır. C# dilini bilenler, burada using (T kaynak=ifade) cümlesinin sözdizimsel ve işlevsel bir benzerini fark edeceklerdir;
- 40-49. satırlar: [DAO] katmanının kullanılmasıyla [CONF] ve [LISTES] tablolarının içeriğinin alınması;
- 41-47. satırlar: [electionsconfig] önbelleği test ediliyor. Bu önbellek iki yerde tanımlanmıştır:
- [ElectionsDaoJdbc] sınıfında:
@Cacheable("electionsConfig")
public ElectionsConfig getElectionsConfig() {
- (devamı)
- [AppConfig] yapılandırma sınıfında:
@EnableCaching
public class AppConfig {
...
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("electionsConfig");
}
}
- 59. satır: Spring bağlamının kapatılması;
- 62-67. satırlar: elde edilen bilgilerin görüntülenmesi;
[DAO] katmanı uygulandığında elde edilen sonuçlar şunlardır:
- 2-4. satırlar: önbelleğin etkisini gösterir:
- 1. istek 80 milisaniye sürer;
- 2. sorgu 1 milisaniye sürer;
Veritabanı devre dışı bırakıldığında şu sonuçlar elde edilir:
7.10. [ElectionsDaoJdbc] sınıfının JUnit testleri
![]() |
![]() |
[Test01] sınıfı, [JUnit] sınıfının bir sonraki test sınıfıdır:
package elections.dao.junit;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import elections.dao.config.AppConfig;
import elections.dao.entities.ElectionsConfig;
import elections.dao.entities.ListeElectorale;
import elections.dao.service.IElectionsDao;
@SpringApplicationConfiguration(classes = AppConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class Test01 {
// [DAO] katmanı
@Autowired
private IElectionsDao electionsDao;
@Before
public void init() {
// [LISTES] tablosu temizlenir
// yarışan listeler
ListeElectorale[] listes = electionsDao.getListesElectorales();
// oy ve koltuk sayılarını 0'a sıfırlayıp false olarak siliniyor
int voix = 0;
int sièges = 0;
boolean elimine = false;
for (ListeElectorale liste : listes) {
liste.setVoix(voix);
liste.setSieges(sièges);
liste.setElimine(elimine);
}
// [dao] katmanı sayesinde bu verileri kalıcı hale getiriyoruz
electionsDao.setListesElectorales(listes);
}
@Test
public void testElections01() {
...
}
}
- 17. satır: [RunWith] açıklaması, [JUnit] (6. satır) açıklamasının bir parçasıdır ve [SpringJUnit4ClassRunner] sınıfı aracılığıyla Spring ile entegrasyonu sağlar;
- 16. satır: [SpringApplicationConfiguration] anotasyonu, [Spring] anotasyonunun (8. satır) bir örneğidir ve JUnit test yapılandırma sınıflarını belirlemeye olanak tanır. Burada, projeyi yapılandırmak için kullanılan [AppConfig] sınıfı belirtilmektedir. Böylece, bu yapılandırma sınıfı tarafından tanımlanan tüm Spring nesnelerine erişilebilir hale gelir. Bu sayede, 21-22. satırlarda test edilecek olan [DAO] katmanına bir referans enjekte edilebilir;
- 25. satır: [Before] anotasyonu, her testten önce yürütülmesi gereken bir yöntemi belirtir;
- 26-41. satırlar: [init] yöntemi, [LISTES] tablosundaki listelerin oy ve koltuk sayılarını sıfıra, [elimine] boole değerini ise [false]'e ayarlar;
Tek test yöntemi şöyledir:
@Test
public void testElections01() {
System.out.println("testElections01-------------------------------------");
// seçim yapılandırmasının alınması
ElectionsConfig electionsConfig = electionsDao.getElectionsConfig();
int nbSiegesAPourvoir = electionsConfig.getNbSiegesAPourvoir();
double seuilElectoral = electionsConfig.getSeuilElectoral();
Assert.assertEquals(6, nbSiegesAPourvoir);
Assert.assertEquals(0.05, seuilElectoral, 1E-6);
// yarışan listeler
ListeElectorale[] listes = electionsDao.getListesElectorales();
// okunan değerlerin görüntülenmesi
System.out.println("Nombre de sièges à pourvoir : " + nbSiegesAPourvoir);
System.out.println("Seuil électoral : " + seuilElectoral);
System.out.println("Listes en compétition ---------------------");
for (int i = 0; i < listes.length; i++) {
System.out.println(listes[i]);
}
// listelere oy ve sandalye tahsis ediliyor
int voix = 0;
int sièges = 0;
boolean elimine = false;
for (ListeElectorale liste : listes) {
liste.setVoix(voix);
liste.setSieges(sièges);
liste.setElimine(elimine);
voix += 10;
sièges += 1;
elimine = !elimine;
}
// bu veriler [dao] katmanı aracılığıyla kalıcı hale getirilir
electionsDao.setListesElectorales(listes);
// veriler yeniden okunur
ListeElectorale[] listesElectorales2 = electionsDao.getListesElectorales();
// okunan veriler kontrol edilir
Assert.assertEquals(7, listesElectorales2.length);
voix = 0;
sièges = 0;
elimine = false;
for (ListeElectorale liste : listesElectorales2) {
Assert.assertEquals(voix, liste.getVoix());
Assert.assertEquals(sièges, liste.getSieges());
Assert.assertEquals(elimine, liste.isElimine());
voix += 10;
sièges += 1;
elimine = !elimine;
}
System.out.println("Listes en compétition ---------------------");
for (int i = 0; i < listes.length; i++) {
System.out.println(listes[i]);
}
}
- 5-9. satırlar: [CONF] tablosunun içeriğinin alınabildiğinden emin olunur;
- 11-19. satırlar: [LISTES] tablosunun içeriği görüntülenir. Burada test yapılmaz, sadece görsel kontrol yapılır;
- 21-35. satırlar: Veritabanındaki [LISTES] tablosunu, listelere [voix, sieges, elimine] alanları için değerler atayarak değiştiriyoruz;
- satır 37-51: [LISTES] tablosu yeniden okunur ve elde edilen sonucun girilen değerlerle tam olarak eşleştiği kontrol edilir;
- satır 52-55: görsel kontrol;
[DAO] katmanı uygulandığında elde edilen konsol sonuçları şunlardır:
- satır 1-23: Spring Test günlükleri;
- satır 25-26: [CONF] tablosunun içeriği;
- satır 27-34: [LISTES] tablosunun başlangıç içeriği;
- satır 35-42: [voix, sieges, elimine] alanlarına değerler atandıktan sonra [LISTES] tablosunun içeriği;
Ayrıca, JUnit testi başarılı oldu:
![]() |
7.11. [dao] katmanından [with-dependencies] arşivinin oluşturulması
Nihai projenin mimarisi şu şekildedir:
![]() |
Projenin Maven yapılandırmasını hatırlayalım:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.elections</groupId>
<artifactId>elections-dao-jdbc-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<dependencies>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- Tomcat Jdbc -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency>
<!-- jSON kütüphanesi -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Testi -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Günlüğü -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
</dependencies>
<!-- eklentiler -->
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>config.AppConfig</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- proje artefaktını yerel Maven deposuna yüklemek için -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
![]() |
Yukarıdaki tüm jar dosyalarındaki sınıfları ve [DAO] katman projesindeki sınıfları içeren tek bir jar dosyası oluşturacağız. Bu, [pom.xml] dosyasında yapılacak bir değişiklikle gerçekleştirilir:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.elections</groupId>
<artifactId>elections-jdbc-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.2.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
...
</dependencies>
<!-- eklentiler -->
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>console.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 25-37. satırlar: jar dosyasını oluşturmak için bir Maven eklentisi yapılandırır;
- 15. satır: derleme için kullanılacak Java sürümünü belirtir;
Bu değişiklik yapıldıktan sonra, jar dosyası şu şekilde oluşturulabilir: [1-10]:
![]() |
![]() |
- [5]'te, [6] düğmesini kullanarak proje klasörünü seçin;
- [7]'te, çalıştırma yapılandırmasına bir ad verin;
- [8] dosyasında, çalıştırılacak Maven hedeflerinin listesini girin:
- [clean]: Oluşturulan jar dosyasının yerleştirileceği projenin [target] klasörünü boşaltır;
- [compile]: projeyi derler;
- [assembly:single]: projenin ve bağımlılıklarının tüm sınıflarını içeren tek bir jar dosyası oluşturur;
![]() |
- [9-10] komutunda, JDK (Java Development Kit) dosyasının bulunduğundan emin olun; JRE (Java Runtime Environment) dosyası olmamalıdır. Aradaki fark, JDK'te derleyici bulunurken JRE'te bulunmamasıdır. JDK sürümüne sahip değilseniz, [10] sürümüne [11] sürümünü eklemeniz gerekir. Bunun için 3.1. paragrafında açıklanan prosedürü izleyin;
![]() |
- [17]'te, yürütme yapılandırmasını gerçekleştirin;
- [13]'e, oluşturulan arşivi;
Bu arşivi bir açma programı ile açabilirsiniz:
![]() |
7.12. [DAO] katman arşivini test etme
Standart bir Eclipse projesi (Maven değil) [1] oluşturalım:
![]() |
[elections-dao-jdbc-01] projesindeki [dao.console] paketini [elections-dao-jdbc-02] ve [2] projelerine kopyalayıp yapıştıralım. [Main] sınıfı, kendi [Classpath]'inde bulunmayan sınıflara referans verdiği için birçok hata ortaya çıkıyor. Bunu değiştireceğiz.
Öncelikle yeni projede [2-8] adlı bir klasör oluşturuyoruz:
![]() |
![]() |
[9]'te, önceki adımda oluşturulan arşivi [lib] klasörüne yerleştiriyoruz ve ardından projenin Build Path'ini değiştiriyoruz:
![]() |
![]() |
![]() |
- olarak değiştiriyoruz; daha önce oluşturulan [DAO] katman arşivini içe aktardık;
![]() |
- [19] dosyasında, projede artık hata bulunmamaktadır;
Böylece [Main] sınıfını çalıştırabiliriz. Daha önce elde edilen sonuçların aynısı elde edilir.







































