Kako koristiti Sequelize s Node.js i MySQL-om


Autor je odabrao Fond za otvoreni internet/slobodu govora za primanje donacije putem programa Pisanje za donacije.

Uvod

Sequelize je Object Relational Mapper temeljen na Node.js-u koji olakšava rad s MySQL, MariaDB, SQLite, PostgreSQL bazama podataka i drugim. Object Relational Mapper obavlja funkcije poput upravljanja zapisima u bazi podataka predstavljanjem podataka kao objekata. Sequelize ima moćan mehanizam migracija koji može transformirati postojeće sheme baze podataka u nove verzije. Sve u svemu, Sequelize pruža izvrsnu podršku za sinkronizaciju baza podataka, eager loading, asocijacije, transakcije i migracije baza podataka, istovremeno smanjujući vrijeme razvoja i sprječavajući SQL injekcije.

U ovom vodiču instalirat ćete i konfigurirati Sequelize s MySQL-om na svom lokalnom razvojnome okruženju. Zatim ćete koristiti Sequelize za stvaranje baza podataka i modela, kao i za izvođenje operacija insert, select i delete. Nakon toga, kreirat ćete Sequelize asocijacije za jedan-prema-jedan, jedan-prema-više i više-prema-više odnose. Na kraju, kreirat ćete Sequelize raw upite za zamjene nizova i objekata. Osim ovih osnovnih zadataka, vodič također pokriva sigurnu konfiguraciju baze podataka korištenjem varijabli okoline, sigurno upravljanje promjenama sheme putem migracija, te najbolje prakse za otklanjanje poteškoća i optimizaciju performansi.

Postavite svoje frontend aplikacije s GitHub-a koristeći Linux-Console.net App Platform. Neka Linux-Console.net brine o skaliranju vaše aplikacije.

Ključne točke:

  • Sequelize pruža jednostavan način za interakciju s MySQL-om koristeći modele umjesto sirovog SQL-a, čineći rad s bazom podataka jednostavnijim i organiziranijim.
  • Spremanje vjerodajnica baze podataka u varijable okoline čuva osjetljive informacije sigurnima i izvan kontrole verzija.
  • Korištenje migracija za ažuriranja sheme sigurnije je od oslanjanja na sequelize.sync(), posebno u produkcijskim okruženjima.
  • Sequelize olakšava definiranje odnosa jedan-prema-jedan, jedan-prema-više i više-prema-više između modela.
  • Sirovi SQL upiti i dalje se mogu koristiti kada su vam potrebna veća kontrola ili bolja izvedba za složene operacije nad bazom podataka.
  • Najčešći problemi sa Sequelizeom, poput problema s vezom ili pogrešaka pri migraciji, mogu se riješiti provjerom varijabli okoline, pregledom dnevnika i korištenjem MySQL alata poput EXPLAIN i SHOW PROCESSLIST.
  • Performanse se mogu poboljšati optimizacijom upita, dodavanjem odgovarajućih indeksa, podešavanjem poolova veza i pohranjivanjem često pristupanim podacima u cache.
  • Testiranje migracija, dodavanje rukovanja pogreškama i praćenje performansi baze podataka pomažu u održavanju pouzdanosti i spremnosti Sequelize aplikacija za proizvodnju.

Preduvjeti

Za dovršetak ovog vodiča trebat će vam:

  • Razvojno okruženje koje koristi Node.js. Da biste to postavili, slijedite vodič Kako instalirati Node.js i kreirati lokalno razvojno okruženje za vaš operativni sustav.
  • MySQL instaliran u vašem radnom okruženju. Slijedite vodič Kako instalirati MySQL za vaše okruženje, uključujući korake za stvaranje korisnika.
  • Poznavanje JavaScripta i MySQL upita, koje možete razvijati uz seriju How To Code in JavaScript i tutorial Uvod u upite u MySQL-u.
  • Osnovno razumijevanje pojmova relacijskih baza podataka, koje možete pronaći u vodiču Razumijevanje relacijskih baza podataka.

Ovaj je vodič testiran na Node.js verziji v24.11.1 i npm verziji 11.6.2.

Instalacija i konfiguracija Sequelize-a

U ovom koraku instalirat ćete Sequelize i uspostaviti vezu s vašom MySQL bazom podataka. Da biste to učinili, prvo ćete stvoriti Node.js aplikaciju. Zatim ćete instalirati Sequelize, konfigurirati MySQL bazu podataka i razviti jednostavnu aplikaciju.

Instaliranje Sequelizea

Započnite stvaranjem projekta. U ovom primjeru možete koristiti hello-world. Kada je mapa stvorena, otvorite terminal i idite u tu mapu:

mkdir hello-world

cd hello-world

Zatim, kreirajte primjer Node.js aplikacije koristeći sljedeću naredbu:

npm init

Zatim ćete biti upitani da odgovorite na neka početna pitanja. Upotrijebite sljedeći izlaz za svoju konfiguraciju. Pritisnite ENTER da koristite prikazanu zadanu vrijednost i budite sigurni da postavite glavnu ulaznu točku kao server.js. Ovo stvara strukturu projekta koja je jednostavna za održavanje.

Izlaz će izgledati kako slijedi, što će popuniti datoteku package.json:

{
  "name": "hello-world",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Zatim, stvorite prazan server.js datoteku unutar mape projekta:

touch server.js

Nakon što slijedite prethodne korake, vaša konačna struktura mapa će izgledati ovako:

hello-world/
├─ package.json
├─ server.js

Sada možete instalirati Sequelize pomoću sljedeće naredbe:

npm i sequelize

Nakon ovih ažuriranja, datoteka package.json sada izgleda ovako:

{
  "name": "hello-world",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "sequelize": "^6.37.7"
  }
}

U odjeljku dependencies sada ćete vidjeti Sequelize ovisnost.

Postavili ste projekt i instalirali Sequelize. Sljedeće, stvorit ćete primjer baze podataka za povezivanje.

Izrada primarne baze podataka

Kao dio preduvjeta, instalirali i konfigurirali ste MySQL, što je uključivalo i kreiranje korisnika. Sada ćete stvoriti praznu bazu podataka.

Da biste to učinili, prvo se trebate prijaviti na svoj MySQL primjerak. Ako radite udaljeno, možete koristiti svoj omiljeni alat. Ako koristite lokalno pokrenuti MySQL primjerak, možete koristiti sljedeću naredbu, zamjenjujući your_username svojim MySQL korisničkim imenom:

mysql -u your_username -p

-u je korisničko ime, a opcija -p se koristi ako je račun zaštićen lozinkom.

MySQL poslužitelj će tražiti vašu lozinku za bazu podataka. Unesite svoju lozinku i pritisnite ENTER.

Kad se prijavite, stvorite bazu podataka pod nazivom hello_world_db koristeći sljedeću naredbu:

CREATE DATABASE hello_world_db;

Da biste provjerili jeste li uspješno stvorili bazu podataka, možete koristiti ovu naredbu:

SHOW DATABASES;

Vaš izlaz bit će sličan ovome:

+--------------------+
|      Database      |
+--------------------+
| hello_world_db     |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

Nakon što kreirate uzorak baze podataka, prekinite vezu s MySQL poslužiteljem:

mysql> QUIT

Sada trebate instalirati ručni upravljački program za odabranu bazu podataka. Budući da Sequelize pruža samo ORM značajke, ne uključuje ugrađene upravljačke programe za bazu podataka. Stoga ćete trebati instalirati upravljačke programe prema vlastitim željama. Da biste to učinili, idite u direktorij projekta pomoću terminala i instalirajte MySQL upravljački program u projekt koristeći sljedeću naredbu:

npm install --save mysql2

U ovom slučaju koristite upravljački program za MySQL.

Napomena: Budući da ovaj vodič koristi MySQL kao bazu podataka, koristite odgovarajući upravljački program za to. Ovisno o vašoj bazi podataka, upravljački program možete ručno instalirati ovako:

  • npm install --save pg pg-hstore # Postgres
  • npm install --save mysql2
  • npm install --save mariadb
  • npm install --save sqlite3
  • npm install --save tedious # Microsoft SQL Server

Sada kada imate uzorak baze podataka, možete stvoriti svoju prvu Sequelize aplikaciju s povezivošću na bazu podataka.

Povezivanje s MySQL bazom podataka

U ovom odjeljku povezat ćete Node.js aplikaciju s MySQL bazom podataka koristeći Sequelize.

Da biste se povezali s bazom podataka, otvorite server.js za uređivanje koristeći nano ili vaš omiljeni uređivač koda:

nano server.js

Ovdje ćete u svojoj aplikaciji uspostaviti vezu s bazom podataka koristeći Sequelize instancu. U metodi new Sequelize() proslijedite parametre MySQL servera i vjerodajnice baze podataka na sljedeći način, zamijenivši DATABASE_USERNAME i DATABASE_PASSWORD vjerodajnicama vašeg MySQL korisnika:

const Sequelize = require("sequelize");
const sequelize = new Sequelize(
 'hello_world_db',
 'DATABASE_USERNAME',
 'DATABASE_PASSWORD',
  {
    host: 'DATABASE_HOST',
    dialect: 'mysql'
  }
);

host je mjesto na kojem je smješten MySQL server, pa ćete morati navesti URL servera ili IP adresu. Ako koristite lokalno instalirani MySQL server, možete zamijeniti DATABASE_HOST s localhost ili 127.0.0.1 kao vrijednost.

Slično tome, ako koristite udaljeni poslužitelj, pobrinite se da odgovarajuće zamijenite vrijednosti za povezivanje s bazom podataka s podacima o udaljenom poslužitelju.

Napomena: Ako koristite bilo koji drugi softver za poslužitelj baze podataka, parametar dijalekta možete zamijeniti u skladu s tim.

Zatim pozovite metodu authenticate() zasnovanu na obećanjima kako biste uspostavili vezu s bazom podataka za aplikaciju. Za to dodajte sljedeći blok koda u svoju datoteku server.js:

...

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

Metoda authenticate() koristi se za povezivanje s bazom podataka i testira jesu li zadani podaci za prijavu točni. Ovdje je veza s bazom podataka po defaultu otvorena i ista veza može se koristiti za sve upite. Kad trebate zatvoriti vezu, pozovite metodu sequelize.close() nakon ovog poziva authenticate(). Za više informacija o Sequelizeu, pogledajte njihov vodič za početnike.

Većina metoda koje pruža Sequelize je asinkrona. To znači da možete pokretati procese u svojoj aplikaciji dok je asinkroni blok koda u tijeku izvršavanja. Također, nakon uspješnog izvršenja asinkronog bloka koda, on vraća obećanje (promise), što je vrijednost koja se vraća na kraju procesa. Stoga, u asinkronim blokovima koda možete koristiti then(), catch() i finally() za vraćanje obrađenih podataka.

U ovom trenutku, datoteka server.js će izgledati ovako:

const Sequelize = require("sequelize");

