Ejemplos de Operaciones de Base de Datos Web TypeScript

Ejemplos de operaciones de base de datos Web TypeScript incluyendo conexión SQLite, ejecución de consultas SQL y manejo de transacciones

💻 Conexión SQLite typescript

🟢 simple ⭐⭐⭐

Conectar a la base de datos SQLite usando SQL.js y WebAssembly en el navegador

⏱️ 25 min 🏷️ typescript, web, database
Prerequisites: Intermediate TypeScript, SQL.js, IndexedDB
// Web TypeScript SQLite Connection Examples
// Using SQL.js (SQLite compiled to WebAssembly) for browser-based database operations

// 1. SQLite Database Connection
class SQLiteDatabase {
  private db: any = null;
  private SQL: any = null;

  // Initialize SQL.js
  async initialize(): Promise<void> {
    // Load SQL.js from CDN
    const sqlJsConfig = await this.loadSQLJS();

    // Initialize SQL.js
    this.SQL = await sqlJsConfig;
    this.db = new this.SQL.Database();

    console.log('SQLite database initialized');
  }

  // Load SQL.js library
  private async loadSQLJS(): Promise<any> {
    // Dynamic import of SQL.js
    const module = await import('sql.js');
    return module;
  }

  // Create database from binary array
  async createFromBuffer(buffer: Uint8Array): Promise<void> {
    this.SQL = await this.loadSQLJS();
    this.db = new this.SQL.Database(buffer);
    console.log('Database created from buffer');
  }

  // Load database from file
  async loadFromFile(file: File): Promise<void> {
    const arrayBuffer = await file.arrayBuffer();
    const uint8Array = new Uint8Array(arrayBuffer);
    await this.createFromBuffer(uint8Array);
  }

  // Export database to binary
  exportDatabase(): Uint8Array {
    if (!this.db) {
      throw new Error('Database not initialized');
    }

    return this.db.export();
  }

  // Download database as file
  downloadDatabase(fileName: string = 'database.db'): void {
    const data = this.exportDatabase();
    const blob = new Blob([data], { type: 'application/x-sqlite3' });

    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    URL.revokeObjectURL(url);
  }

  // Check if database is open
  isOpen(): boolean {
    return this.db !== null;
  }

  // Close database
  close(): void {
    if (this.db) {
      this.db.close();
      this.db = null;
      console.log('Database closed');
    }
  }
}

// 2. Database Schema Manager
class DatabaseSchema {
  private db: SQLiteDatabase;

  constructor(db: SQLiteDatabase) {
    this.db = db;
  }

