mannaxxx 1
mannaxxx
Agora Metin2 1
Agora Metin2
[DEV]AB 1
[DEV]AB
kaptanmikro1 1
kaptanmikro1
emirhanHCL 1
emirhanHCL
Hikaye Ekle

[NODE JS] Veritabanı Seeding ile Test Verisi Oluşturma

  • Konuyu başlatan Konuyu başlatan Silverhand
  • Başlangıç tarihi Başlangıç tarihi
  • Cevaplar Cevaplar 1
  • Görüntüleme Görüntüleme 14

Türkiye'de ilk Mobil & PC Aynı Anda Metin2 Oyna. Triarchonline kalıcı ve uzun ömürlü yapısı ile 24 Temmuz'da açılıyor. | 1-99 Mobil Metin2 Triarch HEMEN TIKLA!

nodejs.png


[NODE JS] Veritabanı Seeding ile Test Verisi Oluşturma​


Veritabanı seeding, geliştirme ve test ortamlarında kullanmak üzere anlamlı test verileri oluşturma işlemidir. Yeni bir projeye başladığınızda boş bir veritabanıyla çalışmak son derece verimsizdir. Arayüz geliştirirken gösterecek veriniz yoktur, sorguları test edemezsiniz ve uç durumları yakalayamazsınız. İşte seed dosyaları tam olarak bu sorunu çözer.

İlk profesyonel projemde seed dosyaları kullanmıyorduk. Her geliştirici kendi test verilerini elle oluşturuyordu. Bir süre sonra herkesin farklı verilerle çalışması yüzünden "bende çalışıyor" problemleri ortaya çıkmaya başladı. Seed dosyalarını devreye aldıktan sonra tüm ekip aynı veri setiyle çalışmaya başladı ve bug raporları çok daha anlamlı hale geldi.

İyi bir seed sistemi birkaç temel özelliğe sahip olmalıdır. Birincisi, idempotent olmalıdır yani birden fazla kez çalıştırıldığında aynı sonucu vermelidir. İkincisi, gerçekçi veriler üretmelidir. "test1", "test2" gibi anlamsız veriler yerine gerçek isimlere, adreslere ve tarihlere benzeyen veriler kullanılmalıdır. Üçüncüsü, ilişkisel verileri doğru sırayla oluşturmalıdır. Önce kategoriler, sonra ürünler, sonra siparişler gibi.

Faker.js kütüphanesi, gerçekçi sahte veri üretmek için harika bir araçtır. Türkçe dil desteği sayesinde Türkçe isimler, adresler ve telefon numaraları oluşturabilirsiniz. Bu, uygulamanızın Türkçe karakterleri doğru işleyip işlemediğini test etmek için de çok faydalıdır.

Seed dosyalarınızı modüler bir yapıda organize etmenizi öneririm. Her tablo veya koleksiyon için ayrı bir seed dosyası oluşturun. Bu dosyaları bir ana dosyadan doğru sırayla çalıştırın. Böylece ihtiyaç duyduğunuzda sadece belirli bir tablonun verilerini yeniden oluşturabilirsiniz.

Performans açısından, büyük miktarda veri eklerken toplu insert (bulk insert) kullanmak çok önemlidir. Her satır için ayrı bir INSERT sorgusu göndermek yerine, binlerce satırı tek bir sorguda eklemek, seeding süresini dramatik şekilde kısaltır. Sequelize'da bulkCreate, MongoDB'de insertMany bu iş için kullanılabilir.

Test verilerinin güvenlik açısından da dikkat gerektirdiğini unutmayın. Seed dosyalarında gerçek kullanıcı bilgilerini asla kullanmayın. Şifreleri düz metin olarak saklamayın, bcrypt ile hashleyin. Ve en önemlisi, seed scriptlerinin üretim ortamında çalıştırılmasını engelleyecek güvenlik kontrolleri ekleyin.

JavaScript:
// Veritabanı Seeding Sistemi - Kapsamlı Örnek
const { Sequelize, DataTypes } = require('sequelize');
const bcrypt = require('bcryptjs');
const { faker } = require('@faker-js/faker/locale/tr');

// Veritabanı bağlantısı
const sequelize = new Sequelize({
  dialect: 'sqlite',
  storage: './gelistirme.sqlite',
  logging: false
});

// ==========================================
// Model Tanımlamaları
// ==========================================
const Kullanici = sequelize.define('Kullanici', {
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  ad: { type: DataTypes.STRING, allowNull: false },
  soyad: { type: DataTypes.STRING, allowNull: false },
  eposta: { type: DataTypes.STRING, unique: true, allowNull: false },
  sifre: { type: DataTypes.STRING, allowNull: false },
  telefon: { type: DataTypes.STRING },
  sehir: { type: DataTypes.STRING },
  rol: { type: DataTypes.STRING, defaultValue: 'kullanici' },
  aktif: { type: DataTypes.BOOLEAN, defaultValue: true }
}, { tableName: 'kullanicilar', timestamps: true });