const sequelize = new Sequelize(
   'hello_world_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

Spremi i zatvori svoj file.

U direktoriju projekta pokrenite aplikaciju server.js izvođenjem sljedeće naredbe:

node server.js

Vaš rezultat će izgledati ovako:

Connection has been established successfully!

Uspješno ste uspostavili vezu s bazom podataka.

U ovom koraku instalirali ste Sequelize, kreirali primjer baze podataka i koristili Sequelize za povezivanje s bazom podataka. Sljedeće, radit ćete s modelima u Sequelizeu.

Stvaranje tablice baze podataka pomoću Sequelizera

Sada kada ste kreirali uzorak MySQL baze podataka, možete koristiti Sequelize za kreiranje tablice i popunjavanje podacima. U Sequelizu se baze podataka tablice nazivaju modelima. Model je apstrakcija koja predstavlja tablicu u bazi podataka. Modeli definiraju nekoliko stvari za Sequelize, kao što su naziv tablice, detalji stupaca i tipovi podataka. U ovom koraku kreirat ćete Sequelize model za podatke o knjigama.

Za početak, stvorite novu datoteku pod nazivom book.model.js u direktoriju projekta:

nano book.model.js

Slično kao i prethodni korak, dodajte Sequelize kod za inicijalizaciju baze podataka s novim uvozom za DataTypes na vrhu datoteke:

const { Sequelize, DataTypes } = require("sequelize");

Sequelize sadrži mnoge ugrađene tipove podataka. Za pristup tim tipovima podataka, dodajete uvoz za DataTypes. Ovaj vodič se oslanja na neke često korištene tipove podataka, poput STRING, INTEGER i DATEONLY. Da biste saznali više o drugim podržanim tipovima podataka, možete se obratiti službenoj dokumentaciji Sequelize.

Zatim uključite redove koje ste ranije koristili za uspostavljanje veze s vašom MySQL bazom podataka, pritom ažurirajući svoje MySQL vjerodajnice:

...

const sequelize = new Sequelize(
   'hello_world_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

Zatim ćete kreirati model pod nazivom books, koji uključuje title, author, release_date i subject ID. Da biste to učinili, koristite metodu sequelize.define() kao što je prikazano:

...

const Book = sequelize.define("books", {
   title: {
     type: DataTypes.STRING,
     allowNull: false
   },
   author: {
     type: DataTypes.STRING,
     allowNull: false
   },
   release_date: {
     type: DataTypes.DATEONLY,
   },
   subject: {
     type: DataTypes.INTEGER,
   }
});

Metoda sequelize.define() definira novi model, koji predstavlja tablicu u bazi podataka. Ovaj blok koda stvara tablicu pod nazivom books i pohranjuje zapise o knjigama prema title, author, release_date i subject.

U ovom kodu, allowNull pokazuje da vrijednost stupca modela ne može biti null. Isto tako, ako trebate postaviti takvu vrijednost, možete koristiti defaultValue: "vrijednost".

Zatim ćete dodati model book u svoju bazu podataka. Da biste to učinili, koristit ćete metodu sync() na sljedeći način:

...

sequelize.sync().then(() => {
   console.log('Book table created successfully!');
}).catch((error) => {
   console.error('Unable to create table : ', error);
});

U metodi sync(), tražite od Sequelizea da učini nekoliko stvari s bazom podataka. Ovim pozivom, Sequelize će automatski izvršiti SQL upit prema bazi podataka i stvoriti tablicu, ispisujući poruku Book table created successfully!.

Kao što je spomenuto, metoda sync() je metoda bazirana na obećanjima (promise), što znači da također može izvršavati rukovanje pogreškama. U ovom bloku koda provjerit ćete je li tablica uspješno kreirana. Ako nije, vratit će pogrešku putem metode catch i ispisati je na izlazu.

Napomena: Možete upravljati sinkronizacijom modela prosljeđivanjem force parametara kako biste prisilili stvaranje nove tablice ako ne postoji, ili pak koristili postojeću. Evo nekoliko primjera koji vam mogu biti korisni dok radite sa Sequelize:

  • model.sync(): Ovo stvara tablicu ako već ne postoji.
  • model.sync({ force: true }): Ovo stvara tablicu tako da je prvo izbriše ako tablica s istim imenom već postoji.

Konačni kod će izgledati ovako:

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'hello_world_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

const Book = sequelize.define("books", {
   title: {
     type: DataTypes.STRING,
     allowNull: false
   },
   author: {
     type: DataTypes.STRING,
     allowNull: false
   },
   release_date: {
     type: DataTypes.DATEONLY,
   },
   subject: {
     type: DataTypes.INTEGER,
   }
});

sequelize.sync().then(() => {
   console.log('Book table created successfully!');
}).catch((error) => {
   console.error('Unable to create table : ', error);
});

Spremi i zatvori svoj file.

Pokrenite svoju aplikaciju koristeći sljedeću naredbu:

node book.model.js

Dobit ćete sljedeći izlaz u vašem naredbenom retku:

Executing (default): SELECT 1+1 AS result
Executing (default): CREATE TABLE IF NOT EXISTS `books` (`id` INTEGER NOT NULL auto_increment , `title` VARCHAR(255) NOT NULL, `author` VARCHAR(255) NOT NULL, `release_date` DATE, `subject` INTEGER, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Connection has been established successfully.
Executing (default): SHOW INDEX FROM `books`
Book table created successfully!

U izlazu ćete vidjeti da zapis povrata sadrži poruku, Tablica knjiga uspješno je kreirana!. To možete provjeriti provjerom svoje baze podataka kako biste vidjeli novu books tablicu kreiranu u hello_world_db bazi podataka.

Da biste provjerili kreiranje nove tablice, prijavite se u svoj MySQL instance:

mysql -u YOUR_USERNAME -p

Nakon unosa svoje lozinke, promijenite se u primjer baze podataka:

USE hello_world_db;

A zatim pokrenite naredbu za prikaz tablica:

SHOW TABLES;

Vaš izlaz bit će sličan ovome:

+---------------------------+
| Tables_in_hello_world_db |
+---------------------------+
| books                     |
+---------------------------+
1 row in set (0.00 sec)

Na kraju se odspojite s MySQL servera:

mysql> QUIT

Potvrdili ste da je stvaranje modela book bilo uspješno. Koristeći ovaj postupak, možete stvoriti bilo koji broj modela slijedeći isti postupak.

U ovom koraku stvorili ste model u bazi podataka i započeli rad s modelom koristeći ugrađene metode. Također ste koristili tipove podataka koje podržava Sequelize za definiranje svog modela. Sljedeće, radit ćete s osnovnim upitima modela.

Korištenje Sequelize-a za upite baze podataka

U ovom koraku koristit ćete ugrađene upite Sequelize-a za umetanje, odabir, odabir s uvjetnim klauzulama i brisanje.

Umetanje novog zapisa

U prethodnom koraku ste kreirali book model unutar baze podataka. U ovom dijelu ćete unijeti podatke u taj model.

Za početak, kopirajte sadržaj iz book.model.js iz prethodnog koraka. Napravite novu datoteku pod nazivom book.controller.js za rukovanje logikom upita. Dodajte kod iz book.model.js u book.controller.js.

U book.controller.js pronađite metodu sync(). U metodi sync(), dodajte sljedeće označene linije:

...

sequelize.sync().then(() => {
   console.log('Book table created successfully!');

   Book.create({
       title: "Clean Code",
       author: "Robert Cecil Martin",
       release_date: "2021-12-14",
       subject: 3
   }).then(res => {
       console.log(res)
   }).catch((error) => {
       console.error('Failed to create a new record : ', error);
   });

}).catch((error) => {
   console.error('Unable to create table : ', error);
});

Ovdje umetnete novi zapis o knjizi u model books koji ste već kreirali koristeći metodu sync(), koja podržava dodavanje novih zapisa u prethodno kreirane modele. Kada se metoda sync() izvrši uspješno, pokreće metodu then(). Unutar metode then() pozivate metodu create() kako biste umetnuli nove zapise u model.

Metodu create() koristite za prosljeđivanje podataka koje trebate dodati u bazu podataka kao objekt. Oznaka u kodu umetnut će novi zapis u već postojeću tablicu books. U ovom primjeru dodajete Clean Code autora Robert C. Martin, koji je kategoriziran s subject ID-om 3. Možete koristiti isti kod, ažuriran s informacijama za druge knjige, za dodavanje novih zapisa u vašu bazu podataka.

Spremi i zatvori datoteku.

Pokrenite aplikaciju koristeći sljedeću naredbu:

node book.controller.js

Vaš će izlaz izgledati slično sljedećem:

books {
  dataValues:
   { id: 1,
     title: 'Clean Code',
     author: 'Robert Cecil Martin',
     release_date: '2021-12-14',
     subject: 3,
     updatedAt: 2021-12-14T10:12:16.644Z,
   ...
}

Unijeli ste novi zapis u model koji ste stvorili u bazi podataka. Možete nastaviti dodavati više zapisa koristeći isti postupak.

Odabir svih zapisa

U ovom odjeljku ćete odabrati i dobiti sve zapise o knjigama iz baze podataka koristeći metodu findAll(). Da biste to učinili, prvo otvorite book.controller.js i uklonite prethodnu metodu Book.create(). U metodu sync() dodajte metodu Book.findAll() kao što je prikazano:

...

sequelize.sync().then(() => {

    Book.findAll().then(res => {
        console.log(res)
    }).catch((error) => {
        console.error('Failed to retrieve data : ', error);
    });

}).catch((error) => {
    console.error('Unable to create table : ', error);
});

...

Spremi i zatvori datoteku.

Zatim ponovo pokrenite aplikaciju koristeći sljedeću naredbu:

node book.controller.js

Vaš će izlaz izgledati slično sljedećem:

[
  books {
    dataValues: {
      id: 1,
      title: 'Clean Code',
      author: 'Robert Cecil Martin',
      release_date: '2020-01-01',
      subject: 3,
      createdAt: 2021-02-22T09:13:55.000Z,
      updatedAt: 2021-02-22T09:13:55.000Z
    },
    _previousDataValues: {
      id: 1,
      title: 'Clean Code',
      author: 'Robert Cecil Martin',
      release_date: '2020-01-01',
      subject: 3,
      createdAt: 2021-02-22T09:13:55.000Z,
      updatedAt: 2021-02-22T09:13:55.000Z
    },
...
]

Izlaz sadrži sve podatke o knjigama kao objekt polja. Uspješno ste koristili Sequelize findAll() metodu za vraćanje svih podataka o knjigama iz baze podataka.

Odabir pomoću where klauzule

U ovom odjeljku odabrat ćete vrijednosti s uvjetima koristeći where klauzulu. Where klauzula se koristi za specificiranje uvjeta prilikom dohvaćanja podataka. Za ovaj tutorial, dobit ćete knjigu po određenom ID-u zapisa iz baze podataka koristeći findOne() metodu.

Da biste to učinili, otvorite book.controller.js za uređivanje, izbrišite metodu findAll() i dodajte sljedeće retke:

...

sequelize.sync().then(() => {

    Book.findOne({
        where: {
            id: 1
        }
    }).then(res => {
        console.log(res)
    }).catch((error) => {
        console.error('Failed to retrieve data : ', error);
    });

}).catch((error) => {
    console.error('Unable to create table : ', error);
});

Ovdje odabirete određeni zapis o knjizi iz baze podataka koristeći metodu findOne() s opcijom where. U ovom primjeru dohvaćate podatke o knjizi čiji je id jednak 1.

Spremi i zatvori datoteku.

Zatim pokrenite aplikaciju:

node book.controller.js

Vaš će izlaz izgledati slično sljedećem:

books {
  dataValues: {
    id: 1,
    title: 'Clean Code',
    author: 'Robert Cecil Martin',
    release_date: '2020-01-01',
    subject: 'Science',
    createdAt: 2021-02-22T09:13:55.000Z,
    updatedAt: 2021-02-22T09:13:55.000Z
  },
  ...
}

Uspješno ste koristili where klauzule za dohvat podataka iz Sequelize modela. Možete koristiti where klauzulu u aplikaciji baze podataka za dohvat uvjetnih podataka.

Brisanje zapisa

Za brisanje određenog zapisa iz baze podataka koristite metodu destroy() s opcijom where. Da to učinite, otvorite book.controller.js, uklonite metodu findOne() i dodajte sljedeće istaknute redove:

...
sequelize.sync().then(() => {

  Book.destroy({
      where: {
        id: 2
      }
  }).then(() => {
      console.log("Successfully deleted record.")
  }).catch((error) => {
      console.error('Failed to delete record : ', error);
  });

}).catch((error) => {
    console.error('Unable to create table : ', error);
});

Ovdje uklanjate zapis o knjizi iz baze podataka koristeći metodu destroy() s opcijom where i prosljeđujući id knjige koju želite ukloniti. Uklonit ćete zapis o knjizi čiji id je 2.

Spremi i zatvori datoteku.

Zatim pokrenite aplikaciju:

node book.controller.js

Vaš izlaz će izgledati ovako:

Successfully deleted record.

Zapis je izbrisan.

U ovom koraku eksperimentirali ste sa svojim modelom baze podataka i upitima prema modelu. Pokrenuli ste bazu podataka, kreirali modele, umetnuli zapise, dohvatili zapise, dohvatili zapise s uvjetima koristeći where klauzulu i izbrisali odabrane zapise. S ovim znanjem o Sequelizeu, sada ćete kreirati asocijacije u Sequelizeu. Nakon toga moći ćete definirati i raditi s različitim vrstama odnosa koristeći Sequelize modele.

Izrada asocijacija koristeći Sequelize

U ovom koraku koristit ćete standardne tipove povezanosti koje Sequelize podržava: jedan-prema-jedan, jedan-prema-više i više-prema-više povezivanja. Koristit ćete uzorke podataka o studentima, tečajevima i razinama ocjena.

Sequelize koristi vrste asocijacija temeljene na sljedećim odnosima u bazi podataka:

  • jedan-na-jedan odnos: Odnos jedan-na-jedan znači da je zapis u jednoj tablici povezan točno s jednim zapisom u drugoj tablici. U terminima Sequelize, možete koristiti belongsTo() i hasOne() asocijacije za stvaranje ovog tipa odnosa.

  • jedan-prema-više odnos: Jedan-prema-više odnos znači da je zapis u jednoj tablici povezan s više zapisa u drugoj tablici. Uz Sequelize, možete koristiti hasMany() metode asocijacija za stvaranje ove vrste odnosa.

  • mnogo-prema-mnogo odnos: Odnos mnogo-prema-mnogo znači da su višestruki zapisi u jednoj tablici povezani s višestrukim zapisima u drugoj tablici. S Sequelizeom možete koristiti belongsToMany() asocijacije za stvaranje ove vrste odnosa.

Prije nego što stvorite ove veze, prvo ćete stvoriti novu bazu podataka pod nazivom student_db i dodati nove modele te neke primjerke podataka za studente, kolegije i razinu ocjena.

Za kreiranje baze podataka, slijedite isti postupak kao u Instaliranju i Konfiguriranju Sequelize kako biste se prijavili u MySQL i kreirali bazu podataka pod nazivom student_db. Nakon što je nova baza podataka kreirana, odjavite se iz MySQL-a. Zatim ćete početi s kreiranjem veza između baza podataka.

Kreiranje veze jedan-na-jedan s belongsTo()

U ovom odjeljku ćete kreirati odnos jedan-prema-jedan koristeći Sequelize modele. Zamislite da želite dobiti detalje jednog studenta zajedno s njegovim razredom. Budući da jedan student može imati samo jedan razred, ovaj tip veze je odnos jedan-prema-jedan i možete koristiti metodu belongsTo().

Napomena: Postoji razlika između belongsTo() i hasOne(). belongsTo() će dodati foreignKey na izvornoj tablici, dok će hasOne() dodati na ciljnoj tablici. U svakom slučaju, ako se obje veze koriste istovremeno, radit će kao Sequelize dvosmjerne one-to-one veze.

Metoda belongsTo() omogućuje vam stvaranje jednog-na-jedan odnosa između dva Sequelize modela. U ovom primjeru koristite modele Student i Grade.

Napravite novu datoteku pod nazivom one_to_one.js. Kao što ste učinili u prethodnom odjeljku, Povezivanje s MySQL bazom podataka, uključite retke za stvaranje veze s bazom podataka i autentifikaciju vašeg MySQL korisnika na vrh datoteke. Obavezno ažurirajte MySQL vjerodajnice po potrebi:

const { Sequelize, DataTypes } = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

U ovom odjeljku stvorit ćete tri modela u novoj bazi podataka student_db: Student, Grade i Course. Počet ćete s kreiranjem modela Student i Grade. Kasnije u ovom koraku stvorit ćete model Courses.

Za model Student, dodajte sljedeći blok koda u one_to_one.js:

...
const Student = sequelize.define("students", {
   student_id: {
       type: DataTypes.UUID,
       defaultValue: DataTypes.UUIDV4,
       primaryKey: true,
   },
   name: {
       type: DataTypes.STRING,
       allowNull: false
   }
});

Ovaj studentski model sadrži dva stupca: student_id i name.

Zatim dodajte blok koda za Grade model:

...
const Grade = sequelize.define("grades", {
   grade: {
       type: DataTypes.INTEGER,
       allowNull: false
   }
});

Model Grade sadrži stupac grade.

Da biste demonstrirali povezanosti, morat ćete dodati uzorke podataka u bazu podataka. Za to ćete koristiti metodu bulk(). Umjesto da unosite podatke u redove jedan po jedan, metoda bulkCreate() omogućuje vam da odjednom ubacite više redova u vaše modele baze podataka.

Dakle, sada uvezite podatke Grade i Student u njihove odgovarajuće modele u bazi podataka kao što je prikazano:

...
const grade_data = [{grade : 9}, {grade : 10}, {grade : 11}]

const student_data = [
   {name : "John Baker", gradeId: 2},
   {name : "Max Butler", gradeId: 1},
   {name : "Ryan Fisher", gradeId: 3},
   {name : "Robert Gray", gradeId: 2},
   {name : "Sam Lewis", gradeId: 1}
]

sequelize.sync({ force: true }).then(() => {
   Grade.bulkCreate(grade_data, { validate: true }).then(() => {
       Student.bulkCreate(student_data, { validate: true }).then(() => {
           …
       }).catch((err) => { console.log(err); });
   }).catch((err) => { console.log(err); });
}).catch((error) => {
   console.error('Unable to create the table : ', error);
});

Ovdje unosite uzorčne podatke i umetnete podatke u modele Student i Grade. S vašom bazom podataka, modelima i uzorčnim podacima spremnim, spremni ste za kreiranje poveznica.

U one-to-one.js, dodajte sljedeći redak ispod bloka student_data:

...
Student.belongsTo(Grade);

Zatim ćete morati provjeriti radi li veza ispravno. Da biste to učinili, možete dohvatiti podatke svih studenata s pripadajućim razredima tako da unutar metode findAll() proslijedite parametar include.

Budući da trebate dobiti razinu ocjene učenika, proslijedit ćete Grade kao model. U metodi sequelize.sync() dodajte označene redove kao što je prikazano:

...
sequelize.sync({ force: true }).then(() => {
   Grade.bulkCreate(grade_data, { validate: true }).then(() => {
       Student.bulkCreate(student_data, { validate: true }).then(() => {
           Student.findAll({
               include: [{
                   model: Grade
               }]
           }).then(result => {
               console.log(result)
           }).catch((error) => {
               console.error('Failed to retrieve data : ', error);
           });
       }).catch((err) => { console.log(err); });
   }).catch((err) => { console.log(err); });
}).catch((error) => {
   console.error('Unable to create the table : ', error);
});

Cjeloviti kod izgleda ovako:

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );
sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

const Student = sequelize.define("students", {
   student_id: {
       type: DataTypes.UUID,
       defaultValue: DataTypes.UUIDV4,
       primaryKey: true,
   },
   name: {
       type: DataTypes.STRING,
       allowNull: false
   }
});

const Grade = sequelize.define("grades", {
   grade: {
       type: DataTypes.INTEGER,
       allowNull: false
   }
});

const grade_data = [{grade : 9}, {grade : 10}, {grade : 11}]

const student_data = [
   {name : "John Baker", gradeId: 2},
   {name : "Max Butler", gradeId: 1},
   {name : "Ryan Fisher", gradeId: 3},
   {name : "Robert Gray", gradeId: 2},
   {name : "Sam Lewis", gradeId: 1}
]

// One-To-One association
Student.belongsTo(Grade);

sequelize.sync({ force: true }).then(() => {
   Grade.bulkCreate(grade_data, { validate: true }).then(() => {
       Student.bulkCreate(student_data, { validate: true }).then(() => {
           Student.findAll({
               include: [{
                   model: Grade
               }]
           }).then(result => {
               console.log(result)
           }).catch((error) => {
               console.error('Failed to retrieve data : ', error);
           });
       }).catch((err) => { console.log(err); });
   }).catch((err) => { console.log(err); });
}).catch((error) => {
   console.error('Unable to create the table : ', error);
});

Spremi i zatvori svoj file.

Pokrenite datoteku koristeći sljedeću naredbu:

node one_to_one.js

Ispis će biti dug, i vidjet ćete podatke svih učenika s razinama ocjena. Evo isječak ispisa koji prikazuje podatke o učenicima:

students {
    dataValues:
   { student_id: '3e786a8f-7f27-4c59-8e9c-a8c606892288',
       name: 'Sam Lewis',
       createdAt: 2021-12-16T08:49:38.000Z,
       updatedAt: 2021-12-16T08:49:38.000Z,
       gradeId: 1,
       grade: [grades] },
    _previousDataValues:
...

Ovisno o alatima naredbenog retka koje koristite, izlaz se može prikazati kao prošireni prikaz ili ne. Ako je to prošireni prikaz, ispisuje prošireni grade objekt kao izlaz.

U ovom odjeljku ste kreirali vezu jedan-na-jedan koristeći poziv metode Student.belongsTo(Grade); i dobili podatke prema vezi koju ste stvorili.

Kreiranje veza jedan-prema-više pomoću hasMany()

U ovom odjeljku ćete kreirati odnos jedno-prema-više koristeći Sequelize modele. Zamislite da želite dobiti sve učenike povezane s odabranom razinom razreda. Budući da jedna određena razina razreda može imati više učenika, ovo je odnos jedno-prema-više.

Za početak, kopirajte sadržaj datoteke one_to_one.js u novu datoteku pod nazivom one_to_many.js. U datoteci one_to_many.js uklonite redove nakon bloka student_data. Vaša datoteka one_to_many.js će izgledati ovako:

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

const Student = sequelize.define("students", {
   student_id: {
       type: DataTypes.UUID,
       defaultValue: DataTypes.UUIDV4,
       primaryKey: true,
   },
   name: {
       type: DataTypes.STRING,
       allowNull: false
   }
});
const Grade = sequelize.define("grades", {
   grade: {
       type: DataTypes.INTEGER,
       allowNull: false
   }
});

const grade_data = [ {grade : 9}, {grade : 10}, {grade : 11}]

const student_data = [
   {name : "John Baker", gradeId: 2},
   {name : "Max Butler", gradeId: 1},
   {name : "Ryan Fisher", gradeId: 3},
   {name : "Robert Gray", gradeId: 2},
   {name : "Sam Lewis", gradeId: 1}
]

Nakon bloka student_data, koristite metodu hasMany() za stvaranje nove veze:

...
Grade.hasMany(Student)

Metoda hasMany() omogućuje vam stvaranje odnosa jedan-prema-više između dvaju Sequelize modela. Ovdje koristite modele Grade i Student.

Zatim dodajte metodu sequelize.sync() s metodom findAll() ispod linije hasMany():

...
sequelize.sync({ force: true }).then(() => {
   Grade.bulkCreate(grade_data, { validate: true }).then(() => {
       Student.bulkCreate(student_data, { validate: true }).then(() => {
           Grade.findAll({
               where: {
                   grade: 9
               },
               include: [{
                   model: Student
               }]
           }).then(result => {
               console.dir(result, { depth: 5 });
           }).catch((error) => {
               console.error('Failed to retrieve data : ', error);
           });
       }).catch((err) => { console.log(err); });
   }).catch((err) => { console.log(err); });
}).catch((error) => {
   console.error('Unable to create table : ', error);
});

Ovdje pokušavate pristupiti svim učenicima u određenoj razini razreda – u ovom slučaju, svim učenicima u 9. razredu. Također ste dodali model Student u opciju include.

Evo kompletnog koda:

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

const Student = sequelize.define("students", {
   student_id: {
       type: DataTypes.UUID,
       defaultValue: DataTypes.UUIDV4,
       primaryKey: true,
   },
   name: {
       type: DataTypes.STRING,
       allowNull: false
   }
});
const Grade = sequelize.define("grades", {
   grade: {
       type: DataTypes.INTEGER,
       allowNull: false
   }
});

const grade_data = [ {grade : 9}, {grade : 10}, {grade : 11}]

const student_data = [
   {name : "John Baker", gradeId: 2},
   {name : "Max Butler", gradeId: 1},
   {name : "Ryan Fisher", gradeId: 3},
   {name : "Robert Gray", gradeId: 2},
   {name : "Sam Lewis", gradeId: 1}
]

// One-To-Many relationship
Grade.hasMany(Student);

sequelize.sync({ force: true }).then(() => {
   Grade.bulkCreate(grade_data, { validate: true }).then(() => {
       Student.bulkCreate(student_data, { validate: true }).then(() => {
           Grade.findAll({
               where: {
                   grade: 9
               },
               include: [{
                   model: Student
               }]
           }).then(result => {
               console.dir(result, { depth: 5 });
           }).catch((error) => {
               console.error('Failed to retrieve data : ', error);
           });
       }).catch((err) => { console.log(err); });
   }).catch((err) => { console.log(err); });
}).catch((error) => {
   console.error('Unable to create table : ', error);
});

Spremi i zatvori svoj file.

Pokrenite datoteku sljedećom naredbom:

node one_to_many.js

Izlaz će izgledati slično sljedećem. Bit će prilično dug, ali svi učenici u razredu 9 bit će prikazani na sljedeći način:

[ grades {
    dataValues:
     { id: 1,
       grade: 9,
       createdAt: 2021-12-20T05:12:31.000Z,
       updatedAt: 2021-12-20T05:12:31.000Z,
       students:
        [ students {
            dataValues:
             { student_id: '8a648756-4e22-4bc0-8227-f590335f9965',
               name: 'Sam Lewis',
               createdAt: 2021-12-20T05:12:31.000Z,
               updatedAt: 2021-12-20T05:12:31.000Z,
               gradeId: 1 },
...
          students {
            dataValues:
             { student_id: 'f0304585-91e5-4efc-bdca-501b3dc77ee5',
               name: 'Max Butler',
               createdAt: 2021-12-20T05:12:31.000Z,
               updatedAt: 2021-12-20T05:12:31.000Z,
               gradeId: 1 },
...

U ovoj sekciji stvorili ste odnos jedan-prema-više koristeći poziv metode Grade.hasMany(Student);. U izlazu ste dohvatili detalje prema udruživanju koje ste stvorili.

Kreiranje veza mnogi-prema-mnogima s belongsToMany()

U ovom odjeljku kreirat ćete veze mnogi-prema-mnogima koristeći Sequelize modele. Kao primjer, zamislite situaciju u kojoj su studenti upisani na kolegije. Jedan student se može upisati na mnogo kolegija, a jedan kolegij može imati mnogo studenata. Ovo je veza mnogi-prema-mnogima. Za implementaciju ovog koncepta pomoću Sequelize-a, koristit ćete modele Student, Course i StudentCourse s metodom belongsToMany().

Za početak, stvorite datoteku pod nazivom many_to_many.js i dodajte blokove koda za inicijalizaciju i autentifikaciju baze podataka na sljedeći način. (Možete ponovno koristiti blokove koda iz prethodnog primjera one_to_many.js.) Pazite da po potrebi ažurirate označene vrijednosti za povezivanje s bazom podataka.

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
);

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

Zatim ćete kreirati modele baze podataka za odnose više prema više: Student i Course. Nakon toga ćete dodati neke primjerke podataka tim modelima.

...

const Student = sequelize.define("students", {
    student_id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
    },
    name: {
        type: DataTypes.STRING,
        allowNull: false
    }
});

const Course = sequelize.define("courses", {
    course_name: {
        type: DataTypes.STRING,
        allowNull: false
    }
});

const StudentCourse = sequelize.define('StudentCourse', {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true,
      allowNull: false
    }
  });

const course_data = [
    {course_name : "Science"},
    {course_name : "Maths"},
    {course_name : "History"}
]

const student_data = [
    {name : "John Baker", courseId: 2},
    {name : "Max Butler", courseId: 1},
    {name : "Ryan Fisher", courseId: 3},
    {name : "Robert Gray", courseId: 2},
    {name : "Sam Lewis", courseId: 1}
]

const student_course_data = [
    {studentId : 1, courseId: 1},
    {studentId : 2, courseId: 1},
    {studentId : 2, courseId: 3},
    {studentId : 3, courseId: 2},
    {studentId : 1, courseId: 2},
]

Ovdje stvarate modele Student i Course te pružate neke primjere podataka. Također postavljate courseID, koji ćete koristiti za dohvat studenata prema ovoj vrsti odnosa.

Na kraju ste definirali novi model pod nazivom StudentCourse, koji upravlja podacima o odnosu između Student i Course. U ovom primjeru, studentId 1 je upisan u courseId 1 i courseId 2.

Završili ste inicijalizaciju baze podataka i dodali uzorak podataka u bazu. Sljedeće, kreirajte veze mnogi-prema-mnogima koristeći metodu belongsToMany() kao što je prikazano:

...
Course.belongsToMany(Student, { through: 'StudentCourse'})
Student.belongsToMany(Course, { through: 'StudentCourse'})

Unutar metode belongsToMany() prosljeđujete konfiguraciju through s imenom modela kao opcijom konfiguracije. U ovom slučaju, to je StudentCourse. To je tablica koja upravlja odnosima mnogi-prema-mnogi.

Konačno, možete provjeriti radi li povezivanje ispravno dohvaćanjem svih podataka o tečajevima s pridruženim studentima. To ćete učiniti prosljeđivanjem parametra include unutar metode findAll(). Dodajte sljedeće retke u many_to_many.js:

...
sequelize.sync({ force: true }).then(() => {
    Course.bulkCreate(course_data, { validate: true }).then(() => {
        Student.bulkCreate(student_data, { validate: true }).then(() => {
            StudentCourse.bulkCreate(student_course_data, { validate: true }).then(() => {
                Course.findAll({
                    include: {
                        model: Student,
                    },
                }).then(result => {
                    console.log(result);
                }).catch((error) => {
                    console.error('Failed to retrieve data : ', error);
                });
            }).catch((error) => {
                console.log(error);
            });
        }).catch((error) => {
            console.log(error);
        });
    }).catch((error) => {
        console.log(error);
    });
}).catch((error) => {
    console.error('Unable to create table : ', error);
});

Cjeloviti kod izgleda ovako:

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
);

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

const Student = sequelize.define("students", {
    student_id: {
       type: DataTypes.UUID,
       defaultValue: DataTypes.UUIDV4,
    },
    name: {
       type: DataTypes.STRING,
       allowNull: false
    }
});

const Course = sequelize.define("courses", {
    course_name: {
        type: DataTypes.STRING,
        allowNull: false
    }
});

const StudentCourse = sequelize.define('StudentCourse', {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true,
      allowNull: false
    }
  });

const course_data = [
    {course_name : "Science"},
    {course_name : "Maths"},
    {course_name : "History"}
]

const student_data = [
    {name : "John Baker", courseId: 2},
    {name : "Max Butler", courseId: 1},
    {name : "Ryan Fisher", courseId: 3},
    {name : "Robert Gray", courseId: 2},
    {name : "Sam Lewis", courseId: 1}
]

const student_course_data = [
    {studentId : 1, courseId: 1},
    {studentId : 2, courseId: 1},
    {studentId : 2, courseId: 3},
    {studentId : 3, courseId: 2},
    {studentId : 1, courseId: 2},
]

Course.belongsToMany(Student, { through: 'StudentCourse'})
Student.belongsToMany(Course, { through: 'StudentCourse'})

sequelize.sync({ force: true }).then(() => {
    Course.bulkCreate(course_data, { validate: true }).then(() => {
        Student.bulkCreate(student_data, { validate: true }).then(() => {
            StudentCourse.bulkCreate(student_course_data, { validate: true }).then(() => {
                Course.findAll({
                    include: {
                        model: Student,
                    },
                }).then(result => {
                    console.log(result);
                }).catch((error) => {
                    console.error('Failed to retrieve data : ', error);
                });
            }).catch((error) => {
                console.log(error);
            });
        }).catch((error) => {
            console.log(error);
        });
    }).catch((error) => {
        console.log(error);
    });
}).catch((error) => {
    console.error('Unable to create table : ', error);
});

Spremi i zatvori datoteku.

Pokrenite datoteku koristeći sljedeću naredbu:

node many_to_many.js

Ispis će biti dug, ali će izgledati otprilike ovako:

[ courses {
    dataValues:
     { id: 1,
       course_name: 'Science',
       createdAt: 2022-05-11T04:27:37.000Z,
       updatedAt: 2022-05-11T04:27:37.000Z,
       students: [Array] },
    _previousDataValues:
     { id: 1,
       course_name: 'Science',
       createdAt: 2022-05-11T04:27:37.000Z,
       updatedAt: 2022-05-11T04:27:37.000Z,
       students: [Array] },
    _changed: Set {},
    _options:
     { isNewRecord: false,
       _schema: null,
       _schemaDelimiter: '',
       include: [Array],
       includeNames: [Array],
       includeMap: [Object],
       includeValidated: true,
       attributes: [Array],
       raw: true },
    isNewRecord: false,
    students: [ [students], [students] ] },
  courses {
    dataValues:
     { id: 2,
       course_name: 'Maths',
       createdAt: 2022-05-11T04:27:37.000Z,
       updatedAt: 2022-05-11T04:27:37.000Z,
       students: [Array] },
    _previousDataValues:
...

Kao što možete vidjeti u ovom izlazu, dohvaćeni su tečajevi s pridruženim studentima. Unutar bloka courses vidjet ćete odvojene id vrijednosti koje označavaju svaki tečaj. Na primjer, id: 1 povezano je s course_name: Science za sat iz znanosti, dok je id: 2 sat iz matematike, i tako dalje.

U bazi podataka možete vidjeti tri generirane tablice s uzorkom podataka koje ste unijeli.

U ovom koraku koristili ste Sequelize za stvaranje odnosa jedan-prema-jedan, jedan-prema-više i više-prema-više. Sljedeće ćete raditi s raw upitima.

Rad s neobrađenim upitima

U ovom koraku radit ćete s neobrađenim upitima u Sequelizeu. U prethodnim koracima koristili ste ugrađene metode Sequelize-a, kao što su insert() i findAll(), za umetanje podataka i njihovo dohvaćanje iz baze podataka. Možda ste primijetili da te metode slijede određeni obrazac za pisanje upita. Međutim, korištenjem neobrađenih upita, ne morate brinuti o ugrađenim metodama i obrascima Sequelize-a. Koristeći svoje znanje SQL upita, možete izvoditi razne upite u Sequelizeu, od jednostavnih do naprednijih.

Evo primjera sirovih upita koji izvršavaju radnju odabira svih vrijednosti iz određene tablice, brisanja odabranih vrijednosti prema uvjetu i ažuriranja tablice s danim vrijednostima.

SELECT * FROM table_name;
DELETE FROM table_name WHERE condition;
UPDATE table_name SET y = 42 WHERE x = 12;

U Sequelizeu, sirovi upiti mogu se koristiti prvenstveno na dva načina: zamjena pomoću niza i zamjena pomoću objekta. Kada prosljeđujete vrijednosti u SQL upit, možete koristiti ili niz ili objekt za tu zamjenu.

Prije nego što napišete sirovi upit, prvo ćete trebati unijeti podatke o studentima u primjer baze podataka. Slijedeći prethodni odjeljak, Kreiranje primjera baze podataka, prijavite se u MySQL, kreirajte bazu podataka pod nazivom sample_student_db i odjavite se iz MySQL-a.

Zatim ćete dodati neke sirove podatke kako biste počeli raditi s izvornim upitima. Napravite novu datoteku pod nazivom add_student_records.js i dodajte sljedeće blokove koda, koji sadrže prethodno spomenute Sequelize metode authenticate(), sync() i bulkCreate().

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'sample_student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
   );

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

const Student = sequelize.define("students", {
    student_id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
    },
    name: {
        type: DataTypes.STRING,
        allowNull: false
    }
});

const student_data = [
    {name : "John Baker"},
    {name : "Max Butler"},
    {name : "Ryan Fisher"},
    {name : "Robert Gray"},
    {name : "Sam Lewis"}
]

sequelize.sync({ force: true }).then(() => {

    Student.bulkCreate(student_data, { validate: true }).then((result) => {
        console.log(result);
    }).catch((error) => {
        console.log(error);
    });

}).catch((error) => {
    console.error('Unable to create table : ', error);
});

Ovdje pokrećete vezu s bazom podataka, kreirate model i unosite nekoliko zapisa o studentima u novu bazu podataka.

Spremi i zatvori datoteku.

Zatim pokrenite ovaj skript koristeći sljedeću naredbu:

node add_student_records.js

Izlaz će biti nešto slično sljedećem. Bit će prilično dugačak, ali svi studentski zapisi koje ste unijeli bit će vraćeni na sljedeći način. Imajte na umu da će student_id budući automatski generirana UUID (Univerzalno jedinstvena oznaka) vrijednost, biti različit ovisno o korisniku.

Executing (default): SELECT 1+1 AS result
Executing (default): DROP TABLE IF EXISTS `students`;
Connection has been established successfully.
Executing (default): DROP TABLE IF EXISTS `students`;
Executing (default): CREATE TABLE IF NOT EXISTS `students` (`student_id` CHAR(36) BINARY , `name` VARCHAR(255) NOT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`student_id`)) ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM `students`
Executing (default): INSERT INTO `students` (`student_id`,`name`,`createdAt`,`updatedAt`) VALUES ('45d1f26c-ba76-431f-ac5f-f41282351710','John Baker','2022-06-03 07:27:49','2022-06-03 07:27:49'),('1cb4e34d-bfcf-4a97-9624-e400b9a1a5f2','Max Butler','2022-06-03 07:27:49','2022-06-03 07:27:49'),('954c576b-ba1c-4dbc-a5c6-8eaf22bbbb04','Ryan Fisher','2022-06-03 07:27:49','2022-06-03 07:27:49'),('e0f15cd3-0025-4032-bfe8-774e38e14c5f','Robert Gray','2022-06-03 07:27:49','2022-06-03 07:27:49'),('826a0ec9-edd0-443f-bb12-068235806659','Sam Lewis','2022-06-03 07:27:49','2022-06-03 07:27:49');
[
  students {
    dataValues: {
      student_id: '45d1f26c-ba76-431f-ac5f-f41282351710',
      name: 'John Baker',
      createdAt: 2022-06-03T07:27:49.453Z,
      updatedAt: 2022-06-03T07:27:49.453Z
    },
    _previousDataValues: {
      name: 'John Baker',
      student_id: '45d1f26c-ba76-431f-ac5f-f41282351710',
      createdAt: 2022-06-03T07:27:49.453Z,
      updatedAt: 2022-06-03T07:27:49.453Z
    },

…

U sljedećem odjeljku primijenit ćete neobrađene upite koristeći jedan od student_id izlaza iz koda iznad. Kopirajte ga kako biste ga imali za sljedeće odjeljke, u kojima ćete koristiti metodu query() za zamjenu nizova i objekata.

Zamjena niza

U ovom odjeljku koristit ćete metodu query() za zamjenu niza. Pomoću ove metode, Sequelize može izvršavati sirove ili već pripremljene SQL upite.

Za početak, kopirajte sadržaj datoteke server.js iz Koraka 1, jer ona uključuje metodu Sequelize() za inicijalizaciju i pokretanje baze podataka. Zalijepite sadržaj u novu datoteku nazvanu array_raw_query.js. Ažurirajte naziv baze podataka na sample_student_db:

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'sample_student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
   );

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

Na kraju datoteke dodajte sljedeći blok koda za zamjenu niza, pazeći da REPLACE_STUDENT_ID zamijenite vrijednošću student_id koju ste kopirali u prethodnom odjeljku.

...
sequelize.query(
    'SELECT * FROM students WHERE student_id = ?',
    {
      replacements: ['REPLACE_STUDENT_ID'],
      type: sequelize.QueryTypes.SELECT
    }
).then(result => {
    console.log(result);
}).catch((error) => {
    console.error('Failed to insert data : ', error);
});

Za zamjenu nizova, prosljeđujete metodi query() SQL upit i objekt konfiguracije. On sadrži vrijednost i tip replacements. Za zamjene, podatke prosljeđujete kao niz i dohvaćate te vrijednosti korištenjem upitnika (?) simbola.

Zatim, budući da trebate dobiti podatke o određenom studentu, student_id se prosljeđuje kao drugi parametar. Nakon toga prosljeđujete par ključ-vrijednost type: sequelize.QueryTypes.SELECT, koji možete koristiti za odabir podataka iz baze podataka.

Postoje i neki drugi tipovi, poput QueryTypes.UPDATE i QueryTypes.DELETE. Ovisno o vašim potrebama, možete odabrati tip koji odgovara vašoj svrsi.

Sljedeće prikazuje puni blok koda. Ovdje se povezujete s bazom podataka i dohvaćate podatke odabranog studenta koristeći sirovi upit.

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'sample_student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
   );

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

sequelize.query(
    'SELECT * FROM students WHERE student_id = ?',
    {
      replacements: ['REPLACE_STUDENT_ID'],
      type: sequelize.QueryTypes.SELECT
    }
).then(result => {
    console.log(result);
}).catch((error) => {
    console.error('Failed to insert data : ', error);
});

Spremi i zatvori svoj file.

Zatim možete pokrenuti ovaj skript koristeći sljedeću naredbu:

node array_raw_query.js

Vidjet ćete izlaz sličan sljedećem:

Connection has been established successfully.
[ { student_id: 'STUDENT_ID_YOU_RETRIEVED',
    name: 'Robert Gray',
    createdAt: 2022-05-06T13:14:50.000Z,
    updatedAt: 2022-05-06T13:14:50.000Z } ]

Zbog odabranog student_id, vaši izlazni vrijednosti mogu se razlikovati.

Zamjena objekta

Na površini, zamjena objekata slična je zamjeni nizova, ali se način prosljeđivanja podataka u sirovi upit razlikuje. U opciji zamjene podatke prosljeđujete kao objekt, a u opciji upita koristite vrijednosti poput :key.

Za početak, kreirajte novu datoteku pod nazivom object_raw_query.js i zalijepite kompletne blokove koda iz datoteke server.js, ažurirajući bazu podataka na sample_student_db.

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'sample_student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
   );

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

Zatim dodajte sljedeći blok koda na kraj nove datoteke object_raw_query.js:

...
sequelize.query(
  'SELECT * FROM students WHERE student_id = :id',
  {
    replacements: { id: 'REPLACE_STUDENT_ID' },
    type: sequelize.QueryTypes.SELECT
  }
 ).then(result => {
    console.log(result);
}).catch((error) => {
    console.error('Failed to insert data : ', error);
});

Ovdje dobivate podatke o odabranom studentu koristeći metodu zamjene objekta. Kreirate objekt replacement, postavljajući id kao informacije o studentu koje želite dohvatiti: { id: '<mark>REPLACE_STUDENT_ID</mark>' }.

U query() navodite: 'SELECT * FROM students WHERE student_id=:id'. Koristeći metodu query(), prosljeđujete zamjensku vrijednost kao objekt, zbog čega je ova metoda poznata kao zamjena objekta.

Evo kompletnog koda:

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'sample_student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
   );

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

sequelize.query(
  'SELECT * FROM students WHERE student_id = :id',
  {
    replacements: { id: 'REPLACE_STUDENT_ID' },
    type: sequelize.QueryTypes.SELECT
  }
 ).then(result => {
    console.log(result);
}).catch((error) => {
    console.error('Failed to insert data : ', error);
});

Spremi i zatvori datoteku.

Zatim pokrenite ovaj skript koristeći sljedeću naredbu:

node object_raw_query.js

Ispis će izgledati slično sljedećem:

Connection has been established successfully.
[ { student_id: 'STUDENT_ID_YOU_RETRIEVED',
    name: 'Robert Gray',
    createdAt: 2022-05-06T13:14:50.000Z,
    updatedAt: 2022-05-06T13:14:50.000Z } ]

Zbog odabranog student_id vaši izlazni vrijednosti mogu se razlikovati.

U ovom koraku ste radili s Sequelize raw upitima koristeći dvije različite metodologije: zamjenu nizom i zamjenu objektom.

Konfiguriranje varijabli okoline za sigurne veze s bazom podataka

Do ovog trenutka pohranjivali ste podatke za pristup bazi podataka izravno u svom kodu. Iako ovaj pristup funkcionira za lokalno eksperimentiranje, nije siguran. Hardcodiranje akreditiva izlaže osjetljive informacije poput lozinki baze podataka i imena hosta, te povećava rizik od proboja kada se kod dijeli ili implementira. U ovom odjeljku naučit ćete kako pohraniti te akreditive sigurno koristeći varijable okruženja.

Zašto koristiti varijable okoline?

Varijable okoline djeluju kao vanjski parametri konfiguracije koje vaša aplikacija može čitati tijekom izvođenja. Umjesto da vjerodajnice ugrađujete izravno u svoj kod, one se pohranjuju u vaš sustav ili u zasebnu konfiguracijsku datoteku koja se nikada ne šalje u kontrolu verzija. Ovaj pristup donosi više prednosti:

  • Sigurnost: Vaši su vjerodajnici skriveni od javnih spremišta i sustava za kontrolu verzija.
  • Fleksibilnost: Svako okruženje (razvojno, testno, proizvodno) može koristiti različitu konfiguraciju baze podataka bez mijenjanja izvornog koda.
  • Održavanje: Centralizirana konfiguracija omogućuje jednostavna ažuriranja; ako se lozinka baze podataka promijeni, potrebno je promijeniti samo jednu datoteku ili varijablu.
  • Usklađenost: Ova praksa se usklađuje s metodologijom Twelve-Factor App, koja promiče upravljanje konfiguracijom specifičnom za okruženje za skalabilne aplikacije.

Postavljanje varijabli okoline

Koristit ćete paket dotenv za upravljanje vašim varijablama okoline u Node.js-u. Ova biblioteka čita varijable iz .env datoteke i ubacuje ih u Node.js process.env objekt, čineći ih dostupnima kroz cijelu vašu aplikaciju.

Instalirajte paket dotenv pokretanjem sljedeće naredbe:

npm install dotenv

Nakon instalacije, stvorite novu datoteku nazvanu .env u glavnom direktoriju vašeg projekta. Ova datoteka će sadržavati sve osjetljive podatke potrebne za povezivanje s bazom podataka.

Evo primjer konfiguracijske datoteke .env za vašu Sequelize postavku:

DB_NAME=hello_world_db
DB_USER=your_username
DB_PASSWORD=your_password
DB_HOST=localhost
DB_DIALECT=mysql

Svaka varijabla odgovara određenom parametru koji Sequelize koristi za uspostavljanje veze.

Osiguravanje .env datoteke

Ključno je da se vaša .env datoteka nikada ne pojavi u kontroli verzija. Da to spriječite, dodajte ga u svoju .gitignore datoteku kako bi ga Git preskočio prilikom commitanja promjena:

.env

U proizvodnim okruženjima, varijable okoline se obično konfiguriraju izravno u postavkama vaše hosting platforme ili u upravitelju tajni, umjesto u lokalnoj .env datoteci.

Ažuriranje vaše Sequelize konfiguracije

Sada kada ste definirali varijable okoline, ažurirajte svoju Sequelize inicijalizaciju da ih koristi. Za početak, uvezite i konfigurirajte dotenv na vrhu glavne datoteke vašeg servera (npr. server.js):

require('dotenv').config();
const Sequelize = require('sequelize');

const sequelize = new Sequelize(
  process.env.DB_NAME,
  process.env.DB_USER,
  process.env.DB_PASSWORD,
  {
    host: process.env.DB_HOST,
    dialect: process.env.DB_DIALECT
  }
);

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database:', error);
});
  1. Redak require('dotenv').config() učitava sve varijable definirane u .env datoteci u process.env.
  2. Sequelize čita varijable okoline prilikom inicijalizacije nove instance.
  3. Ovo osigurava da se u vašem kodu ne pojavljuju osjetljive informacije poput korisničkih imena ili lozinki.

Ovaj pristup povećava sigurnost i omogućuje različitim okruženjima korištenje jedinstvenih konfiguracija jednostavnom promjenom vrijednosti varijabli okruženja.

Provjera konfiguracije

Nakon što ažurirate svoju konfiguraciju, možete provjeriti vezu pokretanjem svoje Node.js aplikacije:

node server.js

Ako je vaša .env datoteka ispravno postavljena i MySQL server radi, vidjet ćete sljedeći ispis:

Connection has been established successfully.

Ako se pojavi pogreška, dvaput provjerite da:

  • Podaci za prijavu u vašoj .env datoteci odgovaraju onima u vašoj MySQL instanci.
  • MySQL poslužitelj radi i dostupan je s vašeg računala.
  • Datoteka .env nalazi se u korijenu vašeg projekta i pravilno je formatirana.

Najbolje prakse za sigurnu konfiguraciju

Korištenje varijabli okoline je snažan prvi korak prema sigurnom upravljanju konfiguracijom. Kako biste osigurali stalnu sigurnost i pouzdanost, razmotrite sljedeće dodatne najbolje prakse:

  • Koristite odvojene konfiguracije za svako okruženje: Održavajte različite .env datoteke za development, staging i production. Možete ih nazvati .env.development, .env.staging i .env.production.
  • Iskoristite upravitelje tajni: Za proizvodna okruženja koristite upravitelje tajni umjesto pohranjivanja tajni u datotekama.
  • Provjerite varijable okoline: Koristite alat za konfiguraciju ili skriptu za pokretanje koja provjerava jesu li potrebne varijable definirane prije nego što aplikacija počne raditi. Ovo pomaže u sprječavanju pogrešaka u izvođenju.
  • Redovito mijenjajte vjerodajnice: Povremeno ažurirajte vjerodajnice baze podataka kako biste smanjili posljedice mogućih propuštanja.
  • Ograničite pristup bazi podataka: Konfigurirajte korisničke uloge baze podataka tako da imaju najmanje potrebna dopuštenja za aplikaciju.

Implementiranjem varijabli okoline i pridržavanjem ovih praksi, osiguravate da vaša Sequelize aplikacija održava i fleksibilnost i sigurnost u razvojnim i produkcijskim okruženjima.

Najbolje prakse za migraciju i sinkronizaciju

Prilikom evolucije strukture baze podataka, imate dva primarna pristupa s Sequelizeom: pisanje i izvođenje eksplicitnih migracija ili korištenje sequelize.sync() kako bi Sequelize automatski generirao promjene u shemi. Oba pristupa su valjana, ali služe različitim svrhama i imaju svoje kompromise. Razumimo kada koristiti koji, kako pisati sigurne migracije i kako ih pouzdano izvršavati u razvoju i produkciji.

Migracije protiv sequelize.sync() — kada koristiti što

  • Migracije (preporučeno za produkciju): Migracije su eksplicitne, verzionirane promjene u vašoj shemi. Spremaju se u sustavu za kontrolu verzija, pregledavaju putem koda i izvršavaju kao dio procesa implementacije. Koristite migracije u produkciji za bilo kakvu promjenu sheme koja utječe na integritet ili dostupnost podataka.

  • sequelize.sync() (koristiti samo za razvoj i prototipe): sync() je praktičan za lokalni razvoj, testove i brze prototipe jer može automatski kreirati ili prilagoditi tablice. Međutim, nemojte koristiti sync({ force: true }) ili sync({ alter: true }) u produkciji: ove opcije mogu izbrisati podatke ili napraviti nesigurne promjene bez kontroliranog puta za poništavanje.

Alati: sequelize-cli i alati za izvođenje migracija

Službeni CLI, sequelize-cli, je najjednostavniji način za upravljanje migracijama s Sequelizeom. Za timove kojima je potrebna veća kontrola ili prilagođeni tijek rada, umzug je popularna alternativa.

Za instalaciju i inicijalizaciju CLI-ja u vašem projektu, pokrenite:

npm install --save-dev sequelize-cli

npx sequelize-cli init

Ovo stvara sljedeće mape:

config/        # Stores database configuration files
models/        # Stores your Sequelize model definitions
migrations/    # Stores migration files
seeders/       # Stores seeder files for inserting initial data

Trebali biste dodati ove mape u upravljanje verzijama (osim datoteka koje sadrže vjerodajnice). To osigurava da svako okruženje dosljedno izvršava iste promjene u bazi podataka.

Kreiranje i izvođenje migracija

Za stvaranje nove datoteke migracije, upotrijebite sljedeću naredbu:

npx sequelize-cli migration:generate --name add-isbn-to-books

Ovo stvara datoteku s vremenskim oznakama u mapi migrations/. Otvorite datoteku i definirajte što bi se trebalo dogoditi kada primijenite migraciju (up) i kako je vratiti (down):

module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.addColumn('books', 'isbn', {
      type: Sequelize.STRING
    });
  },
  async down(queryInterface) {
    await queryInterface.removeColumn('books', 'isbn');
  }
};
  • Funkcija up primjenjuje migraciju (dodaje stupac).
  • Funkcija down poništava promjenu (uklanja stupac).