  // Create table
  createTable(tableName: string, columns: Record<string, string>): void {
    const columnDefs = Object.entries(columns)
      .map(([name, type]) => `${name} ${type}`)
      .join(', ');

    const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (${columnDefs})`;
    this.db.getDB().run(sql);

    console.log(`Table ${tableName} created`);
  }

  // Drop table
  dropTable(tableName: string): void {
    const sql = `DROP TABLE IF EXISTS ${tableName}`;
    this.db.getDB().run(sql);

    console.log(`Table ${tableName} dropped`);
  }

  // Check if table exists
  tableExists(tableName: string): boolean {
    const sql = `SELECT name FROM sqlite_master WHERE type='table' AND name=?`;
    const result = this.db.getDB().exec(sql, [tableName]);

    return result.length > 0;
  }

  // List all tables
  listTables(): string[] {
    const sql = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name";
    const result = this.db.getDB().exec(sql);

    if (result.length > 0) {
      return result[0].values.map((row: any) => row[0]);
    }

    return [];
  }

  // Get table schema
  getTableSchema(tableName: string): any[] {
    const sql = `PRAGMA table_info(${tableName})`;
    const result = this.db.getDB().exec(sql);

    if (result.length > 0) {
      return result[0].values.map((row: any) => ({
        cid: row[0],
        name: row[1],
        type: row[2],
        notNull: row[3] === 1,
        defaultValue: row[4],
        pk: row[5] === 1
      }));
    }

    return [];
  }
}

// 3. In-Memory Database
class InMemoryDatabase extends SQLiteDatabase {
  // Create temporary database
  static async create(): Promise<InMemoryDatabase> {
    const db = new InMemoryDatabase();
    await db.initialize();
    return db;
  }

  // Save state
  saveState(): Uint8Array {
    return this.exportDatabase();
  }

  // Restore state
  restoreState(data: Uint8Array): void {
    if (this.db) {
      this.db.close();
    }
    this.db = new this.SQL.Database(data);
  }
}

// 4. Persistent Database (IndexedDB)
class PersistentDatabase {
  private dbName: string;
  private storeName: string = 'sqlite_databases';
  private db: IDBDatabase | null = null;
  private sqlDB: SQLiteDatabase | null = null;

  constructor(dbName: string) {
    this.dbName = dbName;
  }

  // Initialize IndexedDB
  async initialize(): Promise<void> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, 1);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        resolve();
      };

      request.onupgradeneeded = (event) => {
        const db = (event.target as IDBOpenDBRequest).result;
        if (!db.objectStoreNames.contains(this.storeName)) {
          db.createObjectStore(this.storeName);
        }
      };
    });
  }

  // Save SQLite database to IndexedDB
  async saveDatabase(name: string, sqlDB: SQLiteDatabase): Promise<void> {
    const data = sqlDB.exportDatabase();

    return new Promise((resolve, reject) => {
      const transaction = this.db!.transaction([this.storeName], 'readwrite');
      const store = transaction.objectStore(this.storeName);
      const request = store.put(data, name);

      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  // Load SQLite database from IndexedDB
  async loadDatabase(name: string): Promise<SQLiteDatabase> {
    return new Promise((resolve, reject) => {
      const transaction = this.db!.transaction([this.storeName], 'readonly');
      const store = transaction.objectStore(this.storeName);
      const request = store.get(name);

      request.onsuccess = async () => {
        const data = request.result;
        if (data) {
          const sqlDB = new SQLiteDatabase();
          await sqlDB.initialize();
          sqlDB.getDB().close();
          sqlDB['db'] = new sqlDB['SQL'].Database(data);
          resolve(sqlDB);
        } else {
          reject(new Error('Database not found'));
        }
      };

      request.onerror = () => reject(request.error);
    });
  }

  // List all saved databases
  async listDatabases(): Promise<string[]> {
    return new Promise((resolve, reject) => {
      const transaction = this.db!.transaction([this.storeName], 'readonly');
      const store = transaction.objectStore(this.storeName);
      const request = store.getAllKeys();

      request.onsuccess = () => resolve(request.result as string[]);
      request.onerror = () => reject(request.error);
    });
  }

  // Delete database
  async deleteDatabase(name: string): Promise<void> {
    return new Promise((resolve, reject) => {
      const transaction = this.db!.transaction([this.storeName], 'readwrite');
      const store = transaction.objectStore(this.storeName);
      const request = store.delete(name);

      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  // Close IndexedDB
  close(): void {
    if (this.db) {
      this.db.close();
      this.db = null;
    }
  }
}

// 5. Database Connection Pool (Simulated)
class DatabasePool {
  private pools: Map<string, SQLiteDatabase[]> = new Map();
  private maxSize: number = 5;

  // Create pool
  createPool(name: string, size: number = this.maxSize): void {
    const pool: SQLiteDatabase[] = [];

    for (let i = 0; i < size; i++) {
      // Would create connections here
      // In browser, this is simulated
    }

    this.pools.set(name, pool);
  }

  // Get connection from pool
  async getConnection(name: string): Promise<SQLiteDatabase | null> {
    const pool = this.pools.get(name);

    if (pool && pool.length > 0) {
      return pool.pop()!;
    }

    // Create new connection
    const db = new SQLiteDatabase();
    await db.initialize();
    return db;
  }

  // Return connection to pool
  returnConnection(name: string, db: SQLiteDatabase): void {
    const pool = this.pools.get(name);

    if (pool && pool.length < this.maxSize) {
      pool.push(db);
    } else {
      db.close();
    }
  }

  // Close all connections in pool
  closePool(name: string): void {
    const pool = this.pools.get(name);

    if (pool) {
      pool.forEach(db => db.close());
      pool.length = 0;
    }
  }

  // Close all pools
  closeAll(): void {
    this.pools.forEach((pool, name) => {
      this.closePool(name);
    });
    this.pools.clear();
  }
}

// 6. Database Health Monitor
class DatabaseHealthMonitor {
  private db: SQLiteDatabase;
  private metrics: Map<string, any> = new Map();

  constructor(db: SQLiteDatabase) {
    this.db = db;
  }

  // Get database size
  getDatabaseSize(): number {
    const data = this.db.exportDatabase();
    return data.length;
  }

  // Get database page size
  getPageSize(): number {
    const result = this.db.getDB().exec('PRAGMA page_size');
    return result.length > 0 ? result[0].values[0][0] : 0;
  }

  // Get page count
  getPageCount(): number {
    const result = this.db.getDB().exec('PRAGMA page_count');
    return result.length > 0 ? result[0].values[0][0] : 0;
  }

  // Get database version
  getDatabaseVersion(): string {
    const result = this.db.getDB().exec('SELECT sqlite_version()');
    return result.length > 0 ? result[0].values[0][0] : '';
  }

  // Check database integrity
  checkIntegrity(): boolean {
    const result = this.db.getDB().exec('PRAGMA integrity_check');
    return result.length > 0 && result[0].values[0][0] === 'ok';
  }

  // Get table row counts
  getTableRowCounts(): Map<string, number> {
    const counts = new Map<string, number>();
    const tables = this.db.getDB().exec("SELECT name FROM sqlite_master WHERE type='table'");

    if (tables.length > 0) {
      for (const table of tables[0].values) {
        const tableName = table[0];
        const result = this.db.getDB().exec(`SELECT COUNT(*) FROM ${tableName}`);
        if (result.length > 0) {
          counts.set(tableName, result[0].values[0][0]);
        }
      }
    }

    return counts;
  }

  // Get health report
  getHealthReport(): Record<string, any> {
    return {
      size: this.getDatabaseSize(),
      pageSize: this.getPageSize(),
      pageCount: this.getPageCount(),
      version: this.getDatabaseVersion(),
      integrity: this.checkIntegrity(),
      tableCounts: Object.fromEntries(this.getTableRowCounts())
    };
  }
}

// Extend SQLiteDatabase to access internal db
(SQLiteDatabase.prototype as any).getDB = function() { return this.db; };

// Usage Examples
async function demonstrateSQLiteConnection() {
  console.log('=== Web TypeScript SQLite Connection Examples ===\n');

  // 1. Initialize in-memory database
  console.log('--- 1. Initialize In-Memory Database ---');
  const db1 = await InMemoryDatabase.create();
  console.log(`Database open: ${db1.isOpen()}`);

  // 2. Create schema
  console.log('\n--- 2. Create Schema ---');
  const schema = new DatabaseSchema(db1);
  schema.createTable('users', {
    id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
    name: 'TEXT NOT NULL',
    email: 'TEXT UNIQUE',
    created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
  });

  const tables = schema.listTables();
  console.log(`Tables: ${tables.join(', ')}`);

  // 3. Get table schema
  console.log('\n--- 3. Get Table Schema ---');
  const tableSchema = schema.getTableSchema('users');
  tableSchema.forEach(col => {
    console.log(`  ${col.name}: ${col.type}`);
  });

  // 4. Persistent database
  console.log('\n--- 4. Persistent Database ---');
  const persistentDB = new PersistentDatabase('MyAppDB');
  await persistentDB.initialize();
  await persistentDB.saveDatabase('main_db', db1);
  console.log('Database saved to IndexedDB');

  const savedDBs = await persistentDB.listDatabases();
  console.log(`Saved databases: ${savedDBs.join(', ')}`);

  // 5. Health monitor
  console.log('\n--- 5. Health Monitor ---');
  const monitor = new DatabaseHealthMonitor(db1);
  const health = monitor.getHealthReport();
  console.log('Health Report:');
  console.log(`  Size: ${health.size} bytes`);
  console.log(`  Version: ${health.version}`);
  console.log(`  Integrity: ${health.integrity}`);

  // 6. Export and download
  console.log('\n--- 6. Export Database ---');
  db1.downloadDatabase('my_database.db');

  // Cleanup
  db1.close();
  persistentDB.close();

  console.log('\n=== All SQLite Connection Examples Completed ===');
}

// Export functions
export { SQLiteDatabase, DatabaseSchema, InMemoryDatabase, PersistentDatabase, DatabasePool, DatabaseHealthMonitor };
export { demonstrateSQLiteConnection };

💻 Ejecución de Consultas SQL typescript

🟡 intermediate ⭐⭐⭐⭐

Ejecutar consultas SELECT, INSERT, UPDATE, DELETE con enlace de parámetros y manejo de resultados

⏱️ 30 min 🏷️ typescript, web, database, sql
Prerequisites: Intermediate TypeScript, SQL knowledge, Design patterns
// Web TypeScript SQL Query Execution Examples
// Using SQL.js for executing SQL queries in browser environment

// 1. Query Builder
class QueryBuilder {
  private table: string = '';
  private selectColumns: string[] = ['*'];
  private whereConditions: string[] = [];
  private joinClauses: string[] = [];
  private orderByClause: string = '';
  private limitValue: number = 0;
  private offsetValue: number = 0;
  private bindParams: any[] = [];

  // Select table
  from(table: string): this {
    this.table = table;
    return this;
  }

  // Select columns
  select(...columns: string[]): this {
    this.selectColumns = columns;
    return this;
  }

  // Where clause
  where(condition: string, ...params: any[]): this {
    this.whereConditions.push(condition);
    this.bindParams.push(...params);
    return this;
  }

  // Join clause
  join(table: string, onCondition: string): this {
    this.joinClauses.push(`JOIN ${table} ON ${onCondition}`);
    return this;
  }

  // Left join
  leftJoin(table: string, onCondition: string): this {
    this.joinClauses.push(`LEFT JOIN ${table} ON ${onCondition}`);
    return this;
  }

  // Order by
  orderBy(column: string, direction: 'ASC' | 'DESC' = 'ASC'): this {
    this.orderByClause = `ORDER BY ${column} ${direction}`;
    return this;
  }

  // Limit
  limit(count: number): this {
    this.limitValue = count;
    return this;
  }

  // Offset
  offset(count: number): this {
    this.offsetValue = count;
    return this;
  }

  // Build query
  build(): { sql: string; params: any[] } {
    const sql = `SELECT ${this.selectColumns.join(', ')} FROM ${this.table}`;

    const parts = [sql];

    if (this.joinClauses.length > 0) {
      parts.push(this.joinClauses.join(' '));
    }

    if (this.whereConditions.length > 0) {
      parts.push(`WHERE ${this.whereConditions.join(' AND ')}`);
    }

    if (this.orderByClause) {
      parts.push(this.orderByClause);
    }

    if (this.limitValue > 0) {
      parts.push(`LIMIT ${this.limitValue}`);
    }

    if (this.offsetValue > 0) {
      parts.push(`OFFSET ${this.offsetValue}`);
    }

    return {
      sql: parts.join(' '),
      params: this.bindParams
    };
  }

  // Reset builder
  reset(): this {
    this.table = '';
    this.selectColumns = ['*'];
    this.whereConditions = [];
    this.joinClauses = [];
    this.orderByClause = '';
    this.limitValue = 0;
    this.offsetValue = 0;
    this.bindParams = [];
    return this;
  }
}

// 2. Database Query Executor
class QueryExecutor {
  private db: any;

  constructor(db: any) {
    this.db = db;
  }

  // Execute SELECT query
  select(sql: string, params: any[] = []): any[] {
    try {
      const results = this.db.exec(sql, params);

      if (results.length > 0) {
        const columns = results[0].columns;
        const values = results[0].values;

        return values.map((row: any[]) => {
          const obj: Record<string, any> = {};
          columns.forEach((col: string, index: number) => {
            obj[col] = row[index];
          });
          return obj;
        });
      }

      return [];
    } catch (error) {
      console.error('Query error:', error);
      throw error;
    }
  }

  // Execute SELECT and return single row
  selectOne(sql: string, params: any[] = []): any | null {
    const results = this.select(sql, params);
    return results.length > 0 ? results[0] : null;
  }

  // Execute INSERT query
  insert(table: string, data: Record<string, any>): number {
    const columns = Object.keys(data);
    const values = Object.values(data);
    const placeholders = columns.map(() => '?').join(', ');

    const sql = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`;

    try {
      this.db.run(sql, values);
      return this.getLastInsertId();
    } catch (error) {
      console.error('Insert error:', error);
      throw error;
    }
  }

