Ana içeriğe geç

1.7. Data#

Uygulamalarda veri katmanında kullanılacak fonksiyonların sağlandığı altyapı bileşenidir. ORM işlemleri için JPA ve QueryDSL teknolojileri tercih edilip bu teknolojiler üzerinde özelleştirmeler yapılmıştır. Auditing altyapısı için Javers ve Envers teknolojilerine destek verilmektedir. Aynı zamanda sorgulama, view, auditing için özelleştirilmiş yetenekler sağlamaktadır ve ldap, redis gibi teknolojilerin veri katmanı destekleri de bu bileşende bulunmaktadır. Başlıca sağlanan katmanlar:

  • Javers, Envers
  • Model
  • Entity
  • JDBC İlklendirme
  • PreLiquibase
  • Model Çeviricileri
  • Hibernate Anotasyonları

Converter#

Converter bileşeni Hibernate, JPA teknolojilerine bağlı olmadan nesneler arası dönüştürücüleri sağlamaktadır. Aynı zamanda enum sınıfların ordinal dışında veritabanı işlemleri için dönüştürücüsünü sağlamaktadır.

NOT: HvlGenericConverter ve HvlGenericHibernateConverter sınıfları runtime anında reflection ile çalıştığı için performansal olarak önerilmemektedir. HvlGenericHibernateConverter dönüştürücüsü hibernate annotation'larına göre davranış göstermektedir. Herhangi bir veritabanı bağlılığı gerekmemektedir.


drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-data-converter'

JPA#

Veritabanı işlemlerini sağlayan altyapı bileşenidir. Veritabanı işlemlerini yapacak olan sınıflar declarative olarak sağlanmaktadır.

JPA kullanırken transaction kullanımı önemlidir. Transaction için @Transactional annotation'ını kullanılmaktadır. @Transactional annotation'ını unchecked exception'larda direkt rollback almaktadır. Fakat checked işlemleri için geliştiricinin bu durumu ele alması beklenmektedir. Altyapıda bütün checked hatalar için rollback aktif olması için @HvlTransactionalRollbackForCheckedException annotation'ı bulunmaktadır. Sorgulama işlemleri gibi read only işlemler için ise @HvlTransactionalReadOnly anotasyonu bulunmaktadır. Bu anotasyonlarda transaction'ı özelleştirebileceğiniz isolation, propogation gibi yetenekler de sağlanmaktadır.

Altyapıda veritabanı işlemlerinde kolon bazlı şifreleme yeteneği bulunmaktadır. @Convert annotation'ı ile kullanılmaktadır.

import javax.persistence.Convert;

@Convert(converter = HvlStringCryptoConverter.class)
private String identityNumber;

NOT: Kolon şifreleme aktif etmek için 'application-database-datasource.yml' içerisinde spring.jpa.properties.hibernate.encryption_enabled alanının true yapılması gerekmektedir. Altyapı olarak şifreleme algoritması olarak şu anda AES desteklenmektedir. AES üzerinden şifreleme işleminde kullanılacak olan key değeri spring.jpa.properties.hibernate.encryption_key alanından verilmesi gerekmektedir.

Altyapı kullanılarak yapılan veritabanı işlemlerinde işlemi kimin yaptığı, tarihi gibi işlem sırasında otomatik verilmesi gereken değerler HvlJpaAwareProvider sınıfı ile yönetilmektedir. Geliştiriciler tarafından özelleştirilebilir.

JPA ile ilgili içeriğe ulaşmak için HvlJpaContext veya HvlJpaContextHolder sınıfı kullanılmalıdır.

Veritabanı işlemleri için altyapıdan PostgreSQL ve Oracle veritabanları için dialect bulunmaktadır. Bu dialect'ler özelleştirilmiştir. Örneğin; PostgreSQL için ilike vb. fonksiyonlar eklenmiştir.