Pokrenite migraciju koristeći:

npx sequelize-cli db:migrate

Za poništavanje posljednje migracije, upotrijebite:

npx sequelize-cli db:migrate:undo

Također možete poništiti sve migracije u razvoju pokretanjem:

npx sequelize-cli db:migrate:undo:all

Pisanje sigurnih, idempotentnih migracija

Slijedite ova pravila prilikom izrade migracija:

  • Omotajte operacije u transakcije kada ih baza podataka podržava. To osigurava da migracija ili potpuno uspije ili se potpuno poništi u slučaju greške.

    module.exports = {
      async up(queryInterface, Sequelize) {
        return queryInterface.sequelize.transaction(async (t) => {
          await queryInterface.addColumn('books', 'isbn', {
            type: Sequelize.STRING
          }, { transaction: t });
        });
      },
      async down(queryInterface) {
        return queryInterface.sequelize.transaction(async (t) => {
          await queryInterface.removeColumn('books', 'isbn', { transaction: t });
        });
      }
    };
  • Držite migracije malima i fokusiranima. Svaka migracija trebala bi izvršiti jednu logičku promjenu (dodavanje stupca, stvaranje indeksa, preimenovanje stupca). Manje migracije lakše je pregledati, testirati i poništiti.

  • Budite defanzivni i idempotentni. Kad je prikladno, provjerite postojanje kako biste izbjegli neuspjehe u okruženjima s odstupanjem. Koristite queryInterface.describeTable() ili raw upite kako biste zaštitili operacije.

  • Izbjegavajte destruktivne operacije u jednom koraku. Na primjer, za sigurno uklanjanje stupca, dodajte novi stupac, popunite ga podacima, preusmjerite čitanja na novi stupac u kodu aplikacije, i tek tada uklonite stari stupac u kasnijoj migraciji.