  // Execute batch INSERT
  batchInsert(table: string, dataList: Record<string, any>[]): number {
    if (dataList.length === 0) return 0;

    const columns = Object.keys(dataList[0]);
    const placeholders = columns.map(() => '?').join(', ');

    const sql = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`;

    let insertedCount = 0;

    try {
      this.db.run('BEGIN TRANSACTION');

      for (const data of dataList) {
        const values = columns.map(col => data[col]);
        this.db.run(sql, values);
        insertedCount++;
      }

      this.db.run('COMMIT');
      return insertedCount;
    } catch (error) {
      this.db.run('ROLLBACK');
      console.error('Batch insert error:', error);
      throw error;
    }
  }

  // Execute UPDATE query
  update(table: string, data: Record<string, any>, where: string, whereParams: any[] = []): number {
    const setClause = Object.keys(data)
      .map(key => `${key} = ?`)
      .join(', ');

    const values = [...Object.values(data), ...whereParams];
    const sql = `UPDATE ${table} SET ${setClause} WHERE ${where}`;

    try {
      this.db.run(sql, values);
      return this.getChanges();
    } catch (error) {
      console.error('Update error:', error);
      throw error;
    }
  }

  // Execute DELETE query
  delete(table: string, where: string, params: any[] = []): number {
    const sql = `DELETE FROM ${table} WHERE ${where}`;

    try {
      this.db.run(sql, params);
      return this.getChanges();
    } catch (error) {
      console.error('Delete error:', error);
      throw error;
    }
  }

  // Execute raw SQL
  execute(sql: string, params: any[] = []): any[] {
    return this.select(sql, params);
  }

  // Get last insert ID
  getLastInsertId(): number {
    const result = this.db.exec('SELECT last_insert_rowid()');
    return result.length > 0 ? result[0].values[0][0] : 0;
  }

  // Get number of affected rows
  getChanges(): number {
    const result = this.db.exec('SELECT changes()');
    return result.length > 0 ? result[0].values[0][0] : 0;
  }

  // Count rows
  count(table: string, where: string = '', params: any[] = []): number {
    let sql = `SELECT COUNT(*) as count FROM ${table}`;

    if (where) {
      sql += ` WHERE ${where}`;
    }

    const result = this.selectOne(sql, params);
    return result ? result.count : 0;
  }

  // Check if exists
  exists(table: string, where: string, params: any[] = []): boolean {
    const count = this.count(table, where, params);
    return count > 0;
  }
}

// 3. Repository Pattern
class Repository {
  protected executor: QueryExecutor;
  protected tableName: string;

  constructor(db: any, tableName: string) {
    this.executor = new QueryExecutor(db);
    this.tableName = tableName;
  }

  // Find by ID
  findById(id: number): any | null {
    return this.executor.selectOne(
      `SELECT * FROM ${this.tableName} WHERE id = ?`,
      [id]
    );
  }

  // Find all
  findAll(limit: number = 100, offset: number = 0): any[] {
    return this.executor.select(
      `SELECT * FROM ${this.tableName} LIMIT ? OFFSET ?`,
      [limit, offset]
    );
  }

  // Find where
  findWhere(where: string, params: any[]): any[] {
    return this.executor.select(
      `SELECT * FROM ${this.tableName} WHERE ${where}`,
      params
    );
  }

  // Find one where
  findOneWhere(where: string, params: any[]): any | null {
    return this.executor.selectOne(
      `SELECT * FROM ${this.tableName} WHERE ${where}`,
      params
    );
  }

  // Insert
  create(data: Record<string, any>): number {
    return this.executor.insert(this.tableName, data);
  }

  // Update
  update(id: number, data: Record<string, any>): number {
    return this.executor.update(
      this.tableName,
      data,
      'id = ?',
      [id]
    );
  }

  // Delete
  delete(id: number): number {
    return this.executor.delete(
      this.tableName,
      'id = ?',
      [id]
    );
  }

  // Count
  count(): number {
    return this.executor.count(this.tableName);
  }

  // Exists
  exists(id: number): boolean {
    return this.executor.exists(this.tableName, 'id = ?', [id]);
  }
}

// 4. Active Record Pattern
class ActiveRecord {
  protected db: any;
  protected tableName: string = '';
  protected data: Record<string, any> = {};
  protected isNew: boolean = true;

  constructor(db: any, data: Record<string, any> = {}) {
    this.db = db;
    this.data = { ...data };
    if (data.id) {
      this.isNew = false;
    }
  }

  // Get attribute
  get(key: string): any {
    return this.data[key];
  }

  // Set attribute
  set(key: string, value: any): void {
    this.data[key] = value;
  }

  // Save
  save(): number {
    const executor = new QueryExecutor(this.db);

    if (this.isNew) {
      const id = executor.insert(this.tableName, this.data);
      this.data.id = id;
      this.isNew = false;
      return id;
    } else {
      executor.update(
        this.tableName,
        this.data,
        'id = ?',
        [this.data.id]
      );
      return this.data.id;
    }
  }

  // Delete
  delete(): number {
    if (this.isNew) return 0;

    const executor = new QueryExecutor(this.db);
    return executor.delete(this.tableName, 'id = ?', [this.data.id]);
  }

  // Refresh
  refresh(): void {
    if (this.isNew) return;

    const executor = new QueryExecutor(this.db);
    const data = executor.selectOne(
      `SELECT * FROM ${this.tableName} WHERE id = ?`,
      [this.data.id]
    );

    if (data) {
      this.data = data;
    }
  }

  // Convert to JSON
  toJSON(): Record<string, any> {
    return { ...this.data };
  }
}

// 5. Query Logger
class QueryLogger {
  private executor: QueryExecutor;
  private logs: Array<{ sql: string; params: any[]; time: number }> = [];

  constructor(executor: QueryExecutor) {
    this.executor = executor;
  }

  // Execute with logging
  select(sql: string, params: any[] = []): any[] {
    const start = performance.now();
    const results = this.executor.select(sql, params);
    const time = performance.now() - start;

    this.logs.push({ sql, params, time });
    console.log(`[SELECT] ${sql} - ${time.toFixed(2)}ms`);

    return results;
  }

  // Get slow queries
  getSlowQueries(threshold: number = 100): Array<{ sql: string; params: any[]; time: number }> {
    return this.logs.filter(log => log.time > threshold);
  }

  // Get query statistics
  getStatistics(): Record<string, any> {
    const totalTime = this.logs.reduce((sum, log) => sum + log.time, 0);
    const avgTime = totalTime / this.logs.length;
    const maxTime = Math.max(...this.logs.map(log => log.time));

    return {
      totalQueries: this.logs.length,
      totalTime: totalTime.toFixed(2),
      averageTime: avgTime.toFixed(2),
      maxTime: maxTime.toFixed(2),
      slowQueries: this.getSlowQueries().length
    };
  }

  // Clear logs
  clear(): void {
    this.logs = [];
  }
}

// 6. Data Mapper
class DataMapper {
  private executor: QueryExecutor;

  constructor(db: any) {
    this.executor = new QueryExecutor(db);
  }

  // Map rows to entities
  mapToEntities<T>(rows: any[], entityClass: new () => T): T[] {
    return rows.map(row => this.mapToEntity(row, entityClass));
  }

  // Map row to entity
  mapToEntity<T>(row: any, entityClass: new () => T): T {
    const entity = new entityClass();
    Object.assign(entity, row);
    return entity;
  }

  // Execute query and map to entities
  queryAndMap<T>(sql: string, params: any[], entityClass: new () => T): T[] {
    const rows = this.executor.select(sql, params);
    return this.mapToEntities(rows, entityClass);
  }
}

// Usage Examples
async function demonstrateSQLQueryExecution() {
  console.log('=== Web TypeScript SQL Query Execution Examples ===\n');

  // Simulate database connection
  const mockDB = {
    exec: (sql: string, params: any[] = []) => {
      // Mock implementation
      console.log(`Executing: ${sql}`);
      return [];
    },
    run: (sql: string, params: any[] = []) => {
      console.log(`Running: ${sql}`);
    }
  };

  const executor = new QueryExecutor(mockDB);

  // 1. Query builder
  console.log('--- 1. Query Builder ---');
  const builder = new QueryBuilder();
  const query = builder
    .select('id', 'name', 'email')
    .from('users')
    .where('age > ?', 18)
    .where('status = ?', 'active')
    .orderBy('name', 'ASC')
    .limit(10)
    .build();

  console.log(`SQL: ${query.sql}`);
  console.log(`Params: ${query.params}`);

  // 2. Repository
  console.log('\n--- 2. Repository Pattern ---');
  class UserRepository extends Repository {
    findByEmail(email: string) {
      return this.findOneWhere('email = ?', [email]);
    }

    findActiveUsers() {
      return this.findWhere('status = ?', ['active']);
    }

    searchByName(keyword: string) {
      return this.findWhere('name LIKE ?', [`%${keyword}%`]);
    }
  }

  const userRepo = new UserRepository(mockDB, 'users');
  console.log('Repository created for users table');

  // 3. Query logging
  console.log('\n--- 3. Query Logging ---');
  const logger = new QueryLogger(executor);
  // logger.select('SELECT * FROM users');

  console.log('\n=== All SQL Query Execution Examples Completed ===');
}

// Export functions
export { QueryBuilder, QueryExecutor, Repository, ActiveRecord, QueryLogger, DataMapper };
export { demonstrateSQLQueryExecution };

💻 Manejo de Transacciones typescript

🔴 complex ⭐⭐⭐⭐

Gestionar transacciones de base de datos con BEGIN, COMMIT, ROLLBACK y niveles de aislamiento

⏱️ 35 min 🏷️ typescript, web, database, transactions
Prerequisites: Advanced TypeScript, Database transactions, ACID principles
// Web TypeScript Transaction Handling Examples
// Using SQL.js for transaction management in browser environment

// 1. Transaction Manager
class TransactionManager {
  private db: any;
  private inTransaction: boolean = false;
  private savepoints: string[] = [];

  constructor(db: any) {
    this.db = db;
  }

  // Begin transaction
  begin(): void {
    if (this.inTransaction) {
      throw new Error('Transaction already in progress');
    }

    this.db.run('BEGIN TRANSACTION');
    this.inTransaction = true;
    console.log('Transaction started');
  }

  // Commit transaction
  commit(): void {
    if (!this.inTransaction) {
      throw new Error('No transaction in progress');
    }

    this.db.run('COMMIT');
    this.inTransaction = false;
    this.savepoints = [];
    console.log('Transaction committed');
  }

  // Rollback transaction
  rollback(): void {
    if (!this.inTransaction) {
      throw new Error('No transaction in progress');
    }

    this.db.run('ROLLBACK');
    this.inTransaction = false;
    this.savepoints = [];
    console.log('Transaction rolled back');
  }

  // Create savepoint
  createSavepoint(name: string): void {
    if (!this.inTransaction) {
      throw new Error('No transaction in progress');
    }

    this.db.run(`SAVEPOINT ${name}`);
    this.savepoints.push(name);
    console.log(`Savepoint ${name} created`);
  }

  // Release savepoint
  releaseSavepoint(name: string): void {
    if (!this.savepoints.includes(name)) {
      throw new Error(`Savepoint ${name} not found`);
    }

    this.db.run(`RELEASE SAVEPOINT ${name}`);
    this.savepoints = this.savepoints.filter(sp => sp !== name);
    console.log(`Savepoint ${name} released`);
  }

  // Rollback to savepoint
  rollbackToSavepoint(name: string): void {
    if (!this.savepoints.includes(name)) {
      throw new Error(`Savepoint ${name} not found`);
    }

    this.db.run(`ROLLBACK TO SAVEPOINT ${name}`);
    this.savepoints = this.savepoints.filter(sp => sp !== name);
    console.log(`Rolled back to savepoint ${name}`);
  }

  // Execute in transaction
  async transaction<T>(callback: () => T): Promise<T> {
    this.begin();

    try {
      const result = callback();
      this.commit();
      return result;
    } catch (error) {
      this.rollback();
      throw error;
    }
  }

  // Check if in transaction
  isInTransaction(): boolean {
    return this.inTransaction;
  }

  // Get active savepoints
  getSavepoints(): string[] {
    return [...this.savepoints];
  }
}

// 2. Unit of Work Pattern
class UnitOfWork {
  private db: any;
  private transaction: TransactionManager;
  private newEntities: Map<string, any[]> = new Map();
  private modifiedEntities: Map<string, any[]> = new Map();
  private deletedEntities: Map<string, any[]> = new Map();

  constructor(db: any) {
    this.db = db;
    this.transaction = new TransactionManager(db);
  }

  // Register new entity
  registerNew(tableName: string, entity: any): void {
    if (!this.newEntities.has(tableName)) {
      this.newEntities.set(tableName, []);
    }
    this.newEntities.get(tableName)!.push(entity);
  }

  // Register modified entity
  registerModified(tableName: string, entity: any): void {
    if (!this.modifiedEntities.has(tableName)) {
      this.modifiedEntities.set(tableName, []);
    }
    this.modifiedEntities.get(tableName)!.push(entity);
  }

  // Register deleted entity
  registerDeleted(tableName: string, entity: any): void {
    if (!this.deletedEntities.has(tableName)) {
      this.deletedEntities.set(tableName, []);
    }
    this.deletedEntities.get(tableName)!.push(entity);
  }

  // Commit all changes
  commit(): void {
    this.transaction.begin();

    try {
      // Insert new entities
      for (const [tableName, entities] of this.newEntities) {
        for (const entity of entities) {
          const executor = new QueryExecutor(this.db);
          const columns = Object.keys(entity);
          const values = Object.values(entity);
          const placeholders = columns.map(() => '?').join(', ');
          const sql = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`;
          this.db.run(sql, values);
        }
      }

      // Update modified entities
      for (const [tableName, entities] of this.modifiedEntities) {
        for (const entity of entities) {
          const executor = new QueryExecutor(this.db);
          const columns = Object.keys(entity).filter(k => k !== 'id');
          const values = columns.map(k => entity[k]);
          const setClause = columns.map(k => `${k} = ?`).join(', ');
          const sql = `UPDATE ${tableName} SET ${setClause} WHERE id = ?`;
          this.db.run(sql, [...values, entity.id]);
        }
      }

      // Delete entities
      for (const [tableName, entities] of this.deletedEntities) {
        for (const entity of entities) {
          const executor = new QueryExecutor(this.db);
          const sql = `DELETE FROM ${tableName} WHERE id = ?`;
          this.db.run(sql, [entity.id]);
        }
      }

      this.transaction.commit();
      this.clean();
    } catch (error) {
      this.transaction.rollback();
      throw error;
    }
  }

  // Rollback all changes
  rollback(): void {
    this.transaction.rollback();
    this.clean();
  }

  // Clean tracked entities
  private clean(): void {
    this.newEntities.clear();
    this.modifiedEntities.clear();
    this.deletedEntities.clear();
  }

  // Check if has changes
  hasChanges(): boolean {
    return this.newEntities.size > 0 ||
           this.modifiedEntities.size > 0 ||
           this.deletedEntities.size > 0;
  }
}

