En aquest blog parlaré una mica sobre els ORM ‘s, els seus beneficis (pros) i limitacions (cons), que és Sequelize i com usar-lo.
Object-Relational Mapping
és una tècnica per convertir dades entre el sistema de tipus de el llenguatge de programació i la base de dades. Com el seu nom indica, això va dirigit només a les base de dades relacional (SQL). Això crea un efecte “objecte base de dades virtual” sobre la base de dades relacional, aquest efecte és el que ens permet manipular la base de dades a través del codi.
Object – Fa referència a el / els objecte (s) que podem usar en el nostre llenguatge.
Relational – Fa referència al nostre Sistema Gestor de Base de Dades (MySQL, MSSQL, PostgreSQL).
Mapping – Fa referència a la connexió entre el els objectes i les taules.
tl; dr ORM és una tècnica que ens permet fer queries i manipular dades de la base de dades des d’un llenguatge de programació .
Pros
- Abstracte: Disseny d’una estructura o model aïllat de la base de dades.
- Portable: Et permet transportar l’estructura del teu ORM a qualsevol DBMS.
- Implantació de dades: En cas que una taula tingui una o diverses relacions amb altres.
Cons
- Lent: Si es compara el temps de resposta entre un raw query i un query fet per objectes, raw query és molt més ràpid a causa de que no hi ha una capa (mapping).
- Complexitat: Algunes vegades necessitarem fer queries complexos, per sort Sequelize et permet executar raw queries.
Què és Sequelize?
Sequelize és un ORM basat en promeses per Node.js. Suporta PostgreSQL, MySQL, SQLite i MSSQL, i lliurament característiques sòlides de transaccions, relacions entre taules, mecanismes de migracions i càrrega de dades, i més.
Perquè vaig decidir utilitzar Sequelize?
Sequelize fa anar els seus objectes com promeses, cosa que va de la mà amb el event loop de NODE.JS.
Ara els mostraré com crear i migrar taules, carregar dades, i com consultar aquestes dades. Si volen checar el codi, poden clonar des d’aquí.
Requeriments
- NODE.JS 8.0 +
- MySQL 5.7 +
Primer instal·larem globalment el mòdul sequelize-cli:
NPM i -g sequelize-cli
Després crearem una carpeta on contingui la nostra aplicació, crearem un arxiu js i instal·larem sequelize i el seu dialecte (en aquest cas MySQL):
mkdir howto-sequelize & & cd howto-sequelizetouch index.jsnpm init -ynpm i -S sequelize mysql2
Ara hem d’iniciar el projecte amb sequelize-cli:
sequelize init
sequelize-cli ens va crear una estructura base en l’arrel del nostre projecte:
Si revisem l’arxiu ./config/config.json, veiem que tenim 3 opcions de connexió a una base de dades, modifiquem l’opció” development “:
"development": { "username": "root", "password": "tevacontrasenya", "database": "howto-sequelize", "host": "127.0.0.1", "dialect": "mysql" , "operatorsAliases": false, "dialectOptions": { "charset": "utf8mb4"}, "logging": true, "benchmark": true}
Ara revisem l’arxiu ./models/index. js. Aquest fitxer té com a funció crear una nova instància de Sequelize cada vegada que sigui cridat, i té com a variable d’entorn default a “development”, la qual utilitzarà la base de dades, host, usuari, contrasenya i opcions que acabem d’afegir.
Creiem nostra base de dades amb la següent comanda:
sequelize db: create
Molt bé! Ara comencem a crear els nostres models:
sequelize model: generate --name Usuari --attributes nom: string, apellidoP: string, apellidoM: string, email: string sequelize model: generate - -name LenguajeP --attributes nom: string sequelize model: generate --name Usuario_LenguajeP --attributesUsuarioId: integer, LenguajePId: integer
Després de crear els nostres models cal fer la relació entre usuaris i llenguatges.
./ models / usuario.js
'usi strict'; module.exports = (sequelize, datatypes) = > {var Usuari = s'assequi lize.define ( 'Usuari', {nom: DataTypes.STRING, apellidoP: DataTypes.STRING, apellidoM: DataTypes.STRING, email: DataTypes.STRING}, {}); Usuario.associate = function (models) {// associations can be defined here Usuario.belongsToMany (models.LenguajeP, {through: 'Usuario_LenguajeP', es: 'lenguajesProgramacion', foreignKey: 'UsuarioId',})}; return Usuari;};
analitzem això, veiem que el nostre model Usuari executa una funció belongsToMany la qual apunta el model LenguajeP, en poques paraules estem indicant que un usuari pot pertànyer a diversos llenguatges. A les opcions, through indica la taula per la qual ha de creuar per trobar aquestes relacions, es, és opcional, és el nom de la propietat o key que ens lliurarà aquestes relacions, foreignKey marca perquè columna volem que busqui aquestes relacions.
./ models / lenguajep.js
'usi strict'; module.exports = (sequelize, datatypes) = > {var LenguajeP = sequelize.define ( 'LenguajeP', {nom: DataTypes.STRING}, {}); LenguajeP.associate = function (models) {// associations can be defined here LenguajeP.belongsToMany (models.Usuario, {through: 'Usuario_LenguajeP', es: 'usuaris', foreignKey: 'LenguajePId',})}; return LenguajeP;};
Fem el mateix amb els llenguatges (LenguajeP), però ara apuntant a el model Usuari.
Recordem una mica el que platicamos dalt, ORM treballa sobre una capa que és el mapping (mapatge), aquestes relacions només es veuran efectuades en el projecte, ens falta crear una migració que afecti la base de dades. Hi ORM ‘s que revisen si has fet canvis als teus models i creen noves migracions a partir d’aquests canvis (Django ORM, Peewee), en el nostre cas Sequelize no compta amb això, així que nosaltres crearem les nostres migracions:
sequelize migration: generate --name relation-many-to-many
Això ens va generar un arxiu nou amb un esquelet a les nostres migracions, ara hem de modificar-lo:
'usi strict'; module.exports = {up: (queryInterface, Sequelize) = > {return, {type: 'FOREIGN KEY', name: 'FK_UsuarioLenguajeP_Usuario_1', references: {table: 'Usuari' , field: 'id',}, onDelete: 'no action', onUpdate: 'no action',}), queryInterface.addConstraint ( 'Usuario_LenguajeP',, {type: 'FOREIGN KEY', name: 'FK_UsuarioLenguajeP_LenguajeP_1', references : {table: 'LenguajeP', field: 'id',}, onDelete: 'no action', onUpdate: 'no action',}),]}, down: (queryInterface, Sequelize) = > {return}};
Aquest fitxer exporta un objecte amb 2 propietats, up i down. La propietat up està encarregada de lliurar una promesa que alteri les dades (crear taules, relacions, camps, canviar tipus, etc), i la propietat down fa el contrari, reverteix els canvis que s’hagin efectuat en up.
Molt bé, ara toca la meva part favorita, cal córrer els scripts de migració amb la següent comanda:
sequelize db: migrate
BOOM! Però què va passar?!?! Si llegim amb atenció l’error en la consola diu que no troba la taula Usuario_LenguajeP a la base de dades, revisem les taules. Hi ha una cosa curiosa, totes acaben amb una “s”, això és perquè sequelize-cli maneja les taules en plural per default, tot i que en les nostres opcions tinguem freezeTableName: true, aquest cas el poden veure aquí.
llavors només ens falta canviar el nom de les taules a plural en relation-many-to-many (Usuario_LenguajePs, Usuaris, LenguajePs).
sequelize db: migrate: ón: allsequelize db: migrate
les migracions van ser reeixides! Ara hem de poblar aquestes taules, sequelize-cli utilitza les seeds (llavors). Crearem 3 arxius seed:
sequelize seed: generate --name usuariosequelize seed: generate --name lenguajepsequelize seed: generate --name usuario_lenguajep
Aquestes llavors estan ubicades a la carpeta ./seeders, cal posar les dades que desitgem carregar a la base de dades.
Semi lla usuari:
'usi strict'; module.exports = {up: (queryInterface, Sequelize) = > {return queryInterface.bulkInsert ( 'Usuaris',, {}); }, Down: (queryInterface, Sequelize) = > {return queryInterface.bulkDelete ( 'Usuaris', null, {}); }};
Llavor lenguajep:
'usi strict'; module.exports = {up: (queryInterface, Sequelize) = > {return queryInterface.bulkInsert ( 'LenguajePs',, {}); }, Down: (queryInterface, Sequelize) = > {return queryInterface.bulkDelete ( 'LenguajePs', null, {}); }};
Llavor usuario_lenguajep:
'usi strict'; module.exports = {up: (queryInterface, Sequelize) = > {return queryInterface.bulkInsert ( 'Usuario_LenguajePs',, {}); }, Down: (queryInterface, Sequelize) = > {return queryInterface.bulkDelete ( 'Usuario_LenguajePs', null, {}); }};
Ara cal carregar aquestes dades a la base de dades amb la següent comanda:
sequelize db: seed: all
Molt bé! Ja vam veure com inicialitzar un projecte amb sequelize, crear models, assignar relacions, crear migracions, córrer i desfer migracions, crear llavors i carregar-les a la base de dades, només ens falta fer queries. Anem al nostre index.js en arrel i escrivim la següent funció:
const db = requereix ( './ models'); const main = async () = > {try {const usuaris = await db.Usuario.findAll ({include:}, through: {attributes:},}],}); console.log (JSON.stringify (usuaris)); process.exit (); } Catch (error) {console.log (error); }}; main ();
El guardem i executem el programa (ja sigui per consola o amb l’eina de debug de visual studio code) i hauríem de rebre un JSON amb una llista d’usuaris (en aquest exemple només n’hi ha un ) amb les seves dades i llenguatges:
}]
Vam aprendre sequelize-cli i el bàsic de sequelize, ara podem crear un API que executi accions CRUD perquè els nostres clients la consumeixin!