Migracije dugog trajanja i bez zastoja

Velike tablice i operacije nad indeksima mogu uzrokovati zaključavanja. Upotrijebite ove strategije kako biste smanjili njihov utjecaj:

  1. Prvo dodajte stupce koji mogu imati vrijednost null. Dodavanje stupca koji može imati vrijednost null obično je brzo. Izbjegavajte dodavanje stupaca s netrivijalnim zadanim vrijednostima koje uzrokuju prepisivanje cijele tablice.
  2. Popunjavajte u malim serijama. Implementirajte pozadinski zadatak za popunjavanje novih stupaca u dijelovima umjesto da izvršavate jedno ogromno UPDATE.
  3. Stvarajte indekse bez ekskluzivnih zaključavanja gdje je to moguće. Koristite značajke specifične za bazu podataka (na primjer, Postgres CONCURRENTLY i noviji MySQL online DDL) putem sirovog SQL-a u migracijama.
  4. Uvođenje oznake značajke. Implementirajte kod aplikacije koji čita novi stupac iza oznake značajke. Nakon što zatrpavanje završi i testovi prođu, okrenite zastavicu i kasnije uklonite staro polje.

Povratci i hitna popravka

  • Osigurajte down migraciju. Svaki up trebao bi imati odgovarajući down koji dokumentira kako vratiti promjenu. Čak i ako rijetko pokrećete down u produkciji, on služi kao jasan plan za povratak.
  • Planirajte hitne povrate promjena. Ako migracija uzrokuje probleme, budite spremni prvo vratiti aplikacijski kod i po potrebi obnoviti bazu podataka iz sigurnosnih kopija. Redovito testirajte postupke obnove.