// 3. Transaction Isolation Levels
class TransactionIsolation {
  private db: any;

  constructor(db: any) {
    this.db = db;
  }

  // Set isolation level (simulated for SQL.js)
  setIsolationLevel(level: 'DEFERRED' | 'IMMEDIATE' | 'EXCLUSIVE'): void {
    const sql = `BEGIN ${level} TRANSACTION`;
    this.db.run(sql);
    console.log(`Isolation level set to ${level}`);
  }

  // Read uncommitted (simulated)
  readUncommitted(callback: () => void): void {
    console.log('Starting READ UNCOMMITTED transaction');
    this.begin();
    try {
      callback();
      this.commit();
    } catch (error) {
      this.rollback();
      throw error;
    }
  }

  // Read committed (simulated)
  readCommitted(callback: () => void): void {
    console.log('Starting READ COMMITTED transaction');
    this.begin();
    try {
      callback();
      this.commit();
    } catch (error) {
      this.rollback();
      throw error;
    }
  }

  // Repeatable read (simulated)
  repeatableRead(callback: () => void): void {
    console.log('Starting REPEATABLE READ transaction');
    this.begin();
    try {
      callback();
      this.commit();
    } catch (error) {
      this.rollback();
      throw error;
    }
  }

  // Serializable (simulated)
  serializable(callback: () => void): void {
    console.log('Starting SERIALIZABLE transaction');
    this.begin();
    try {
      callback();
      this.commit();
    } catch (error) {
      this.rollback();
      throw error;
    }
  }