NOT: Dialect, 'application-database-datasource.yml' içerisinde spring.jpa.properties.hibernate.dialect alanınından yönetilmektedir. Kullanılmak istenen dialect paket bilgisi ile verilmelidir. Postgresql için tr.com.havelsan.javarch.data.jpa.dialect.postgresql.HvlPostgreSQL10Dialect ve Oracle için tr.com.havelsan.javarch.data.jpa.dialect.oracle.HvlOracleSQL12cDialect altyapıdan sağlanan dialect'lerdir.

Altyapının sağladığı declarative repository sınıflarını kullanabilmek için konfigurasyon sınıfına aşağıdaki annotation eklenmelidir.

import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import tr.com.havelsan.javarch.data.jpa.factory.HvlJpaRepositoryFactoryBean;

@EnableJpaRepositories(basePackages = {"base_package"},
        repositoryFactoryBeanClass = HvlJpaRepositoryFactoryBean.class)

Veritabanı sorguları için Querydsl teknolojisi kullanılmaktadır. Altyapıya özgü alanların varsayılan değerlerinin verildiği ve sorgu (predicate) oluşturmaya yarayan HvlBaseQueryGenerator sınıfından türeyen HvlEntityQueryGenerator ve HvlViewQueryGenerator sınıfı bulunmaktadır.

NOT: Sorgulama işlemi için bu sınıflardan türeyen sınıfları kullanmak önerilmektedir. 'Soft delete' gibi özel yapılar için yapılan değişiklikler desteklenmektedir. Geliştiricinin ekstra kontrol veya ekleme yapmasına gerek yoktur.

Veritabanı işlemleri için altyapı tarafından 4 tane arayüz sağlanmaktadır:

  • HvlJpaRepository: HvlEntity sınıfından türeyen sınıflar için kullanılan arayüzdür.
  • HvlJpaSearchRepository: Sorgulama ile ilgili fonksiyonların sağlandığı arayüzdür.
  • HvlJpaSimpleRepository: HvlSimpleEntity sınıfından türeyen sınıflar için kullanılan arayüzdür.
  • HvlJpaViewRepository: HvlView annotation'a sahip entity sınıfları için persist yeteneklerinin bulunmadığı arayüzdür.

Validasyon işlemleri için 2 adet anotasyon sağlanmaktadır:

  • @HvlIdValid: Model ve entity içerisindeki obje alanlarda id alanı validasyonu yapmak için kullanılır.
    public class HvlOAuthUserModel extends HvlModel {
    
        ..
    
        @HvlIdValid
        private HvlOAuthUserDetailModel userDetail;
    
        ..
    }
    
  • @HvlUuidValid: Model ve entity içerisindeki obje alanlarda uuid alanı validasyonu yapmak için kullanılır.
    public class HvlOAuthProfileDetailModel extends HvlModel {
    
       ..
    
        @NotNull
        @HvlIdValid
        private HvlOAuthProfileDetailTypeModel profileDetailType;
    
        @HvlUuidValid
        private HvlOAuthProfileDetailModel parent;
    
        ..
    
    }
    

drawing Nasıl konfigure edilir?

hvl-infra altında bulunan 'application-database-datasource.yml, application-hvl-data.yml' dosyasıyla konfigure edilebilmektedir.


drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-data-jpa'

Dinamik Güncelleme (Dynamic Update)#

hvl-infra projesinde application-database-datasource.yml içerisindeki hvl.jpa.properties.hibernate.enable_dynamic_update property'si varsayılan olarak true değerini almaktadır. Bundan dolayı EKSEN altyapısı ile dynamic update yeteneği otomatik olarak devreye girecektir.

Eğer sınıfta 'dynamic update' çalışması istenmiyorsa @HvlDynamicUpdateExclude annotation'ı entity üzerine eklenmelidir.

@Entity
@Table(name = TABLE_NAME)
@HvlDynamicUpdateExclude
public class HvlOAuthRole extends HvlSoftDeleteEntity {

}

NOT: 'dynamic update' performans için önemlidir. Varitabanı işlemlerinde 'update' işleminde sadece değişen alanların gönderilmesini sağlamaktadır.