Inicijalizacija podataka i promjene sheme

  • Držite broj seedera minimalnim i stabilnim. Koristite seedere za važne redove za pretraživanje ili konfiguraciju. Izbjegavajte unošenje velike količine poslovnih podataka kroz migracije.
  • Odvojite sjemenke od strukturnih migracija. Ako morate popuniti podatke koji ovise o promjeni sheme, bolje je koristiti naknadni seeder kako bi odgovornosti ostale jasne.

CI, pregled koda i automatizacija implementacije

  • Pokrenite migracije prvo u CI i staging okruženju. Dodajte CI korak koji pokreće migracije na privremenoj bazi podataka kako biste rano otkrili pogreške.

  • Postupajte s migracijama kao s kodom. Provjeravajte i pregledavajte datoteke migracija u pull zahtjevima. Uključite automatizirane provjere gdje je moguće.

  • Automatizirajte izvođenje migracija tijekom implementacije. Osigurajte da migracije budu izvršene prije nego što aplikacijski procesi počnu prihvaćati promet. Primjer package.json skripti:

    "scripts": {
      "migrate": "npx sequelize-cli db:migrate",
      "start": "node server.js",
      "deploy": "npm run migrate && npm run start"
    }

U kontejneriziranim implementacijama pokrenite migracije kao init kontejner ili u deploy hook-u tako da web procesi počnu tek nakon što migracije završe.