const Kategori = sequelize.define('Kategori', {
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  isim: { type: DataTypes.STRING, allowNull: false },
  aciklama: { type: DataTypes.TEXT },
  siraNo: { type: DataTypes.INTEGER, defaultValue: 0 }
}, { tableName: 'kategoriler', timestamps: true });

const Urun = sequelize.define('Urun', {
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  isim: { type: DataTypes.STRING, allowNull: false },
  aciklama: { type: DataTypes.TEXT },
  fiyat: { type: DataTypes.DECIMAL(10, 2), allowNull: false },
  stok: { type: DataTypes.INTEGER, defaultValue: 0 },
  kategoriId: { type: DataTypes.INTEGER, allowNull: false },
  aktif: { type: DataTypes.BOOLEAN, defaultValue: true },
  resimUrl: { type: DataTypes.STRING }
}, { tableName: 'urunler', timestamps: true });

const Siparis = sequelize.define('Siparis', {
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  kullaniciId: { type: DataTypes.INTEGER, allowNull: false },
  toplamTutar: { type: DataTypes.DECIMAL(10, 2) },
  durum: { type: DataTypes.STRING, defaultValue: 'beklemede' },
  adres: { type: DataTypes.TEXT },
  not: { type: DataTypes.TEXT }
}, { tableName: 'siparisler', timestamps: true });

// İlişkiler
Kategori.hasMany(Urun, { foreignKey: 'kategoriId' });
Urun.belongsTo(Kategori, { foreignKey: 'kategoriId' });
Kullanici.hasMany(Siparis, { foreignKey: 'kullaniciId' });
Siparis.belongsTo(Kullanici, { foreignKey: 'kullaniciId' });

// ==========================================
// Seed Fonksiyonları
// ==========================================

// Kullanıcı seeding
async function kullanicilariSeedle() {
  console.log('Kullanıcılar oluşturuluyor...');
  const sifreHash = await bcrypt.hash('test123', 10);

  // Admin kullanıcı
  const adminSifre = await bcrypt.hash('admin456', 10);
  const kullanicilar = [
    {
      ad: 'Admin',
      soyad: 'Yönetici',
      eposta: 'admin@ornek.com',
      sifre: adminSifre,
      telefon: '0532 100 0001',
      sehir: 'İstanbul',
      rol: 'admin',
      aktif: true
    }
  ];

  // Rastgele kullanıcılar oluştur
  const sehirler = ['İstanbul', 'Ankara', 'İzmir', 'Bursa', 'Antalya',
                     'Konya', 'Adana', 'Gaziantep', 'Mersin', 'Eskişehir'];

  for (let i = 0; i < 50; i++) {
    const ad = faker.person.firstName();
    const soyad = faker.person.lastName();
    kullanicilar.push({
      ad: ad,
      soyad: soyad,
      eposta: faker.internet.email({ firstName: ad, lastName: soyad }).toLowerCase(),
      sifre: sifreHash,
      telefon: faker.phone.number('05## ### ## ##'),
      sehir: sehirler[Math.floor(Math.random() * sehirler.length)],
      rol: 'kullanici',
      aktif: Math.random() > 0.1 // %90 aktif
    });
  }

  await Kullanici.bulkCreate(kullanicilar);
  console.log('  ' + kullanicilar.length + ' kullanıcı oluşturuldu');
  return kullanicilar.length;
}

// Kategori seeding
async function kategorileriSeedle() {
  console.log('Kategoriler oluşturuluyor...');
  const kategoriler = [
    { isim: 'Elektronik', aciklama: 'Telefon, bilgisayar ve elektronik ürünler', siraNo: 1 },
    { isim: 'Giyim', aciklama: 'Kadın, erkek ve çocuk giyim ürünleri', siraNo: 2 },
    { isim: 'Kitap', aciklama: 'Roman, bilim, kişisel gelişim kitapları', siraNo: 3 },
    { isim: 'Ev & Yaşam', aciklama: 'Mobilya, dekorasyon ve ev gereçleri', siraNo: 4 },
    { isim: 'Spor', aciklama: 'Spor ekipmanları ve outdoor ürünleri', siraNo: 5 },
    { isim: 'Oyuncak', aciklama: 'Çocuk oyuncakları ve eğitici materyaller', siraNo: 6 },
    { isim: 'Kozmetik', aciklama: 'Bakım ürünleri ve makyaj malzemeleri', siraNo: 7 },
    { isim: 'Otomotiv', aciklama: 'Araç aksesuarları ve yedek parçalar', siraNo: 8 }
  ];

  await Kategori.bulkCreate(kategoriler);
  console.log('  ' + kategoriler.length + ' kategori oluşturuldu');
  return kategoriler.length;
}