Aşağıdaki kod bloğunun çalıştırıldığı varsayılırsa;

final HvlOAuthRole editableRole = roleRepository.getById(roleAssignerPersistModel.getRoleId());
editableRole.setName("Updated role name");
editableRole.setDescription("Updated role description");
roleRepository.updateWithoutFind(editableRole);

Dynamic update pasif durumda iken atılan query aşağıdaki gibi olacaktır.

update oauth.kys_role
set code='adminRole',
    created_at='2023-12-26 15:57:43.713131',
    updated_at='2023-12-26 15:57:43.713131',
    created_by='hvltest1',
    updated_by='hvltest1',
    obj_version=1,
    deleted=0,
    deleted_at=null,
    name='Updated role name',
    description='Updated role description',
    hierarchical=0,
    editable=0,
    enabled=0
where id = 51
  and uuid = 'ROLE_UUID_00000000000000000000000006'
  and obj_version = 0;

Dynamic update aktif durumda iken ise aşağıdaki şekilde olacaktır.

update oauth.kys_role
set name='Updated role name',
    description='Updated role description',
    updated_at='2023-12-27 15:04:30.211457',
    obj_version=1
where id = 51
  and uuid = 'ROLE_UUID_00000000000000000000000006'
  and obj_version = 0;

Yukarıdaki örneklerlede de görüldüğü üzere büyük veritabanı operasyonlarında performans artışı sağlandığı gözlenebilir.

Envers#

Veritabanı işlemlerinin izini tutmak için Envers teknolojisi kullanılmaktadır. Envers, nesnenin üzerinde yapılan işlemleri nesnenin tablo isminin sonuna _AUD eklediği tablo üzerinde tutmaktadır.

Altyapıdan gelen declarative repository kullanımı için aşağıdaki konfigurasyon eklenmelidir.

import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import tr.com.havelsan.javarch.data.jpa.envers.factory.HvlEnversJpaRepositoryFactoryBean;

@EnableJpaRepositories(basePackages = {"base_package"},
        repositoryFactoryBeanClass = HvlEnversJpaRepositoryFactoryBean.class)

EKSEN, envers veritabanı işlemleri için altyapı tarafından 4 tane arayüz sağlanmaktadır:

  • HvlEnversJpaRepository: HvlEntity sınıfından türeyen sınıflar için kullanılan arayüzdür.
  • HvlEnversJpaLocalizedRepository: HvlLocalizedEntity sınıfından türeyen sınıflar için kullanılan arayüzdür.

drawing Nasıl konfigure edilir?

hvl-infra altında bulunan 'application-database-datasource.yml, application-hvl-data.yml' dosyasıyla konfigure edilebilmektedir.


drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-data-jpa-envers'

HvlEnversRevisionEntity Kullanımı#

HvlEntity gibi EKSEN entity'leri kullanılan sistemlerde envers aktif edildiği durumlarda EKSEN'e ait HvlEnversRevisionEntity nesnesi aşağıdaki gibi kullanılarak uygun revision tablosu üretilmelidir. Aksi durumlarda uyumsuzluklar görülebilir.

package tr.com.havelsan.javarch.system.setting.starter.entity;

import com.querydsl.core.annotations.QueryExclude;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import org.hibernate.envers.RevisionEntity;
import tr.com.havelsan.javarch.data.jpa.envers.entity.HvlEnversRevisionEntity;
import tr.com.havelsan.javarch.data.jpa.envers.listener.HvlEnversRevisionListener;
import tr.com.havelsan.javarch.system.setting.starter.entity.constant.HvlSettingEntityConstant;

import static tr.com.havelsan.javarch.system.setting.starter.entity.HvlSettingEnversRevisionEntity.TABLE_NAME;

/**
 * Revision entity for Setting.
 *
 * @author isekeroz
 */
