Entity - Hookyns/unimapperjs GitHub Wiki

Entity

Static Methods

  • async insert(entity: Entity)
  • async remove(entity: Entity)
  • async getAll() : Query
  • async getById(id) : Entity

Instance Methods

  • select(...fields) : Object Creates new Object and copy selected fields
  • async save() Save tracked changes
  • mapFrom(data: Object) Map data from given Object into current entity instance.

Declarations

Entity can be declared in two ways. Object-like and class-like.

Object-like

It's simple declaration but it cannot have navigation properties, seeding nor additional methods.

const User = domain.createEntity("User", {
	name: type.string.length(100),
	email: type.string.length(100).unique(),
	password: type.string.length(40),
	created: type.date.now(),
	deleted: type.boolean.default(false),
	income: type.number.decimals(2).default(0)
});

Class-like

Normal declaration by ES6 class which extends unimapperjs/src/Entity.

TypeScript variant

import {type} from "unimapperjs";
import {Entity} from "unimapperjs/src/Entity";
import {domain} from "./domain";
import {Teacher} from "./Teacher";

@domain.entity() // decorator which registers this entity
export class Student extends Entity<Student>
{
    /**
     * Student Id
     */
    id: number;

    /**
     * Student name
     */
    name: string = "Can have default value defined like this one";

    /**
     * Student's teacher id
     */
    teacherId: number;

    /**
     * Navigation property to Teacher
     */
    teacher: Teacher;

    // MAPPING
    static map(map: Student) {
        const {Teacher} = require("./Teacher");

        map.id = <any>type.uuid;
        map.name = <any>type.string.length(100);
        map.teacherId = <any>type.number;
        map.teacher = <any>type.foreign(Teacher.name)
            .withForeign<Student>(s => s.teacherId); // Expression to property; allow refactoring
    }
}

JavaScript variant

const {type} = require("unimapperjs");
const {Entity} = require("unimapperjs/src/Entity");
const {domain} = require("./domain");

const Teacher = class Teacher extends Entity {
    constructor() {
        super();

	/**
         * Teacher Id
	 * @type {number}
	 */
	this.id = null;

	/**
         * Teacher first name
	 * @type {string}
	 */
	this.firstName = null;

	/**
         * Teacher last name
	 * @type {string}
	 */
	this.lastName = null;

	/**
         * Teacher's students - navigation property
	 * @type {Promise<Array<Student>>}
	 */
	this.students = null;
    }

    static map(map) {
        const { Student } = require("./Student");
        map.id = type.number.primary().autoIncrement();
        map.firstName = type.string.length(50);
        map.lastName = type.string.length(50);
        map.students = type.foreign(Student.name)
            .hasMany(s => s.teacherId);
    }
};

// Register entity with TS decorator function manually
domain.entity()(Teacher);

// export
module.exports.Teacher = Teacher;

You can omit constructor declaration, only map() method is important, but if you do so, you'll lose IDE completions.

Why is map() important?

Teacher navigate to Student and Student navigate to Teacher. There is cycle dependency which must be resolved. Method map() is async, it init map() method and rest of work is in process.nextTick, giving chance to node to resolve modules.

Because of that async behavior, you should not work with entities in same event-loop iteration in which you declare your entities.

See this example.

example.js

const {Student} = require("./Student");

// Create new Student
let newStudent = new Student();
newStudent.name = "John Smith";

Student.insert(newStudent);

If this is first time when Student is required, class Student in declared and domain.entity()(Student) will be called. But how I've said before, it is async and it's going to be finished in process.nextTick(). You can use setImmediate() for example, if you run some simple scripts, which has no async behavior, like this. But in most cases, after require() you wait eg. for connection, so it will not be necessary.

Example

const $um = require("unimapperjs");
const MySqlAdapter = require(...);
const type = $um.type;
const domain = $um.createDomain(MySqlAdapter, "mysql://test:test@localhost/test");

/**
 * Define User entity
 * @class User
 * @extends UniMapperEntity
 */
const User = domain.createEntity("User", {
	name: type.string.length(100),
	email: type.string.length(100).unique(),
	password: type.string.length(40),
	created: type.date.now(),
	deleted: type.boolean.default(false),
	income: type.number.decimals(2).default(0)
});

(async function() {
    var user = new User({
        name: "Jane",
        email: "[email protected]",
        password: "68d79gds4fv5sd4g645fgad6fg4"
    };

    await User.insert(user);
    console.log("Insert ID:", user.id);

    user.name = "Kate";
    await user.save(); // Will create "update User SET name = 'Kate' where id = ...;" Only changed properties are sent

})().catch(function(err) {
    console.error(err);
}).then(function() {
    domain.dispose(); // End connection pool before exiting script
);
⚠️ **GitHub.com Fallback** ⚠️