  private begin(): void {
    this.db.run('BEGIN IMMEDIATE TRANSACTION');
  }

  private commit(): void {
    this.db.run('COMMIT');
  }

  private rollback(): void {
    this.db.run('ROLLBACK');
  }
}

// 4. Nested Transactions
class NestedTransaction {
  private db: any;
  private transactionStack: TransactionManager[] = [];
  private depth: number = 0;

  constructor(db: any) {
    this.db = db;
  }

  // Begin nested transaction
  begin(): void {
    if (this.depth === 0) {
      // Top-level transaction
      const tx = new TransactionManager(this.db);
      tx.begin();
      this.transactionStack.push(tx);
    } else {
      // Nested transaction using savepoint
      const parent = this.transactionStack[this.depth - 1];
      const savepointName = `sp_${this.depth}`;
      parent.createSavepoint(savepointName);
      this.transactionStack.push(parent);
    }

    this.depth++;
    console.log(`Transaction depth: ${this.depth}`);
  }

  // Commit current level
  commit(): void {
    if (this.depth === 0) {
      throw new Error('No transaction to commit');
    }

    this.depth--;

    if (this.depth === 0) {
      // Commit top-level transaction
      const tx = this.transactionStack.pop()!;
      tx.commit();
    } else {
      // Release savepoint
      const parent = this.transactionStack[this.depth - 1];
      const savepointName = `sp_${this.depth}`;
      parent.releaseSavepoint(savepointName);
      this.transactionStack.pop();
    }

    console.log(`Transaction depth: ${this.depth}`);
  }