Testiranje migracija

  • Testirajte oba smjera. Pokrenite db:migrate, a zatim db:migrate:undo:all lokalno i u CI kako biste provjerili da up i down obje rade.
  • Koristite privremene baze podataka za testove. Preferirajte ephemere baze podataka ili kontejnere za determinističko izvođenje testova.

Slijedeći ove najbolje prakse osigurava da vaši Sequelize migracije ostanu pouzdani, pratljivi i sigurni za dugoročne projekte.

Rješavanje problema i savjeti za poboljšanje performansi

Čak i s dobro postavljenim sustavom, Sequelize aplikacije ponekad mogu naići na neočekivane probleme poput pogrešaka u povezivanju s bazom podataka, neuspjeha migracije, sporih upita, mrtvih zaključavanja ili sukoba u konkurenciji. Ovaj odjeljak pruža korak-po-korak upute za prepoznavanje i rješavanje tih problema, zajedno s najboljim praksama za performanse koje pomažu u održavanju stabilnosti i učinkovitosti vaše aplikacije.

Rješavanje uobičajenih problema

1. Pogreške povezivanja

Problem: Sequelize se ne može povezati s MySQL bazom podataka i ispisuje pogreške poput SequelizeConnectionError: connect ECONNREFUSED, Access denied for user ili SequelizeHostNotFoundError.

