Mapeo relacional de objetos para fibjs
Instalar
1npm install fib-orm
Prueba
1npm run ci
Soporte de SGBD
- MySQL y MariaDB
- SQLite
Características
fib-orm agrega un conjunto de métodos de versión síncrona en el objeto node-orm.
Introducción
Este es un módulo de mapeo relacional de objetos fibjs.
Un ejemplo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37var orm = require("fib-orm");
var db = orm.connectSync("mysql://username:password@host/database");
var Person = db.define("person", {
name : String,
surname : String,
age : Number, // FLOAT
male : Boolean,
continent : [ "Europe", "America", "Asia", "Africa", "Australia", "Antartica" ], // ENUM type
photo : Buffer, // BLOB/BINARY
data : Object // JSON encoded
}, {
methods: {
fullName: function () {
return this.name + ' ' + this.surname;
}
},
validations: {
age: orm.enforce.ranges.number(18, undefined, "under-age")
}
});
// add the table to the database
db.syncSync();
// add a row to the person table
Person.createSync({ id: 1, name: "John", surname: "Doe", age: 27 });
// query the person table by surname
var people = Person.findSync({ surname: "Doe" });
// SQL: "SELECT * FROM person WHERE surname = 'Doe'"
console.log("People found: %d", people.length);
console.log("First person: %s, age %d", people[0].fullName(), people[0].age);
people[0].age = 16;
people[0].saveSync();
La versión de node.js es así:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49var orm = require("orm");
orm.connect("mysql://username:password@host/database", function (err, db) {
if (err) throw err;
var Person = db.define("person", {
name : String,
surname : String,
age : Number, // FLOAT
male : Boolean,
continent : [ "Europe", "America", "Asia", "Africa", "Australia", "Antartica" ], // ENUM type
photo : Buffer, // BLOB/BINARY
data : Object // JSON encoded
}, {
methods: {
fullName: function () {
return this.name + ' ' + this.surname;
}
},
validations: {
age: orm.enforce.ranges.number(18, undefined, "under-age")
}
});
// add the table to the database
db.sync(function(err) {
if (err) throw err;
// add a row to the person table
Person.create({ id: 1, name: "John", surname: "Doe", age: 27 }, function(err) {
if (err) throw err;
// query the person table by surname
Person.find({ surname: "Doe" }, function (err, people) {
// SQL: "SELECT * FROM person WHERE surname = 'Doe'"
if (err) throw err;
console.log("People found: %d", people.length);
console.log("First person: %s, age %d", people[0].fullName(), people[0].age);
people[0].age = 16;
people[0].save(function (err) {
// err.msg = "under-age";
});
});
});
});
});
Documentación
Fibjs no agregó nuevas funciones, el desarrollo de documentos puede referirse a node-orm, solo es necesario cambiar la llamada asincrónica a la versión sincrónica wiki .
Ajustes
Ver información en la wiki .
Conectando
Ver información en la wiki .
Modelos
Un modelo es una abstracción de una o más tablas de bases de datos. Los modelos admiten asociaciones (más información a continuación). Se supone que el nombre del modelo coincide con el nombre de la tabla.
Los modelos admiten comportamientos para acceder y manipular datos de tablas.
Definición de modelos
Ver información en la wiki .
Properties
Ver información en la wiki .
Instance Methods
Se pasan durante la definición del modelo.
1
2
3
4
5
6
7
8
9
10
11
12
13var Person = db.define('person', {
name : String,
surname : String
}, {
methods: {
fullName: function () {
return this.name + ' ' + this.surname;
}
}
});
var person = Person.getSync(4);
console.log( person.fullName() );
Model Methods
Se definen directamente en el modelo.
1
2
3
4
5
6
7
8
9var Person = db.define('person', {
name : String,
height : { type: 'integer' }
});
Person.tallerThan = function(height) {
return this.findSync({ height: orm.gt(height) });
};
var tallPeople = Person.tallerThan( 192);
Cargando modelos [NO SOPORTE]
Los modelos pueden estar en módulos separados. Simplemente asegúrese de que el módulo que contiene los modelos utilice module.exports para publicar una función que acepte la conexión de la base de datos, luego cargue sus modelos como desee.
Nota: al utilizar esta técnica, puede tener cargas en cascada.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// your main file (after connecting)
db.loadSync("./models");
// loaded!
var Person = db.models.person;
var Pet = db.models.pet;
// models.js
module.exports = function (db) {
db.loadSync("./models-extra");
db.define('person', {
name : String
});
};
// models-extra.js
module.exports = function (db) {
db.define('pet', {
name : String
});
};
Sincronización de modelos
Ver información en la wiki .
Dejar caer modelos
Ver información en la wiki .
Opciones avanzadas
ORM2 le permite algunos ajustes avanzados en las definiciones de su modelo. Puede configurarlos a través de la configuración o en la llamada define
cuando configura el modelo.
Por ejemplo, cada instancia del modelo tiene un ID único en la base de datos. Esta columna de la tabla se agrega automáticamente y se llama "id" de forma predeterminada.
Si define su propia key: true
columna, no se agregará "id":
1
2
3
4
5
6
7
8
9
10
11
12var Person = db.define("person", {
personId : { type: 'serial', key: true },
name : String
});
// You can also change the default "id" property name globally:
db.settings.set("properties.primary_key", "UID");
// ..and then define your Models
var Pet = db.define("pet", {
name : String
});
El modelo de mascota tendrá 2 columnas, una UID
y una name
.
También es posible tener claves compuestas:
1
2
3
4var Person = db.define("person", {
firstname : { type: 'text', key: true },
lastname : { type: 'text', key: true }
});
Otras opciones:
identityCache
: (predeterminado:false
) Configúrelotrue
para habilitar el caché de identidad ( Singletons ) o establezca un valor de tiempo de espera (en segundos);autoSave
: (predeterminado:false
) Configúrelo paratrue
guardar una instancia justo después de cambiar cualquier propiedad;autoFetch
: (predeterminado:false
) Configúrelotrue
para recuperar asociaciones al recuperar una instancia de la base de datos;autoFetchLimit
: (predeterminado:1
) SiautoFetch
está habilitado, esto define cuántos aros (asociaciones de asociaciones) desea que recupere automáticamente.
Manos
Ver información en la wiki .
Encontrar artículos
Model.getSync(id, [ options ])
Para obtener un elemento específico de la base de datos utilice Model.get
.
1
2var person = Person.getSync(123);
// finds person with id = 123
Model.findSync([ conditions ] [, options ] [, limit ] [, order ])
Encontrar uno o más elementos tiene más opciones, cada una se puede dar sin un orden de parámetros específico, solo options
tiene que ser después conditions
(incluso si es un objeto vacío).
1
2var people = Person.findSync({ name: "John", surname: "Doe" }, 3);
// finds people with name='John' AND surname='Doe' and returns the first 3
Si necesita ordenar los resultados porque los está limitando o simplemente porque quiere ordenarlos, haga lo siguiente:
1
2
3
4
5
6var people = Person.findSync({ surname: "Doe" }, "name");
// finds people with surname='Doe' and returns sorted by name ascending
people = Person.findSync({ surname: "Doe" }, [ "name", "Z" ]);
// finds people with surname='Doe' and returns sorted by name descending
// ('Z' means DESC; 'A' means ASC - default)
Hay más opciones que puedes pasar para encontrar algo. Estas opciones se pasan en un segundo objeto:
1
2var people = Person.findSync({ surname: "Doe" }, { offset: 2 });
// finds people with surname='Doe', skips the first 2 and returns the others
También puede utilizar SQL sin formato al realizar búsquedas, como se documenta en la sección Encadenamiento a continuación.
Model.countSync([ conditions])
Si solo desea contar la cantidad de elementos que coinciden con una condición, puede usarlos .count()
en lugar de encontrarlos todos y contarlos. Esto en realidad le indicará al servidor de la base de datos que haga un recuento (no se hará en el proceso del nodo en sí). ).
1
2var count = Person.countSync({ surname: "Doe" });
console.log("We have %d Does in our db", count);
Model.existsSync([ conditions])
De manera similar a .count()
, este método simplemente verifica si el recuento es mayor que cero o no.
1
2var exists = Person.existsSync({ surname: "Doe" });
console.log("We %s Does in our db", exists ? "have" : "don't have");
Aggregating Functions
Array
Se puede pasar un de propiedades para seleccionar solo unas pocas propiedades. Object
También se acepta un para definir condiciones.
Aquí hay un ejemplo para ilustrar cómo usarlo .groupBy()
:
1
2
3//The same as "select avg(weight), age from person where country='someCountry' group by age;"
var stats = Person.aggregate(["age"], { country: "someCountry" }).avg("weight").groupBy("age").getSync();
// stats is an Array, each item should have 'age' and 'avg_weight'
.aggregate()
Métodos básicos
.limit()
: puede pasar un número como límite, o dos números como compensación y límite respectivamente.order()
: igual queModel.find().order()
.aggregate()
Métodos adicionales
min
max
avg
sum
count
(hay un atajo para esto -Model.count
)
Hay más funciones agregadas según el controlador (funciones matemáticas, por ejemplo).
Chaining
Si prefiere una sintaxis menos complicada, puede encadenar .find()
sin proporcionar un parámetro de devolución de llamada.
1
2
3var people = Person.find({ surname: "Doe" }).limit(3).offset(2).only("name", "surname").runSync();
// finds people with surname='Doe', skips first 2 and limits to 3 elements,
// returning only 'name' and 'surname' properties
Si desea omitir solo una o dos propiedades, puede llamar .omit()
en lugar de .only
.
El encadenamiento permite consultas más complicadas. Por ejemplo, podemos buscar especificando SQL personalizado:
1Person.find({ age: 18 }).where("LOWER(surname) LIKE ?", ['dea%']).allSync( ... );
Es una mala práctica escapar manualmente de los parámetros SQL, ya que es propenso a errores y expone su aplicación a la inyección SQL. La ?
sintaxis se encarga de escapar por usted, sustituyendo de forma segura el signo de interrogación en la consulta con los parámetros proporcionados. También puede encadenar varias where
cláusulas como necesario.
.find
, .where
& .all
hacen lo mismo; todos son intercambiables y encadenables.
También puedes order
o orderRaw
:
1
2
3Person.find({ age: 18 }).order('-name').allSync( ... );
// see the 'Raw queries' section below for more details
Person.find({ age: 18 }).orderRaw("?? DESC", ['age']).allSync( ... );
También puedes encadenar y simplemente obtener el recuento al final. En este caso, se ignoran el desplazamiento, el límite y el orden.
1
2var people = Person.find({ surname: "Doe" }).countSync();
// people = number of people with surname="Doe"
También está disponible la opción de eliminar los elementos seleccionados. Tenga en cuenta que una eliminación encadenada no ejecutará ningún gancho.
1
2Person.find({ surname: "Doe" }).removeSync();
// Does gone..
También puede realizar modificaciones en sus instancias utilizando métodos comunes de recorrido de matriz y guardar todo al final. [NO ES SOPORTE]
1
2
3
4
5
6
7
8
9
10
11
12
13Person.find({ surname: "Doe" }).each(function (person) {
person.surname = "Dean";
}).save(function (err) {
// done!
});
Person.find({ surname: "Doe" }).each().filter(function (person) {
return person.age >= 18;
}).sort(function (person1, person2) {
return person1.age < person2.age;
}).get(function (people) {
// get all people with at least 18 years, sorted by age
});
Por supuesto, puedes hacer esto directamente en .find()
, pero para algunas tareas más complicadas puede resultar muy útil.
Model.find()
no devuelve una matriz, por lo que no puede simplemente encadenar directamente. Para comenzar a encadenar debe llamar
.each()
(con una devolución de llamada opcional si desea recorrer la lista). Luego puede usar las funciones comunes
.filter()
y más de una vez..sort()
.forEach()
Al final (o durante el proceso...) puedes llamar a:
.countSync()
si sólo quieres saber cuántos artículos hay;.getSync()
para recuperar la lista;.saveSync()
para guardar todos los cambios de elementos.
Condiciones
Las condiciones se definen como un objeto donde cada clave es una propiedad (columna de la tabla). Se supone que todas las claves están concatenadas por el lógico AND
. Se considera que los valores coinciden exactamente, a menos que estés pasando un Array
. En este caso se considera una lista para comparar la propiedad.
1
2{ col1: 123, col2: "foo" } // `col1` = 123 AND `col2` = 'foo'
{ col1: [ 1, 3, 5 ] } // `col1` IN (1, 3, 5)
Si necesita otras comparaciones, debe usar un objeto especial creado por algunas funciones auxiliares. Aquí hay algunos ejemplos para describirlo:
1
2
3
4
5
6
7
8
9
10
11{ col1: orm.eq(123) } // `col1` = 123 (default)
{ col1: orm.ne(123) } // `col1` <> 123
{ col1: orm.gt(123) } // `col1` > 123
{ col1: orm.gte(123) } // `col1` >= 123
{ col1: orm.lt(123) } // `col1` < 123
{ col1: orm.lte(123) } // `col1` <= 123
{ col1: orm.between(123, 456) } // `col1` BETWEEN 123 AND 456
{ col1: orm.not_between(123, 456) } // `col1` NOT BETWEEN 123 AND 456
{ col1: orm.like(12 + "%") } // `col1` LIKE '12%'
{ col1: orm.not_like(12 + "%") } // `col1` NOT LIKE '12%'
{ col1: orm.not_in([1, 4, 8]) } // `col1` NOT IN (1, 4, 8)
Consultas sin procesar
1
2
3
4
5
6
7
8
9
10
11
12
13var data = db.driver.execQuerySync("SELECT id, email FROM user")
// You can escape identifiers and values.
// For identifier substitution use: ??
// For value substitution use: ?
var data = db.driver.execQuerySync(
"SELECT user.??, user.?? FROM user WHERE user.?? LIKE ? AND user.?? > ?",
['id', 'name', 'name', 'john', 'id', 55])
// Identifiers don't need to be scaped most of the time
var data = db.driver.execQuerySync(
"SELECT user.id, user.name FROM user WHERE user.name LIKE ? AND user.id > ?",
['john', 55])
Identity pattern
Puede utilizar el patrón de identidad (desactivado de forma predeterminada). Si está habilitado, varias consultas diferentes darán como resultado el mismo resultado: obtendrá el mismo objeto. Si tiene otros sistemas que pueden cambiar su base de datos o necesita llamar a algún manual Consultas SQL, no deberías usar esta característica. También se sabe que causa algunos problemas con relaciones complejas de búsqueda automática. Úsala bajo tu propio riesgo.
Se puede habilitar/deshabilitar por modelo:
1
2
3
4
5var Person = db.define('person', {
name : String
}, {
identityCache : true
});
y también a nivel mundial:
1
2var db = orm.connectSync('...');
db.settings.set('instance.identityCache', true);
La caché de identidad se puede configurar para que caduque después de un período de tiempo pasando un número en lugar de un valor booleano. El número se considerará el tiempo de espera de la caché en segundos (puede usar punto flotante).
Nota : una excepción al almacenamiento en caché es que no se usará si una instancia no se guarda. Por ejemplo, si busca una persona y luego la cambia, mientras no se guarde, no se pasará del caché.
Creando artículos
Model.createSync(items)
Para insertar nuevos elementos en la base de datos utilice Model.create
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var items = Person.createSync([
{
name: "John",
surname: "Doe",
age: 25,
male: true
},
{
name: "Liza",
surname: "Kollan",
age: 19,
male: false
}
]);
// items - array of inserted items
Actualizando elementos
Cada elemento devuelto tiene las propiedades que se definieron en el modelo y también un par de métodos que puede utilizar para cambiar cada elemento.
1
2
3
4
5var John = Person.getSync(1);
John.name = "Joe";
John.surname = "Doe";
John.saveSync();
console.log("saved!");
Actualizar y luego guardar una instancia se puede realizar en una sola llamada:
1
2
3var John = Person.getSync(1);
John.saveSync({ name: "Joe", surname: "Doe" });
console.log("saved!");
Si desea eliminar una instancia, simplemente haga:
1
2
3
4// you could do this without even fetching it, look at Chaining section above
var John = Person.getSync(1);
John.removeSync();
console.log("removed!");
Validaciones
Ver información en la wiki .
Asociaciones
Una asociación es una relación entre una o más tablas.
hasOne
Es una relación de muchos a uno . Es lo mismo que pertenece a.
Por ejemplo: Animal.hasOne('owner', Person)
El
animal solo puede tener un dueño, pero la Persona puede tener muchos animales.
Al animal se le agregará la owner_id
propiedad automáticamente.
Las siguientes funciones estarán disponibles:
1
2
3
4animal.getOwnerSync() // Gets owner
animal.setOwnerSync(person) // Sets owner_id
animal.hasOwnerSync() // Checks if owner exists
animal.removeOwnerSync() // Sets owner_id to 0
Búsqueda de cadena
La asociación hasOne también es compatible con búsqueda de cadena. Usando el ejemplo anterior, podemos hacer esto para acceder a una nueva instancia de un objeto ChainFind:
1Animal.findByOwner({ /* options */ })
Acceso inverso
1Animal.hasOne('owner', Person, {reverse: 'pets'})
agregará lo siguiente:
1
2
3
4
5
6// Instance methods
person.getPetsSync(function..)
person.setPetsSync(cat, function..)
// Model methods
Person.findByPets({ /* options */ }) // returns ChainFind object
hasMany
Es una relación de muchos a muchos (incluye tabla de unión).
Por ejemplo: Patient.hasMany('doctors', Doctor, { why: String }, { reverse: 'patients', key: true })
El
paciente puede tener muchos médicos diferentes. Cada médico puede tener muchos pacientes diferentes.
Esto creará una tabla de unión patient_doctors
cuando llames Patient.sync()
:
nombre de la columna | tipo |
---|---|
ID del paciente | Entero (clave compuesta) |
médico_id | Entero (clave compuesta) |
por qué | varchar(255) |
Las siguientes funciones estarán disponibles:
1
2
3
4
5
6
7
8
9
10
11
12patient.getDoctorsSync() // List of doctors
patient.addDoctorsSync(docs) // Adds entries to join table
patient.setDoctorsSync(docs) // Removes existing entries in join table, adds new ones
patient.hasDoctorsSync(docs) // Checks if patient is associated to specified doctors
patient.removeDoctorsSync(docs) // Removes specified doctors from join table
doctor.getPatientsSync()
etc...
// You can also do:
patient.doctors = [doc1, doc2];
patient.saveSync()
Para asociar un médico a un paciente:
1patient.addDoctorSync(surgeon, {why: "remove appendix"})
que se agregará {patient_id: 4, doctor_id: 6, why: "remove appendix"}
a la tabla de unión.
obtener accesor
Este descriptor de acceso en este tipo de asociación devuelve un ChainFind
si no pasa una devolución de llamada. Esto significa que puedes hacer cosas como:
1
2var doctors = patient.getDoctors().order("name").offset(1).runSync());
// ... all doctors, ordered by name, excluding first one
extendsTo
Si desea dividir propiedades opcionales en diferentes tablas o colecciones, cada extensión estará en una nueva tabla, donde el identificador único de cada fila es la identificación de la instancia del modelo principal. Por ejemplo:
1
2
3
4
5
6
7var Person = db.define("person", {
name : String
});
var PersonAddress = Person.extendsTo("address", {
street : String,
number : Number
});
Esto creará una tabla person
con columnas id
y name
. La extensión creará una tabla person_address
con columnas person_id
y street
. number
Los métodos disponibles en el Person
modelo son similares a una hasOne
asociación. En este ejemplo, podrá llamar a .getAddress(cb)
, .setAddress(Address, cb)
, ..
Nota: no es necesario guardar el resultado desde Person.extendsTo
. Devuelve un modelo extendido. Puede usarlo para consultar directamente esta tabla extendida (e incluso encontrar el modelo relacionado), pero eso depende de usted. Si solo desea acceder a ella Usando el modelo original puedes simplemente descartar la devolución.
Examples & options
Si tiene una relación de 1 a n, debe usar hasOne
(pertenece a) asociación.
1
2
3
4
5
6
7
8
9
10
11
12
13var Person = db.define('person', {
name : String
});
var Animal = db.define('animal', {
name : String
});
Animal.hasOne("owner", Person); // creates column 'owner_id' in 'animal' table
// get animal with id = 123
var animal = Animal.getSync(123);
// animal is the animal model instance, if found
var person = animal.getOwnerSync();
// if animal has really an owner, person points to it
Puede marcar el owner_id
campo como requerido en la base de datos especificando la required
opción:
1Animal.hasOne("owner", Person, { required: true });
Si un campo no es obligatorio, pero debe validarse incluso si no está presente, especifique la alwaysValidate
opción (esto puede suceder, por ejemplo, cuando la validación de un campo nulo depende de otros campos en el registro).
1Animal.hasOne("owner", Person, { required: false, alwaysValidate: true });
Si prefiere utilizar otro nombre para el campo (owner_id), puede cambiar este parámetro en la configuración.
1db.settings.set("properties.association_key", "{field}_{name}"); // {name} will be replaced by 'owner' and {field} will be replaced by 'id' in this case
Nota: Esto debe hacerse antes de especificar la asociación.
Las hasMany
asociaciones pueden tener propiedades adicionales en la tabla de asociaciones.
1
2
3
4
5
6
7
8
9
10
11var Person = db.define('person', {
name : String
});
Person.hasMany("friends", {
rate : Number
}, {}, { key: true });
var John = Person.getSync(123);
var friends = John.getFriendsSync();
// assumes rate is another column on table person_friends
// you can access it by going to friends[N].extra.rate
Si lo prefieres, puedes activar autoFetch
De esta manera, las asociaciones se obtienen automáticamente cuando obtienes o encuentras instancias de un modelo.
1
2
3
4
5
6
7
8
9
10
11
12var Person = db.define('person', {
name : String
});
Person.hasMany("friends", {
rate : Number
}, {
key : true, // Turns the foreign keys in the join table into a composite key
autoFetch : true
});
var John = Person.getSync(123);
// no need to do John.getFriends() , John already has John.friends Array
También puede definir esta opción globalmente en lugar de por asociación.
1
2
3
4
5
6
7
8
9
10var Person = db.define('person', {
name : String
}, {
autoFetch : true
});
Person.hasMany("friends", {
rate : Number
}, {
key: true
});
Las asociaciones pueden realizar llamadas al modelo asociado usando la reverse
opción. Por ejemplo, si tiene una asociación del ModeloA al ModeloB, puede crear un descriptor de acceso en el ModeloB para obtener instancias del ModeloA. ¿Confuso? Mire el siguiente ejemplo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var Pet = db.define('pet', {
name : String
});
var Person = db.define('person', {
name : String
});
Pet.hasOne("owner", Person, {
reverse : "pets"
});
var pets = Person(4).getPetsSync();
// although the association was made on Pet,
// Person will have an accessor (getPets)
//
// In this example, ORM will fetch all pets
// whose owner_id = 4
Esto tiene aún más sentido cuando se tienen hasMany
asociaciones, ya que se pueden gestionar muchas o muchas
asociaciones de ambos lados.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var Pet = db.define('pet', {
name : String
});
var Person = db.define('person', {
name : String
});
Person.hasMany("pets", Pet, {
bought : Date
}, {
key : true,
reverse : "owners"
});
Person(1).getPetsSync(...);
Pet(2).getOwnersSync(...);
Soporte de transacciones
Puede utilizar la función de transacción de bajo nivel para procesar la transacción de base de datos.
1
2
3
4
5
6db.begin();
...
if(err)
db.rollback();
else
db.commit();
O puedes usar trans para simplificar el proceso.
1
2
3
4var result = db.trans(() => {
...
return result;
});
Agregar adaptadores de bases de datos externos
Para agregar un adaptador de base de datos externo orm
, llame al addAdapter
método y pase el alias que se usará para conectarse con este adaptador, junto con el constructor del adaptador:
1require('orm').addAdapter('cassandra', CassandraAdapter);
Verthe documentation for creating adapterspara más detalles.