// Ürün seeding
async function urunleriSeedle() {
  console.log('Ürünler oluşturuluyor...');
  const kategoriler = await Kategori.findAll();
  const urunler = [];

  const urunSablonlari = {
    'Elektronik': ['Akıllı Telefon', 'Dizüstü Bilgisayar', 'Tablet', 'Kulaklık', 'Şarj Cihazı', 'Akıllı Saat', 'Kamera', 'Hoparlör'],
    'Giyim': ['Tişört', 'Kot Pantolon', 'Gömlek', 'Kazak', 'Ceket', 'Elbise', 'Şort', 'Eşofman'],
    'Kitap': ['Roman', 'Bilim Kitabı', 'Tarih Kitabı', 'Kişisel Gelişim', 'Çocuk Kitabı', 'Ansiklopedi'],
    'Ev & Yaşam': ['Yastık', 'Nevresim', 'Lamba', 'Halı', 'Perde', 'Çerçeve', 'Vazo'],
    'Spor': ['Koşu Ayakkabısı', 'Yoga Matı', 'Dambıl Seti', 'Bisiklet', 'Tenis Raketi'],
    'Oyuncak': ['Lego Seti', 'Puzzle', 'Peluş Oyuncak', 'Oyun Konsolu', 'Masa Oyunu'],
    'Kozmetik': ['Nemlendirici Krem', 'Güneş Kremi', 'Parfüm', 'Ruj', 'Fondöten'],
    'Otomotiv': ['Araç Kokusu', 'Koltuk Kılıfı', 'Direksiyon Kılıfı', 'Far Ampulü']
  };

  for (const kategori of kategoriler) {
    const sablonlar = urunSablonlari[kategori.isim] || ['Ürün'];
    for (const sablon of sablonlar) {
      const fiyat = (Math.random() * 5000 + 50).toFixed(2);
      urunler.push({
        isim: sablon + ' ' + faker.commerce.productAdjective(),
        aciklama: faker.commerce.productDescription(),
        fiyat: parseFloat(fiyat),
        stok: Math.floor(Math.random() * 200),
        kategoriId: kategori.id,
        aktif: Math.random() > 0.05,
        resimUrl: '/resimler/urun-' + (urunler.length + 1) + '.jpg'
      });
    }
  }

  await Urun.bulkCreate(urunler);
  console.log('  ' + urunler.length + ' ürün oluşturuldu');
  return urunler.length;
}

// Sipariş seeding
async function siparisleriSeedle() {
  console.log('Siparişler oluşturuluyor...');
  const kullanicilar = await Kullanici.findAll({ where: { rol: 'kullanici' } });
  const durumlar = ['beklemede', 'onaylandı', 'kargoda', 'teslim edildi', 'iptal'];
  const siparisler = [];

  for (let i = 0; i < 100; i++) {
    const rastgeleKullanici = kullanicilar[Math.floor(Math.random() * kullanicilar.length)];
    const toplamTutar = (Math.random() * 10000 + 100).toFixed(2);
    siparisler.push({
      kullaniciId: rastgeleKullanici.id,
      toplamTutar: parseFloat(toplamTutar),
      durum: durumlar[Math.floor(Math.random() * durumlar.length)],
      adres: faker.location.streetAddress() + ', ' + faker.location.city(),
      not: Math.random() > 0.7 ? faker.lorem.sentence() : null
    });
  }

  await Siparis.bulkCreate(siparisler);
  console.log('  ' + siparisler.length + ' sipariş oluşturuldu');
  return siparisler.length;
}

// ==========================================
// Ana Seed Çalıştırıcı
// ==========================================
async function seedBaslat() {
  const ortam = process.env.NODE_ENV || 'development';

  // Güvenlik kontrolü
  if (ortam === 'production') {
    console.error('HATA: Seed işlemi üretim ortamında çalıştırılamaz!');
    process.exit(1);
  }

  console.log('Veritabanı seeding başlıyor... (Ortam: ' + ortam + ')');
  console.log('='.repeat(50));

  try {
    // Tabloları sıfırla ve yeniden oluştur
    await sequelize.sync({ force: true });
    console.log('Tablolar yeniden oluşturuldu\n');

    // Seed işlemlerini sırayla çalıştır
    const kullaniciSayisi = await kullanicilariSeedle();
    const kategoriSayisi = await kategorileriSeedle();
    const urunSayisi = await urunleriSeedle();
    const siparisSayisi = await siparisleriSeedle();

    console.log('\n' + '='.repeat(50));
    console.log('Seed işlemi tamamlandı!');
    console.log('Özet:');
    console.log('  Kullanıcılar: ' + kullaniciSayisi);
    console.log('  Kategoriler:  ' + kategoriSayisi);
    console.log('  Ürünler:      ' + urunSayisi);
    console.log('  Siparişler:   ' + siparisSayisi);
  } catch (hata) {
    console.error('Seed hatası:', hata.message);
    process.exit(1);
  } finally {
    await sequelize.close();
  }
}

seedBaslat();

Veritabanı seeding, profesyonel yazılım geliştirmenin vazgeçilmez bir parçasıdır. İyi yapılandırılmış seed dosyaları, yeni ekip üyelerinin projeye hızlıca adapte olmasını sağlar, test süreçlerini kolaylaştırır ve demo ortamlarını hazırlamayı dakikalar içinde mümkün kılar. Her projede ilk günden itibaren seed altyapısını kurmayı alışkanlık haline getirin.
 

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)

Geri
Üst