Kako popraviti:

Počnite tako da potvrdite da su vaši podaci za povezivanje ispravni. Dvaput provjerite vrijednosti u svojoj .env datoteci, uključujući ime baze podataka, korisničko ime, lozinku, host i port. Čak i mala pogreška u kucanju ili dodatni razmak može uzrokovati da Sequelize ne uspije uspostaviti vezu.

Zatim provjerite radi li vaš MySQL server i sluša li na zadanoj priključnici (3306). Povezanost možete testirati ručnim prijavljivanjem:

mysql -u your_username -p -h your_host -P 3306

Ako ova naredba zakaže, problem vjerojatno leži u vašoj MySQL usluzi ili vjerodajnicama.

Ako koristite paket dotenv, provjerite da se require('dotenv').config() pojavljuje prije nego što se inicijalizira Sequelize. Ovaj korak osigurava da su sve varijable okoline dostupne tijekom izvođenja.

Na kraju, za udaljene veze, provjerite postavke vašeg vatrozida ili pravila sigurnosne grupe. MySQL server mora prihvaćati dolazne veze s domaćina vaše aplikacije.

2. Neuspjesi migracije

Problem: Pokrenete npx sequelize-cli db:migrate ili sequelize.sync() i naiđete na pogreške poput nedostajućih tablica, dupliciranih stupaca ili nesukladnosti sheme.

Kako popraviti:

Problemi s migracijom obično nastaju kada se datoteke izvršavaju u pogrešnom redoslijedu ili sadrže pogreške u SQL operacijama. Sequelize obrađuje migracije na temelju vremenskih oznaka u nazivima datoteka, stoga provjerite jesu li datoteke pravilno imenovane.

Ako migracija ne uspije, detaljno pregledajte poruku o pogrešci. Zatim, sigurno vratite posljednju migraciju koristeći:

npx sequelize-cli db:migrate:undo

Nakon što popravite datoteku migracije, ponovno je pokrenite.

Uvijek omotajte svoju migracijsku logiku unutar transakcije kako biste osigurali atomarnost: ili sve promjene uspiju ili nijedna. Prije pokretanja migracija u produkciji, temeljito ih testirajte na testnoj ili lokalnoj bazi podataka kako biste potvrdili ispravnost.

3. Problemi s sinkronizacijom modela

Problem: Vaši Sequelize modeli i shema baze podataka ne podudaraju se, što dovodi do nedostajućih stupaca, pogrešnih tipova podataka ili zastarjelih tablica.

Kako popraviti:

Izbjegavajte korištenje sequelize.sync({ force: true }) u produkciji, jer će to izbrisati i ponovno stvoriti tablice, uzrokujući nepovratni gubitak podataka. Slično tome, koristite sequelize.sync({ alter: true }) oprezno; može nepredvidivo mijenjati sheme.

Umjesto toga, radije koristite eksplicitne migracije koje jasno definiraju svaku promjenu sheme. Za usporedbu svojih modela s stvarnom strukturom baze podataka, koristite:

await queryInterface.describeTable('tableName');

Ovo pomaže u otkrivanju neslaganja prije nego što utječu na proizvodnju.

4. Pogreške upita i isteka vremena

Problem: Vidite pogreške vezane uz isteka vremena poput Connection timeout ili Too many connections.

Kako popraviti:

Problemi s istekom vremena često su uzrokovani iscrpljenim bazenima veza ili nezatvorenim vezama. Osigurajte da su svi Sequelize upiti ispravno awaitani kako bi veze nakon korištenja bile vraćene u bazen.

Također možete fino podesiti konfiguraciju Sequelizeovog poola kako bi bolje odgovarala opterećenju vaše aplikacije:

const sequelize = new Sequelize(DB_NAME, DB_USER, DB_PASSWORD, {
  host: DB_HOST,
  dialect: 'mysql',
  pool: {
    max: 10,
    min: 0,
    acquire: 30000,
    idle: 10000
  }
});

Ako vaša aplikacija podržava mnoge istovremene korisnike, razmislite o povećanju MySQL max_connections parametra u konfiguraciji baze podataka. Pratite aktivne veze pomoću SHOW PROCESSLIST; u MySQL-u kako biste otkrili curenja ili neiskorištene sesije.

5. Spora izvedba upita

Problem: Određeni upiti traju znatno duže nego što se očekivalo, što uzrokuje spore API odgovore ili visoku latenciju.

Kako dijagnosticirati:

Započnite tako da identificirate koje su upite spori. Sve upite koje Sequelize izvršava možete zapisivati omogućavanjem zapisivanja privremeno:

const sequelize = new Sequelize(..., { logging: console.log });

Zatim upotrijebite MySQL-ovu EXPLAIN naredbu kako biste shvatili kako baza podataka izvršava upit:

EXPLAIN SELECT * FROM users WHERE email = 'user@example.com';

Ako rezultat prikazuje 'punu pretragu tablice', upitu nedostaje indeks.

Također možete omogućiti MySQL log sporih upita kako biste automatski bilježili spore operacije:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

Kako popraviti:

Dodajte indekse na stupce koji se često upitaju (osobito na one koji se koriste u WHERE, JOIN i ORDER BY klauzulama). Izbjegavajte SELECT *, umjesto toga, eksplicitno dohvaćajte samo polja koja su vam potrebna koristeći opciju attributes.

Ako Sequelize generira neučinkovite upite s nepotrebnim spajanjima, prepišite upit koristeći sirovi SQL. Za velike izvještaje ili analitičke upite, razbijte ih u manje korake ili koristite alate za keširanje poput Redis za pohranu ponovljenih rezultata.

6. Zastoji i pogreške u konkurentnosti

Problem: Primate pogreške poput Deadlock found when trying to get lock; try restarting transaction, osobito pri velikom opterećenju.