@QueryExclude
@Entity
@Table(name = TABLE_NAME)
@RevisionEntity(HvlEnversRevisionListener.class)
public class HvlSettingEnversRevisionEntity extends HvlEnversRevisionEntity {

    //**********************************************************
    //* Table Name
    //**********************************************************

    /**
     * The constant TABLE_NAME.
     */
    public static final String TABLE_NAME = HvlSettingEntityConstant.TABLE_PREFIX + "AUDIT_REVISION";

}

Bu şekilde hibernate'in default olarak oluşturduğu revinfo tablosu yerine, TABLE_NAME değerinde verdiğiniz isimde bir revision tablosu oluşmuş olacaktır. Buna ek olarak _AUD ile biten tabloların sequenceleri de EKSEN entity yapısına uygun şekilde oluşmuş olacaktır.

Audit İşlemlerinin Gerçek Zamanlı Olarak Yönetilebilmesi#

EKSEN, özel envers servisleri sunarak, gerçek zamanlı olarak audit işlemlerinin aktif/pasif duruma getirilmesini sağlamaktadır. Bu 2 yöntemle desteklenmektedir. In-memory ve redis.

Dağıtık sistemlerde redis kullanımı önerilirken, daha basit küçük projelerde in-memory kullanım da tercih edilebilir.

Bağımlılıklar:

In-memory kullanım için:

group: 'tr.com.havelsan.framework.data', name: 'hvl-data-jpa-envers-service'

Redis üzerinden kullanım için:

group: 'tr.com.havelsan.framework.data', name: 'hvl-data-jpa-envers-redis-service'

Varsayılan olarak tüm auditler aktif olacak şekilde düşünüldüğü için, audit özelliğinin kapatılması istenen entity sınıfının bilgisinin HvlEnversContextHolder üzerindeki unregister methodlarına verilmesi gerekmektedir.

try {
    HvlEnversContextHolder.unregister("tr.com.havelsan.javarch.oauth.jpa.data.provider.module.user.entity.HvlOAuthUser");
} catch (HvlEnversServiceException e) {
    // handle exception
}

Örnekteki kod bloğu çalıştığında HvlOAuthUser entity'sinin audit özelliği runtime'da kapalı hale gelecektir ve sonraki işlemler için USER_AUD tablosuna kayıt atmayacaktır.

Session Aware#

Veritabanı işlemleri sırasında mevcut session'daki kullanıcı bilgisini ve işlem yapılan tarih saat bilgisini sağlayan EKSEN bileşenidir. Bu bilgilerin veritabanı kayıtlarına otomatik olarak eklenmesi isteniyorsa bu bileşen kullanılmalıdır.


drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-data-jpa-session-aware-configuration'

NOT: Özelleştirilmesi gereken durumlarda bu bileşen devreden çıkarılmalı ve HvlJpaAwareProvider arayüzü implement edilmelidir.

Redis Second Level Cache#


drawing Nasıl konfigure edilir?

hvl-infra altında bulunan 'application-database-datasource.yml, redisson-slc.yaml' dosyasıyla konfigure edilebilmektedir.


drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-data-jpa-slc-redis'

Redis#

Redis'in manuel olarak kullanılması gerektiği durumlar için EKSEN tarafından sağlanan bileşendir. RedisTemplate sınıfı bean olarak EKSEN tarafından konfigüre edilerek sağlanır.


drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-data-jpa-slc-redis'

Domain Model#

EKSEN tarafından sağlanan Entity sınıflarının sunulduğu bileşendir. Aynı zamanda UUID generator, Sequence generator, veritabanı işlemi esnasında UUID ayarlama özellikleri bu bileşenden sağlanmaktadır. hvl-data-jpa bileşeninin kullanıldığı durumlarda bu bileşen de gelmektedir.

Sağlanan Entity sınıfları şunlardır:

  • HvlEntity: Temel entity sınıfıdır.
  • HvlHardDeleteEntity
  • HvlSoftDeleteEntity
  • HvlLocalizedEntity
  • HvlLookupEntity
  • HvlSimpleEntity: id bilgisi içermeyen entity sınıfıdır.