  // Rollback current level
  rollback(): void {
    if (this.depth === 0) {
      throw new Error('No transaction to rollback');
    }

    this.depth--;

    if (this.depth === 0) {
      // Rollback top-level transaction
      const tx = this.transactionStack.pop()!;
      tx.rollback();
    } else {
      // Rollback to savepoint
      const parent = this.transactionStack[this.depth - 1];
      const savepointName = `sp_${this.depth}`;
      parent.rollbackToSavepoint(savepointName);
      this.transactionStack.pop();
    }

    console.log(`Transaction depth: ${this.depth}`);
  }

  // Get current depth
  getDepth(): number {
    return this.depth;
  }
}

// 5. Transaction Logger
class TransactionLogger {
  private transaction: TransactionManager;
  private logs: Array<{ event: string; timestamp: number }> = [];

  constructor(transaction: TransactionManager) {
    this.transaction = transaction;
  }

  // Begin with logging
  begin(): void {
    this.log('BEGIN');
    this.transaction.begin();
  }

  // Commit with logging
  commit(): void {
    this.log('COMMIT');
    this.transaction.commit();
  }

  // Rollback with logging
  rollback(): void {
    this.log('ROLLBACK');
    this.transaction.rollback();
  }

  // Log event
  private log(event: string): void {
    this.logs.push({
      event,
      timestamp: Date.now()
    });
    console.log(`[TX LOG] ${event} at ${new Date().toISOString()}`);
  }