Kako dijagnosticirati:

Provjerite status InnoDB-a u MySQL-u kako biste identificirali koji su upiti ili tablice bili uključeni:

SHOW ENGINE INNODB STATUS\G

Ovaj izlaz uključuje detaljne informacije o nedavnim zastoju i čekanjima na zaključavanje. Također možete omogućiti opće ili transakcijske zapise kako biste pregledali redoslijed upita koji je doveo do problema.

Kako popraviti:

Zastoji nastaju kada dvije ili više transakcija čekaju na zaključavanja jedna druge. Kako ih smanjiti:

  1. Držite transakcije što kraćima. Ne pozivate vanjske usluge niti obavljajte teške izračune dok držite zaključavanje.

  2. Uvijek pristupajte tablicama i redovima istim redoslijedom u svim transakcijama kako biste izbjegli kružno čekanje.

  3. Za ponavljajuće zastoje, implementirajte jednostavan mehanizam ponovnog pokušaja s eksponencijalnim odgodama:

    async function runWithRetry(fn, retries = 3) {
      for (let i = 0; i < retries; i++) {
        try {
          return await fn();
        } catch (err) {
          if (isDeadlockError(err) && i < retries - 1) {
            await sleep(100 * (i + 1));
            continue;
          }
          throw err;
        }
      }
    }
  4. Koristite zaključavanje na razini retka (SELECT ... FOR UPDATE) samo kada je potrebno i odmah ih oslobodite.

  5. Podijelite velike transakcije na manje serije kako biste smanjili sukobe.

  6. Prilagodite razine izolacije transakcija ako je to prikladno, ali tek nakon što potvrdite da to neće utjecati na dosljednost podataka.

Savjeti za optimizaciju performansi

Problemi s performansama često proizlaze iz neučinkovitih upita, nedostajućih indeksa ili lošeg upravljanja vezama. Evo nekoliko praktičnih savjeta kako poboljšati performanse Sequelize-a bez žrtvovanja jasnoće ili održivosti.

1. Optimizirajte upite

Pišite upite koji traže samo ono što je potrebno. Dohvaćanje nepotrebnih podataka povećava upotrebu memorije i usporava odgovore. Koristite attributes za ograničavanje polja:

const users = await User.findAll({ attributes: ['id', 'name'] });

Uvijek filtrirajte rezultate koristeći indeksirane stupce i izbjegavajte široka pretraživanja poput LIKE '%term%' kad god je to moguće.

2. Učinkovito korištenje indeksa

Indeksi ubrzavaju pretraživanje, ali mogu usporiti zapisivanje. Dodajte indekse samo tamo gdje pružaju mjerljive koristi. Analizirajte izvedbu upita pomoću MySQL-ovog EXPLAIN kako biste provjerili poboljšanja prije i nakon dodavanja indeksa:

await queryInterface.addIndex('users', ['email']);

3. Prilagodite bazen veza

Pool veza određuje koliko veza s bazom podataka Sequelize održava.

  • Za male aplikacije, obično je dovoljno 5–10 veza.
  • Za sustave s velikim opterećenjem, povećajte veličinu pool-a i prilagodite timeout-e kako biste spriječili zagušenja veza. Redovito pratite korištenje pomoću MySQL-ove naredbe SHOW PROCESSLIST;.

4. Omogući evidentiranje i nadzor

Privremeno uključite Sequelize zapisivanje kako biste pregledali SQL upite. Koristite MySQL zapis sporih upita kako biste identificirali upite koji premašuju vaš prag performansi. Za praćenje u stvarnom vremenu integrirajte alate za nadzor poput PM2, New Relic ili Datadog kako biste mjerili kašnjenje i korištenje resursa.

5. Keširanje čestih upita

Keširajte rezultate za podatke koji se ne mijenjaju često. Alati poput Redis ili Memcached smanjuju broj upita u bazu podataka i poboljšavaju vrijeme odgovora, osobito za radna opterećenja koja su intenzivna na čitanje.

6. Paginiraj rezultate

Veliki skupovi rezultata troše značajnu količinu memorije. Uvijek stranite upite u API-jima:

const users = await User.findAll({ limit: 50, offset: 100 });

Straniciranje održava odgovore laganima i dosljednima pod opterećenjem.

7. Koristite sirove upite za složene operacije

Ako Sequelizova apstrakcija rezultira neučinkovitim SQL-om za određeni slučaj, izvršite sirovi SQL upit izravno:

const [results] = await sequelize.query('SELECT name FROM users WHERE active = 1');

Koristite sirove upite štedljivo i jasno ih dokumentirajte za buduće održavanje.

Često postavljana pitanja

1. Što je Sequelize u Node.js?

Sequelize je objektno-relacijski mapper (ORM) za Node.js koji pruža visokorazinski, u JavaScriptu bazirani API za rad sa SQL bazama podataka poput MySQL, PostgreSQL, SQLite i MariaDB. Umjesto pisanja sirovog SQL-a za svaku operaciju, definirate modele koji se mapiraju na tablice u bazi podataka i koristite metode modela za umetanje, upite, ažuriranje i brisanje zapisa. Sequelize također podržava asocijacije (odnose između modela), transakcije, migracije i sirove upite kada su vam potrebni izravni SQL.

2. Kako povezati Sequelize s MySQL-om?

Da biste povezali Sequelize s MySQL-om, kreirate Sequelize instancu s vašim podacima za pristup bazi podataka i opcijama. U Node.js datoteci, uključite Sequelize, opcionalno učitajte varijable okoline, a zatim ga instancirajte s nazivom baze podataka, korisničkim imenom, lozinkom i opcijama za host/dialekt. Na primjer:

require('dotenv').config();
const { Sequelize } = require('sequelize');

const sequelize = new Sequelize(
  process.env.DB_NAME,
  process.env.DB_USER,
  process.env.DB_PASSWORD,
  {
    host: process.env.DB_HOST || 'localhost',
    dialect: 'mysql'
  }
);

await sequelize.authenticate();
console.log('Connected to the database successfully!');

Uvijek držite vjerodajnice izvan kontrole izvora (na primjer, u .env datoteci ili upravitelju tajni).

3. Kako kreirati i sinkronizirati modele u Sequelizeu?

U Sequelizeu, modeli predstavljaju tablice. Definirate ih koristeći sequelize.define() ili proširujući Model klasu. Nakon toga, možete ih sinkronizirati s vašom bazom podataka tako da Sequelize automatski kreira tablice (odlično za razvoj). Evo malog primjera:

const { DataTypes } = require('sequelize');

const Book = sequelize.define('Book', {
  title: { type: DataTypes.STRING, allowNull: false },
  author: { type: DataTypes.STRING, allowNull: false },
  releaseDate: DataTypes.DATEONLY
});

// Create the table if it does not exist
await sequelize.sync();

Za produkcijska okruženja bolje je koristiti migracije kako biste imali kontrolu verzija nad promjenama sheme.

4. Kako koristiti Sequelize s Expressom?

U Express aplikaciji, inicijalizirajte Sequelize u zasebnoj datoteci (npr. db.js), izvezite povezanu instancu i modele, te ih uvozite gdje vam zatrebaju u vašim rukovateljima ruta ili kontrolerima. Koristite async/await u rukovateljima ruta za pozivanje metoda Sequelize modela, te rukujte pogreškama pomoću try/catch ili middleware-a za pogreške. Evo kako to izgleda u praksi:

// db.js
const sequelize = new Sequelize(...);
const User = sequelize.define(...);
module.exports = { sequelize, User };

// app.js
const { User } = require('./db');
app.get('/users', async (req, res) => {
  const users = await User.findAll();
  res.json(users);
});

Pobrinite se da pokrenete migracije ili pozovete sequelize.sync() prije nego što pokrenete svoj Express server kako bi vaše baze podataka postojale kada stignu zahtjevi.

5. Koja je razlika između Sequelize i običnih SQL upita?

Sequelize vam omogućuje pisanje logike baze podataka u JavaScriptu, dok sirovi SQL znači pisanje SQL naredbi izravno. Sequelize čini vaš kod čišćim i lakšim za održavanje jer možete definirati modele i odnose jednom i koristiti ih u cijeloj aplikaciji. Sirovi SQL vam daje veću kontrolu i ponekad može biti brži za vrlo složene upite.

Ukratko, koristite Sequelize za većinu svakodnevnih zadataka, a sirovi SQL koristite kada trebate fino podešavanje performansi ili pokretanje nečega što Sequelize ne može lako izraziti.

6. Kako mogu definirati asocijacije u Sequelizeu?

Sequelize olakšava opisivanje odnosa između tablica. Možete postaviti odnose jedan na jedan, jedan na više ili više na više koristeći jednostavne metode poput hasOne, hasMany i belongsToMany. Na primjer:

// One to one
User.hasOne(Profile);
Profile.belongsTo(User);

// One to many
Author.hasMany(Book);
Book.belongsTo(Author);

// Many to many
Student.belongsToMany(Course, { through: 'StudentCourse' });
Course.belongsToMany(Student, { through: 'StudentCourse' });

Jednom kada je definirano, možete koristiti include u svojim upitima kako biste automatski dohvatili povezane podatke.

7. Kako da upravljam migracijama i promjenama sheme?

U fazi razvoja možete koristiti sequelize.sync() za brzo kreiranje tablica. Ali u produkciji, uvijek biste trebali koristiti migracije. Migracije su verzionirane datoteke koje opisuju kako se shema vaše baze podataka mijenja tijekom vremena, slično kao što Git commitovi prate promjene koda.

Možete stvoriti migraciju pomoću Sequelize CLI-ja:

npx sequelize-cli migration:generate --name add-users-table

Svaka migracija ima up i down metode, jednu za primjenu promjene i jednu za poništavanje. Pokrenite svoje migracije s:

npx sequelize-cli db:migrate

Na taj način, svi u vašem timu (i svaka implementacija) zadržavaju istu strukturu baze podataka.

Koristite sequelize.sync() za brze eksperimente, ali se držite migracija kada je vaša aplikacija aktivna jer su sigurnije, moguće ih je vratiti i lakše ih je pratiti.

Zaključak

U ovom vodiču postavili ste Sequelize s Node.js i MySQL-om za upravljanje podacima putem modela, poveznica i migracija. Također ste osigurali svoje podatke za pristup bazi pomoću varijabli okoline i istražili najbolje prakse za rješavanje problema i optimizaciju performansi.

S ovim koracima sada imate solidnu osnovu za izgradnju i održavanje sigurnih i učinkovitih Node.js aplikacija koje koriste Sequelize i MySQL za upravljanje podacima.

Za više informacija o Sequelizeu, pogledajte dokumentaciju proizvoda. Za više ovakvih Node.js vodiča, pogledajte sljedeće članke:

  • Kako postaviti GraphQL poslužitelj u Node.js s Apollo Serverom i Sequelizeom
  • Kako implementirati keširanje u Node.js koristeći Redis
  • Kako napraviti web poslužitelj u Node.js-u pomoću HTTP modula

Hvala što učite s Linux-Console.net zajednicom.