Sağlanan UUID Generator strategy sınıfları şunlardır:

  • HvlEntityDCESecurityBasedUUIDGeneratorStrategy
  • HvlEntityDCESecurityBasedUUIDGeneratorStrategy
  • HvlEntityRandomBasedUUIDGeneratorStrategy
  • HvlEntityTimeBasedUUIDGeneratorStrategy
  • HvlEntityTimeOrderedBasedUUIDGeneratorStrategy
  • HvlEntityTimeOrderedWithMACBasedUUIDGeneratorStrategy
  • HvlEntityTimeWithMACBasedUUIDGeneratorStrategy

drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-domain-model'

DTO Model#

EKSEN tarafından sağlanan Model sınıflarının sunulduğu bileşendir.

Sağlanan modeller şunlardır:

  • HvlModel: Temel model sınıfıdır.
  • HvlLocalizedModel
  • HvlLookupModel
  • HvlSimpleModel: id bilgisi içermeyen modeldir.
  • HvlQueryModel: Sorgulama işlemleri için kullanılan temel modeldir.

drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-dto-model'

Hibernate Annotations#

EKSEN tarafından Hibernate için sağlanan özelliklerin kullanılması için oluşturulmuş olan anotasyonların sağlandığı altyapı bileşenidir. Şu anotasyonlar sağlanmaktadır:

  • @HvlEntitySequence: Bu anotasyon kullanılarak özel sequence isimlendirilmesi yapılabilir. Kullanılmadığı durumlarda veritabanı nesnesindeki tablo ismi ile isimlendirme yapılır.
  • @HvlView: Veritabanında view olarak kullanılacak nesnelerin işaretlenmesini ve maplenmesini sağlayan anotasyondur.

drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-hibernate-annotations'

JDBC Initializer#

Veritabanı ilklendirmesinin dosya üzerinden yapılmasını sağlayan bileşendir. Bu bileşen sayesinde veritabanı ilklendirmeleri için senaryolar yazılarak farklı durumlar için ilklendirmeler tetiklenebilmektedir. Senaryo dosyaları içerisinde type bilgisi sql ve ya scenario değerlerini alabilir.

Senaryo içerisindeki elementler sırayla çalışmaktadır ve çalışan elementlerin bilgisi console'a log basılmaktadır.

NOT: Hata alan bir script olursa console'a error logu basılmaktadır ancak diğer scriptler çalışmaya devam etmektedir. Bu yüzden hata durumlarında dikkatli olunmalıdır.

prod.scenario

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Scenario>
    <ScenarioElement type="sql" path="[path]/uniqueConstraint.sql"/>
    <ScenarioElement type="sql" path="[path]/functionalIndex.sql"/>
</Scenario>

dev.scenario

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Scenario>
    <ScenarioElement type="scenario" path="[path]/prod.scenario"/>

    <ScenarioElement type="sql" path="[path]/userType.sql"/>
    <ScenarioElement type="sql" path="[path]/userDetail.sql"/>
    <ScenarioElement type="sql" path="[path]/eventType.sql"/>
    <ScenarioElement type="sql" path="[path]/user.sql"/>
</Scenario>


drawing Nasıl konfigure edilir?

hvl-infra altında bulunan 'application-hvl-data.yml' dosyasıyla konfigure edilebilmektedir.


drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-jdbc-initializer'

Liquibase Initializer#

Liquibase, veritabanı şeması üzerindeki değişiklikleri yönetmeyi ve versiyonlamayı sağlayan açık kaynaklı bir Java kütüphanesidir. Veritabanı şeması üzerindeki değişikliklerin izlenmesini, yönetilmesini ve uygulanmasını sağlar. Kaba tabirle veritabanı üzerinde versiyon kontrol sistemi kurmamıza yardımcı olur.

Preliquibase ise liquibase scriptlerinden önce çalışarak gerekli veritabanı şemasını oluşturmak gibi işlemleri yapmaktadır.