  // Get transaction history
  getHistory(): Array<{ event: string; timestamp: number }> {
    return [...this.logs];
  }

  // Clear logs
  clear(): void {
    this.logs = [];
  }
}

// 6. Distributed Transaction Coordinator (Simulated)
class DistributedTransactionCoordinator {
  private participants: Map<string, any> = new Map();
  private prepared: Set<string> = new Set();

  // Register participant
  registerParticipant(name: string, db: any): void {
    this.participants.set(name, db);
    console.log(`Participant ${name} registered`);
  }

  // Two-phase commit: prepare phase
  async prepare(): Promise<boolean> {
    console.log('Starting prepare phase...');

    for (const [name, db] of this.participants) {
      try {
        db.run('BEGIN TRANSACTION');
        // Simulate prepare
        this.prepared.add(name);
        console.log(`${name} prepared`);
      } catch (error) {
        console.error(`${name} prepare failed`, error);
        await this.rollback();
        return false;
      }
    }

    return true;
  }

  // Two-phase commit: commit phase
  async commit(): Promise<void> {
    console.log('Starting commit phase...');

    for (const [name, db] of this.participants) {
      if (this.prepared.has(name)) {
        try {
          db.run('COMMIT');
          console.log(`${name} committed`);
        } catch (error) {
          console.error(`${name} commit failed`, error);
        }
      }
    }

    this.prepared.clear();
  }

