Skip to content

Commit

Permalink
Merge 71e0220 into 40bcdc0
Browse files Browse the repository at this point in the history
  • Loading branch information
B4nan committed Mar 18, 2019
2 parents 40bcdc0 + 71e0220 commit 4aead42
Show file tree
Hide file tree
Showing 21 changed files with 1,172 additions and 69 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ node_js:
services:
- mongodb
- mysql
- postgresql

cache:
directories:
Expand All @@ -20,6 +21,7 @@ script:

before_install:
- mysql -u root -e 'CREATE DATABASE mikro_orm_test;'
- psql -c 'create database mikro_orm_test;' -U postgres
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# MikroORM

Simple typescript ORM for node.js based on data-mapper, unit-of-work and identity-map patterns. Supports MongoDB,
MySQL and SQLite databases.
MySQL, PostgreSQL and SQLite databases.

Heavily inspired by [Doctrine](https://www.doctrine-project.org/) and [Nextras Orm](https://nextras.org/orm/).

Expand Down Expand Up @@ -37,16 +37,18 @@ First install the module via `yarn` or `npm` and do not forget to install the da

```
$ yarn add mikro-orm mongodb # for mongo
$ yarn add mikro-orm mysql2 # for mysql
$ yarn add mikro-orm sqlite # for sqlite
$ yarn add mikro-orm mysql2 # for mysql
$ yarn add mikro-orm pg # for postgresql
$ yarn add mikro-orm sqlite # for sqlite
```

or

```
$ npm i -s mikro-orm mongodb # for mongo
$ npm i -s mikro-orm mysql2 # for mysql
$ npm i -s mikro-orm sqlite # for sqlite
$ npm i -s mikro-orm mysql2 # for mysql
$ npm i -s mikro-orm pg # for postgresql
$ npm i -s mikro-orm sqlite # for sqlite
```

Next you will need to enable support for [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html)
Expand Down
1 change: 0 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# TODO list

- postgres driver
- schema generator for SQL drivers
- single table inheritance
- implement transactions in mongo driver
Expand Down
11 changes: 10 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,21 @@ services:
image: mysql:5.7
restart: unless-stopped
ports:
- "127.0.0.1:3307:3306"
- 3307:3306
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: 1
volumes:
- mysql:/var/lib/mysql

postgre:
container_name: postgre
image: postgres:11.2
ports:
- 5432:5432
volumes:
- postgre:/var/lib/postgresql/data

volumes:
mongo:
mysql:
postgre:
12 changes: 8 additions & 4 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ First install the module via `yarn` or `npm` and do not forget to install the da

```
$ yarn add mikro-orm mongodb # for mongo
$ yarn add mikro-orm mysql2 # for mysql
$ yarn add mikro-orm sqlite # for sqlite
$ yarn add mikro-orm mysql2 # for mysql
$ yarn add mikro-orm pg # for postgresql
$ yarn add mikro-orm sqlite # for sqlite
```

or

```
$ npm i -s mikro-orm mongodb # for mongo
$ npm i -s mikro-orm mysql2 # for mysql
$ npm i -s mikro-orm sqlite # for sqlite
$ npm i -s mikro-orm mysql2 # for mysql
$ npm i -s mikro-orm pg # for postgresql
$ npm i -s mikro-orm sqlite # for sqlite
```

Next you will need to enable support for [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html)
Expand Down Expand Up @@ -76,6 +78,8 @@ const orm = await MikroORM.init({
});
```

## Request context

Then you will need to fork entity manager for each request so their identity maps will not
collide. To do so, use the `RequestContext` helper:

Expand Down
3 changes: 2 additions & 1 deletion docs/usage-with-sql.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
To use `mikro-orm` with MySQL database, do not forget to install `mysql2` dependency and provide
`MySqlDriver` class when initializing ORM.

Similarly for SQLite install `sqlite` dependency and provide `SqliteDriver`.
Similarly for SQLite install `sqlite` dependency and provide `SqliteDriver`. For PostgreSQL
install `pg` and provide `PostgreSqlDriver`.

Then call `MikroORM.init` as part of bootstrapping your app:

Expand Down
42 changes: 41 additions & 1 deletion lib/connections/Connection.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { URL } from 'url';
import { Configuration } from '../utils';

export abstract class Connection {

protected readonly logger = this.config.getLogger();
protected abstract client: any;

constructor(protected readonly config: Configuration) { }

Expand Down Expand Up @@ -47,7 +49,37 @@ export abstract class Connection {
throw new Error(`Transactions are not supported by current driver`);
}

abstract async execute(query: string, params?: any[], method?: string): Promise<QueryResult | any | any[]>;
abstract async execute(query: string, params: any[], method?: 'all' | 'get' | 'run'): Promise<QueryResult | any | any[]>;

getConnectionOptions(): ConnectionConfig {
const ret: ConnectionConfig = {};
const url = new URL(this.config.getClientUrl());
ret.host = this.config.get('host', url.hostname);
ret.port = this.config.get('port', +url.port);
ret.user = this.config.get('user', url.username);
ret.password = this.config.get('password', url.password);
ret.database = this.config.get('dbName', url.pathname.replace(/^\//, ''));

return ret;
}

protected async executeQuery<T>(query: string, params: any[], cb: () => Promise<T>): Promise<T> {
try {
const now = Date.now();
const res = await cb();
this.logQuery(query + ` [took ${Date.now() - now} ms]`);

return res;
} catch (e) {
e.message += `\n in query: ${query}`;

if (params && params.length) {
e.message += `\n with params: ${JSON.stringify(params)}`;
}

throw e;
}
}

protected logQuery(query: string): void {
this.logger.debug(`[query-logger] ${query}`);
Expand All @@ -59,3 +91,11 @@ export interface QueryResult {
affectedRows: number;
insertId: number;
}

export interface ConnectionConfig {
host?: string;
port?: number;
user?: string;
password?: string;
database?: string;
}
43 changes: 12 additions & 31 deletions lib/connections/MySqlConnection.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import { Connection as MySql2Connection, ConnectionOptions, createConnection } from 'mysql2/promise';
import { readFileSync } from 'fs';
import { URL } from 'url';
import { Connection, QueryResult } from './Connection';

export class MySqlConnection extends Connection {

private connection: MySql2Connection;
protected client: MySql2Connection;

async connect(): Promise<void> {
this.connection = await createConnection(this.getConnectionOptions());
this.client = await createConnection(this.getConnectionOptions());
}

async close(force?: boolean): Promise<void> {
await this.connection.end({ force });
await this.client.end({ force });
}

async isConnected(): Promise<boolean> {
try {
await this.connection.query('SELECT 1');
await this.client.query('SELECT 1');
return true;
} catch {
return false;
Expand All @@ -41,35 +40,17 @@ export class MySqlConnection extends Connection {
}

async execute(query: string, params: any[] = [], method: 'all' | 'get' | 'run' = 'all'): Promise<QueryResult | any | any[]> {
try {
const now = Date.now();
const res = await this.connection.execute(query, params);
this.logQuery(query + ` [took ${Date.now() - now} ms]`);

if (method === 'get') {
return (res as QueryResult[][])[0][0];
}

return res[0];
} catch (e) {
e.message += `\n in query: ${query}`;
const res = await this.executeQuery(query, params, () => this.client.execute(query, params));

if (params && params.length) {
e.message += `\n with params: ${JSON.stringify(params)}`;
}

throw e;
if (method === 'get') {
return (res as QueryResult[][])[0][0];
}

return res[0];
}

getConnectionOptions(): ConnectionOptions {
const ret = {} as ConnectionOptions;
const url = new URL(this.config.getClientUrl());
ret.host = this.config.get('host', url.hostname);
ret.port = this.config.get('port', +url.port);
ret.user = this.config.get('user', url.username);
ret.password = this.config.get('password', url.password);
ret.database = this.config.get('dbName', url.pathname.replace(/^\//, ''));
const ret: ConnectionOptions = super.getConnectionOptions();

if (this.config.get('multipleStatements')) {
ret.multipleStatements = this.config.get('multipleStatements');
Expand All @@ -79,12 +60,12 @@ export class MySqlConnection extends Connection {
}

async loadFile(path: string): Promise<void> {
await this.connection.query(readFileSync(path).toString());
await this.client.query(readFileSync(path).toString());
}

private async query(sql: string): Promise<void> {
const now = Date.now();
await this.connection.query(sql);
await this.client.query(sql);
this.logQuery(`${sql} [took ${Date.now() - now} ms]`);
}

Expand Down
68 changes: 68 additions & 0 deletions lib/connections/PostgreSqlConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Client } from 'pg';
import { readFileSync } from 'fs';
import { Connection, QueryResult } from './Connection';
import { EntityData, IEntity } from '../decorators';

export class PostgreSqlConnection extends Connection {

protected client: Client;

async connect(): Promise<void> {
this.client = new Client(this.getConnectionOptions());
await this.client.connect();
}

async close(force?: boolean): Promise<void> {
await this.client.end();
}

async isConnected(): Promise<boolean> {
try {
await this.client.query('SELECT 1');
return true;
} catch {
return false;
}
}

async beginTransaction(savepoint?: string): Promise<void> {
await this.execute(savepoint ? `SAVEPOINT ${savepoint}` : 'START TRANSACTION', [], 'run');
}

async commit(savepoint?: string): Promise<void> {
await this.execute(savepoint ? `RELEASE SAVEPOINT ${savepoint}` : 'COMMIT', [], 'run');
}

async rollback(savepoint?: string): Promise<void> {
await this.execute(savepoint ? `ROLLBACK TO SAVEPOINT ${savepoint}` : 'ROLLBACK', [], 'run');
}

getDefaultClientUrl(): string {
return 'postgre://postgres@127.0.0.1:5432';
}

async execute(query: string, params: any[] = [], method: 'all' | 'get' | 'run' = 'all'): Promise<QueryResult | any | any[]> {
const res = await this.executeQuery(query, params, () => this.client.query(query, params));
return this.transformResult(res, method);
}

async loadFile(path: string): Promise<void> {
await this.client.query(readFileSync(path).toString());
}

private transformResult(res: any, method: 'all' | 'get' | 'run'): QueryResult | EntityData<IEntity> | EntityData<IEntity>[] {
if (method === 'get') {
return res.rows[0];
}

if (method === 'run') {
return {
affectedRows: res.rowCount,
insertId: res.rows[0] ? res.rows[0].id : 0,
};
}

return res.rows;
}

}

0 comments on commit 4aead42

Please sign in to comment.