EKSEN, preliquibase ile varsayılan veritabanı şemalarını otomatik olarak oluşturmaktadır.


drawing Nasıl konfigüre edilir?

hvl-infra altında bulunan 'application-database-liquibase.yml' dosyasıyla konfigure edilebilmektedir.

Konfigürasyonları spring.liquibase ve preliquibase altında bulunmaktadır.

spring:
  liquibase:
    default-schema: ${spring.jpa.properties.hibernate.default_schema}
    change-log: ${LIQUIBASE_CHANGE_LOG:db/changelog-root.yaml}
    enabled: ${LIQUIBASE_ENABLED:false}
    drop-first: ${LIQUIBASE_DROP_FIRST:false}
    url: ${LIQUIBASE_DB_URL:${spring.datasource.url}}
    user: ${LIQUIBASE_DB_USER:${spring.datasource.username}}
    password: ${LIQUIBASE_DB_PASSWORD:${spring.datasource.password}}
    clear-checksums: ${LIQUIBASE_CLEAR_CHECKSUMS:false}
    contexts: ${LIQUIBASE_CONTEXTS:dev}
preliquibase:
  default-schema: ${spring.liquibase.default-schema}
  # sqlScriptReferences: classpath:/db/preliquibase/schema.sql
  dbPlatformCode: postgresql
  • default-schema: Liquibase scriptleri çalıştırırken şema verilmemiş scriptlerde kullanıacak varsayılan veritabanı şema bilgisidir.
  • change-log: Liquibase scriptleri çalıştırırken ilk olarak bakacağı change-log dosyasıdır. Bu dosyanın içerisindeki bilgilere göre sırası ile scriptler çalıştırılmaktadır.
  • enabled: Uygulama ayağa kalkarken liquibase'in devreye girip girmeyeceğini yöneten konfigürasyon bilgisidir.
  • drop-first: Uygulama ayağa kalkarken liquibase'in tüm tabloları temizleyip herşeyi en baştan çalıştırmasını sağlayan konfigürasyon bilgisidir.
  • url: Liquibase'in scriptlerini çalıştıracağı veritabanı url bilgisidir.
  • user: Liquibase'in scriptlerini çalıştıracağı veritabanının kullanıcı adı bilgisidir.
  • password: Liquibase'in scriptlerini çalıştıracağı veritabanının şifre bilgisidir.
  • clear-checksums: Liquibase'in scriptlerinde değişiklik olduğu durumda güncel scriptlerin baz alınacağını belirleyen konfigürasyon bilgisidir. Normal koşullarda liquibase çalıştırdığı bir script değişmişse hata vererek uygulamayı kapatır. Bu konfigürasyon true olduğu durumlarda mevcut checksum ı günceller ve hata atmadan devam eder.

    NOT: Canlı ortamlarda kurulum yapılmışsa çalışmış scriptleri güncellemek son derece tehlikelidir.

  • contexts: Liquibase'in scriptlerini çalıştırırken kullanacağı context bilgisidir. Context ile belirtilmiş changeset leri bu konfigürasyon kapsamında çalıştıracaktır. Virgül kullanılarak birden fazla değer verilebilir.

Preliquibase:

  • default-schema: Şemanın olmadığı durumlarda preliquibase tarafından oluşturulacak şema bilgisidir.
  • sqlScriptReferences: Şema oluşturulması için çalışacak script dosyasının path bilgisidir.
  • dbPlatformCode: Veri tabanı platform bilgisidir. Varsayılan olarak postgresql olarak ayarlanmıştır.

drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-liquibase-initializer'

Proje içerisinden örnek liquibase kullanım yapısı aşağıdaki gibidir.

Model Converter#

Model ve domain nesnesi (entity) arasındaki dönüşümü sağlayan EKSEN bileşenidir. Ek olarak mapstruct için HvlGenericMapStructMapper sınıfını sağlar.


drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.data', name: 'hvl-model-converter'