  // Two-phase commit: rollback
  async rollback(): Promise<void> {
    console.log('Starting rollback...');

    for (const [name, db] of this.participants) {
      try {
        db.run('ROLLBACK');
        console.log(`${name} rolled back`);
      } catch (error) {
        console.error(`${name} rollback failed`, error);
      }
    }

    this.prepared.clear();
  }

  // Execute distributed transaction
  async execute(callback: () => Promise<void>): Promise<boolean> {
    const prepared = await this.prepare();

    if (!prepared) {
      return false;
    }

    try {
      await callback();
      await this.commit();
      return true;
    } catch (error) {
      console.error('Distributed transaction failed', error);
      await this.rollback();
      return false;
    }
  }
}

// Query Executor helper
class QueryExecutor {
  constructor(private db: any) {}

  select(sql: string, params: any[] = []): any[] {
    const results = this.db.exec(sql, params);
    if (results.length > 0) {
      const columns = results[0].columns;
      const values = results[0].values;
      return values.map((row: any[]) => {
        const obj: Record<string, any> = {};
        columns.forEach((col: string, index: number) => {
          obj[col] = row[index];
        });
        return obj;
      });
    }
    return [];
  }
}

// Usage Examples
async function demonstrateTransactionHandling() {
  console.log('=== Web TypeScript Transaction Handling Examples ===\n');

  // Mock database
  const mockDB = {
    exec: (sql: string, params?: any) => {
      console.log(`Executing: ${sql}`);
      return [];
    },
    run: (sql: string, params?: any) => {
      console.log(`Running: ${sql}`);
    }
  };

  // 1. Basic transaction
  console.log('--- 1. Basic Transaction ---');
  const txManager = new TransactionManager(mockDB);
  txManager.begin();
  console.log(`In transaction: ${txManager.isInTransaction()}`);
  txManager.commit();

  // 2. Transaction with callback
  console.log('\n--- 2. Transaction with Callback ---');
  try {
    await txManager.transaction(() => {
      console.log('Executing operations in transaction...');
      mockDB.run('INSERT INTO users (name) VALUES (?)', ['Alice']);
      mockDB.run('INSERT INTO users (name) VALUES (?)', ['Bob']);
      return 'success';
    });
  } catch (error) {
    console.error('Transaction failed:', error);
  }

  // 3. Savepoints
  console.log('\n--- 3. Savepoints ---');
  txManager.begin();
  mockDB.run('INSERT INTO users (name) VALUES (?)', ['Charlie']);

  txManager.createSavepoint('sp1');
  mockDB.run('INSERT INTO users (name) VALUES (?)', ['David']);

  txManager.rollbackToSavepoint('sp1');
  txManager.commit();

  // 4. Nested transactions
  console.log('\n--- 4. Nested Transactions ---');
  const nestedTx = new NestedTransaction(mockDB);
  nestedTx.begin();
  console.log(`Depth: ${nestedTx.getDepth()}`);

  nestedTx.begin();
  console.log(`Depth: ${nestedTx.getDepth()}`);

  nestedTx.commit();
  nestedTx.commit();

  // 5. Unit of Work
  console.log('\n--- 5. Unit of Work ---');
  const uow = new UnitOfWork(mockDB);
  uow.registerNew('users', { name: 'Eve' });
  uow.registerNew('users', { name: 'Frank' });

  if (uow.hasChanges()) {
    console.log('Committing changes...');
    // uow.commit();
  }

  console.log('\n=== All Transaction Handling Examples Completed ===');
}

// Export functions
export { TransactionManager, UnitOfWork, TransactionIsolation, NestedTransaction, TransactionLogger, DistributedTransactionCoordinator };
export { demonstrateTransactionHandling };