refactored readme stuff and added more contributing docs

This commit is contained in:
Umed Khudoiberdiev 2016-10-16 14:56:41 +05:00
parent a1ff1419de
commit 86a347d1f9
25 changed files with 977 additions and 1656 deletions

3
.gitignore vendored
View File

@ -1,6 +1,5 @@
build/
coverage/
node_modules/
typings/
npm-debug.log
config/parameters.json
ormconfig.json

12
CHANGELOG.md Normal file
View File

@ -0,0 +1,12 @@
# 0.0.2 (in development)
* lot of API refactorings
* complete support TypeScript 2
* optimized schema creation
* command line tools
* multiple drivers support
* multiple bugfixes
# 0.0.1
* first stable version, works with TypeScript 1.x

119
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,119 @@
# Contributing to TypeORM
We would love for you to contribute to TypeORM and help make it even better than it is today!
As a contributor, here are the guidelines we would like you to follow:
- [Question or Problem?](#question)
- [Issues and Bugs](#issue)
- [Feature Requests](#feature)
- [Submission Guidelines](#submit)
## <a name="question"></a> Got a Question or Problem?
There are several ways how you can ask your question:
* You can create a question on [StackOverflow](stackoverflow.com/questions/tagged/typeorm) where the questions should be tagged with tag `typeorm`.
* You can create issue on [github](https://github.com/typeorm/typeorm/issues)
* You can write your questions in our [gitter channel](https://gitter.im/pleerock/typeorm)
* If you have a Skype then try to find me there (`Umed Khudoiberdiev`)
Prefered way if you create your question on StackOverflow, or create a github issue.
## <a name="issue"></a> Found a Bug?
If you find a bug in the source code, you can help us by [submitting an issue](#submit-issue) to our
[GitHub Repository](https://github.com/typeorm/typeorm).
Even better, you can [submit a Pull Request](#submit-pr) with a fix.
## <a name="feature"></a> Missing a Feature?
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub
Repository. If you would like to *implement* a new feature, please submit an issue with
a proposal for your work first, to be sure that we can use it.
Please consider what kind of change it is:
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
and help you to craft the change so that it is successfully accepted into the project.
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
## <a name="submit"></a> Submission Guidelines
### <a name="submit-issue"></a> Submitting an Issue
Before you submit an issue, please search the issue tracker,
maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it.
In order to reproduce bugs we ask you to provide a minimal code snippet that shows a reproduction of the problem.
You can file new issues by filling out our [new issue form](https://github.com/typeorm/typeorm/issues/new).
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
Before you submit your Pull Request (PR) consider the following guidelines:
* Search [GitHub](https://github.com/typeorm/typeorm/pulls) for an open or closed PR
that relates to your submission. You don't want to duplicate effort.
* Make your changes in a new git branch:
```shell
git checkout -b my-fix-branch master
```
* Create your patch, **including appropriate test cases**. Without tests your PR will not be accepted.
* Follow our [Coding Rules](#rules).
* Run the full TypeORM test suite, as described in the [developer documentation](DEVELOPER.md), and ensure that all tests pass.
* Commit your changes using a descriptive commit message
```shell
git commit -a
```
* Push your branch to GitHub:
```shell
git push origin my-fix-branch
```
* In GitHub, send a pull request to `typeorm:master`.
* If we suggest changes then:
* Make the required updates.
* Re-run the TypeORM test suites to ensure tests are still passing.
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```shell
git rebase master -i
git push -f
```
That's it! Thank you for your contribution!
#### After your pull request is merged
After your pull request is merged, you can safely delete your branch and pull the changes
from the main (upstream) repository:
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
```shell
git push origin --delete my-fix-branch
```
* Check out the master branch:
```shell
git checkout master -f
```
* Delete the local branch:
```shell
git branch -D my-fix-branch
```
* Update your master with the latest upstream version:
```shell
git pull --ff upstream master
```

95
DEVELOPER.md Normal file
View File

@ -0,0 +1,95 @@
# Building and Testing TypeORM
This document describes how to set up your development environment and run TypeORM test cases.
* [Prerequisite Software](#prerequisite-software)
* [Getting the Sources](#getting-the-sources)
* [Installing NPM Modules](#installing-npm-modules)
* [Building](#building)
* [Running Tests Locally](#running-tests-locally)
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
if you'd like to contribute to Angular.
## Prerequisite Software
Before you can build and test TypeORM, you must install and configure the
following products on your development machine:
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
Git](https://help.github.com/articles/set-up-git) is a good source of information.
* [Node.js](http://nodejs.org), (better to install latest version) which is used to run a development web server,
run tests, and generate distributable files.
Depending on your system, you can install Node either from source or as a pre-packaged bundle.
* [Mysql](https://www.mysql.com/) is required to run tests on this platform
* [MariaDB](https://mariadb.com/) is required to run tests on this platform
* [Postgres](https://www.postgresql.org/) is required to run tests on this platform
* [Oracle](https://www.oracle.com/database/index.html) is required to run tests on this platform
* [Microsoft SQL Server](https://www.microsoft.com/en-us/cloud-platform/sql-server) is required to run tests on this platform
## Getting the Sources
Fork and clone the repository:
1. Login to your GitHub account or create one by following the instructions given [here](https://github.com/signup/free).
2. [Fork](http://help.github.com/forking) the [main TypeORM repository](https://github.com/typeorm/typeorm).
3. Clone your fork of the TypeORM repository and define an `upstream` remote pointing back to
the TypeORM repository that you forked in the first place.
```shell
# Clone your GitHub repository:
git clone git@github.com:<github username>/typeorm.git
# Go to the TypeORM directory:
cd typeorm
# Add the main TyepORM repository as an upstream remote to your repository:
git remote add upstream https://github.com/typeorm/typeorm.git
```
## Installing NPM Modules
Install all TypeORM dependencies by running this command:
```shell
npm install
```
During installation you may have some probelems with some dependencies.
For example to proper install oracle driver you need to follow all instructions from
[node-oracle documentation](https://github.com/oracle/node-oracledb).
Also install these packages globally:
* `npm install -g gulp` (you might need to prefix this command with `sudo`)
* `npm install -g typescript` (you might need to prefix this command with `sudo`)
## Building
To build a distribution package of TypeORM run:
```shell
gulp package
```
This command will generate you a distribution package in the `build/package` directory.
You can link (or simply copy/paste) this directory into your project and test TypeORM there
(but make sure to keep all node_modules required by TypeORM).
## Running Tests Locally
Setup your environment configuration by copying `ormconfig.json.dist` into `ormconfig.json` and
replacing parameters with your own.
Then run tests:
```shell
gulp tests
```
You should execute test suites before submitting a PR to github.
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.

21
LICENCE Normal file
View File

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2015-2016 Yakdu. http://typeorm.github.io
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

1193
README.md

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +0,0 @@
## Command Line Tools
TODO: first need to create gulp package and tasks to perform following command line operations:
* create a schema
* update a schema
* drop a schema
* create entity
* create subscriber
* create custom repository?
* create a backup?
* .....

View File

@ -1,159 +0,0 @@
## Connection and connection options
You start using ORM by creating a connection with the database. In this section you will learn about:
* [Connection Manager that contains all connections to the databases](#connection-manager)
* [Working with connections](#working-with-connection)
* [Connection options](#connection-options)
* [How to use connection manager, connections and connection options](#example)
### Connection Manager
Connection manager allows to create a new connections and retrieve previously created connections. Also it allows to import
entities and subscribers into specific connection. These are main public methods of the `ConnectionManager`:
* `createConnection(options: CreateConnectionOptions): Connection`
Creates a new connection and registers it in the connection manager. It returns a newly created connection.
New connection will have a given *connection name*. If connection name is not given then "default" will be used as a
connection name. This name will be used to retrieve this connection later.
*Driver* needs to be specified to understand what kind of database will be used for this connection.
Right now it can be only a `mysql` driver.
*Options* specifies connection options. More about it later.
* `getConnection(connectionName: string = "default"): Connection`
Gets a connection with a given name that was created using `createConnection` method. Connection later can be used to
perform actions on it.
* `importEntities(connectionName: string = "default", entities: Function[]): void`
Imports all given entities and registers them in the connection with a given name.
* `importSubscribers(connectionName: string = "default", subscribers: Function[]): void`
Imports all given subscribers and registers them in the connection with a given name.
* `importEntitiesFromDirectories(connectionName: string = "default", paths: string[]): void`
Imports all entities from the given directories and registers them in the connection with a given name.
Paths is an array of directories from where to import entities.
* `importSubscribersFromDirectories(connectionName: string = "default", paths: string[]): void`
Imports all subscribers from the given directories and registers them in the connection with a given name.
Paths is an array of directories from where to import subscribers.
### Working with Connection
Connection is a database connection to specific database of the specific database management system.
There are several useful methods in the Connection object:
* `connect()`
Opens a new connection with the database.
* `close()`
Closes connection with the database.
* `getEntityManager()`
Gets [EntityManager](entity-manager.md) that is used to execute database operations on any entity
that is registered in this connection.
* `getRepository(entityClass: Function)`
Gets a [repository](repository.md) of the specific entity that provides all functionality
(selects/inserts/updates/deletes) with a table of the given entity class.
### Connection Options
To perform a connection you need to specify a connection options. ConnectionOptions is an interface:
```typescript
export interface ConnectionOptions {
url?: string; // connection url
host?: string; // database host
port?: number; // database host port
username?: string; // database username
password?: string; // database password
database?: string; // database name
autoSchemaSync?: boolean; // set to true if you want your database schema to be auto created on each application launch
logging?: {
logger?: (message: any, level: string) => void; // some specific logger to be used. By default it is a console
logQueries?: boolean; // used if you want to log every executed query
logOnlyFailedQueries?: boolean; // used if you want to log only failed query
logFailedQueryError?: boolean; // used if you want to log error of the failed query
};
}
```
* To perform a connection you either must specify a connection `url`, either specify `host/port/username/password/database`.
* `autoSchemaSync` allows you to automatically synchronize your database schema (create new tables,
remove/rename old columns, create foreign keys, etc.) on each application run. Note that there can be errors in schema
synchronization (mostly errors can be caused by unresolved foreign keys) and this will crash your application.
This option should not be used in production, only during development and only if you are too lazy to use
[command line tools](command-line-tools.md). Alternatively you can use [schema update gulp plugin](todo).
### Example
```typescript
// create a new connection manager
let connectionManager = new ConnectionManager();
// prepare connection options
let connectionOptions: ConnectionOptions = {
host: "localhost",
port: 3306,
username: "root",
password: "admin",
database: "test",
autoSchemaSync: true
};
// create a new connection with mysql driver
connectionManager.createConnection(new MysqlDriver(connectionOptions));
// import all entities from specific directory
connectionManager.importEntitiesFromDirectories(__dirname + "/entities");
// get our connection:
let connection = connectionManager.getConnection();
connection.connect().then(connection => {
// now we are connected to the database
// here we have a connection and we can use any of its methods
// lets say we have a Photo entity in the /entities directory
// and lets create a new Photo entity instance
// lets try to use entity manager
let entityManager = connection.getEntityManager();
// and lets create a new Photo entity instance
let photo = new Photo();
photo.name = "photo #1";
// and save it using entity manager
entityManager
.persist(photo)
.then(photo => {
console.log("Photo has been saved using entity manager");
});
// lets try to use repository
let repository = connection.getRepository(Photo);
// and lets create a new Photo entity instance
let photo = new Photo();
photo.name = "photo #2";
// and save it using repository
repository
.persist(photo)
.then(photo => {
console.log("Photo has been saved using repository");
});
}).catch(error => {
// looks like some error during connection. Lets log it to find details
console.log("error during connection to the database ", error);
});
```

View File

@ -1,36 +0,0 @@
## Databases and drivers
ORM is working with databases. To communicate with database it uses **database drivers**.
Database Driver communicates with specific database and performs queries you need.
### Driver Interface
Right now only `mysql` database driver is supported. If you need other driver, or you simply
want to contribute then feel free to add it - adding new drivers is not complicated.
Drivers are typically used within connection:
`let connection = connectionManager.createConnection("my-connection", new MysqlDriver());`
To access the driver you can do it from your connection instance:
`connection.driver`
There are several useful methods in the driver object:
* `driver.native`
Allows you to access a native layer of the driver. For example MysqlDriver is depend of npm's `mysql` package
and `mysqlDriver.native` will give you instance to that package.
* `driver.nativeConnection`
Allows you to access to a connection instance of the native layer of the driver. For example MysqlDriver is depend of
npm's `mysql` package and when we create a connection using that package it returns us some `connection instance` that
we save in the driver.nativeConnection.
Both methods are not recommended to use, but sometimes you have no choice because of the limits of the ORM abstraction
and you have to use them. In such situations both methods are very useful.
There are lot of other methods in the Driver, but you won't use them directly - they are used by other components, like
[Entity Manager](entity-manager.md) or [Repository](repository.md). You should use those components instead.

View File

@ -1,196 +0,0 @@
## Decorators Reference
* Table Decorators
* [@Table](#table)
* [@AbstractTable](#abstract-table)
* Column Decorators
* [@Column](#column)
* [@PrimaryColumn](#primary-column)
* [@CreateDateColumn](#create-date-column)
* [@UpdateDateColumn](#update-date-column)
* Relation Decorators
* [@OneToOne](#one-to-one)
* [@OneToOneInverse](#one-to-one-inverse)
* [@ManyToOne](#many-to-one)
* [@OneToMany](#one-to-many)
* [@ManyToMany](#many-to-many)
* [@ManyToManyInverse](#many-to-many-inverse)
* Subscriber and Listener Decorators
* [@EventSubscriber](#event-subscriber)
* [@AfterLoad](#after-load)
* [@BeforeInsert](#before-insert)
* [@AfterInsert](#after-insert)
* [@BeforeUpdate](#before-update)
* [@AfterUpdate](#after-update)
* [@BeforeRemove](#before-remove)
* [@AfterRemove](#after-remove)
* Indices
* [@Index](#index)
* [@CompoundIndex](#compound-index)
### Table Decorators
#### @Table
`@Table(name: string)`
This decorator is used to mark classes that will be a tables. Database schema will be created for all classes
decorated with it, and Repository can be retrieved and used for it.
#### @AbstractTable
`@AbstractTable()`
Allows to use columns and relations data from the inherited metadata.
### Column Decorators
#### @Column
`@Column(options?: ColumnOptions)`
`@Column(type?: ColumnType, options?: ColumnOptions)`
Column decorator is used to mark a specific class property as a table column. Only properties decorated with this
decorator will be persisted to the database when entity be saved.
#### @PrimaryColumn
`@PrimaryColumn(options?: ColumnOptions)`
`@PrimaryColumn(type?: ColumnType, options?: ColumnOptions)`
Column decorator is used to mark a specific class property as a table column. Only properties decorated with this
decorator will be persisted to the database when entity be saved. Primary columns also creates a PRIMARY KEY for
this column in a db.
#### @CreateDateColumn
`@CreateDateColumn(options?: ColumnOptions)`
This column will store a creation date of the inserted object. Creation date is generated and inserted only once,
at the first time when you create an object, the value is inserted into the table, and is never touched again.
#### @UpdateDateColumn
`@UpdateDateColumn(options?: ColumnOptions)`
This column will store an update date of the updated object. This date is being updated each time you persist the
object.
### Relation Decorators
#### @OneToOne
`@OneToOne<T>(typeFunction: (type?: any) => Function, options?: RelationOptions)`
`@OneToOne<T>(typeFunction: (type?: any) => Function, inverseSide?: string|((object: T) => any), options?: RelationOptions)`
One-to-one relation allows to create direct relation between two entities. Entity1 have only one Entity2.
Entity1 is an owner of the relationship, and storages Entity1 id on its own side.
#### @OneToOneInverse
`@OneToOneInverse<T>(typeFunction: (type?: any) => Function, options?: RelationOptions)`
`@OneToOneInverse<T>(typeFunction: (type?: any) => Function, inverseSide?: string|((object: T) => any), options?: RelationOptions)`
Inverse side of the one-to-one relation. One-to-one relation allows to create direct relation between two entities.
Entity2 have only one Entity1. Entity2 is inverse side of the relation on Entity1. Does not storage id of the
Entity1. Entity1's id is storage on the one-to-one owner side.
#### @ManyToOne
`@ManyToOne<T>(typeFunction: (type?: any) => Function, options?: RelationOptions)`
`@ManyToOne<T>(typeFunction: (type?: any) => Function, inverseSide?: string|((object: T) => any), options?: RelationOptions)`
Many-to-one relation allows to create type of relation when Entity1 can have single instance of Entity2, but
Entity2 can have a multiple instances of Entity1. Entity1 is an owner of the relationship, and storages Entity2 id
on its own side.
#### @OneToMany
`@OneToMany<T>(typeFunction: (type?: any) => Function, options?: RelationOptions)`
`@OneToMany<T>(typeFunction: (type?: any) => Function, inverseSide?: string|((object: T) => any), options?: RelationOptions)`
One-to-many relation allows to create type of relation when Entity2 can have multiple instances of Entity1.
Entity1 have only one Entity2. Entity1 is an owner of the relationship, and storages Entity2 id on its own side.
#### @ManyToMany
`@ManyToMany<T>(typeFunction: (type?: any) => Function, options?: RelationOptions)`
`@ManyToMany<T>(typeFunction: (type?: any) => Function, inverseSide?: string|((object: T) => any), options?: RelationOptions)`
Many-to-many is a type of relationship when Entity1 can have multiple instances of Entity2, and Entity2 can have
multiple instances of Entity1. To achieve it, this type of relation creates a junction table, where it storage
entity1 and entity2 ids. This is owner side of the relationship.
#### @ManyToManyInverse
`@ManyToManyInverse<T>(typeFunction: (type?: any) => Function, options?: RelationOptions)`
`@ManyToManyInverse<T>(typeFunction: (type?: any) => Function, inverseSide?: string|((object: T) => any), options?: RelationOptions)`
Many-to-many is a type of relationship when Entity1 can have multiple instances of Entity2, and Entity2 can have
multiple instances of Entity1. To achieve it, this type of relation creates a junction table, where it storage
entity1 and entity2 ids. This is inverse side of the relationship.
### Subscriber and Listener Decorators
#### @EventSubscriber
`@EventSubscriber()`
Classes decorated with this decorator will listen to ORM events and their methods will be triggered when event
occurs. Those classes must implement EventSubscriberInterface.
#### @AfterLoad
`@AfterLoad()`
* Calls a method on which this decorator is applied after entity is loaded.
#### @BeforeInsert
`@BeforeInsert()`
Calls a method on which this decorator is applied before this entity insertion.
#### @AfterInsert
`@AfterInsert()`
Calls a method on which this decorator is applied after this entity insertion.
#### @BeforeUpdate
`@BeforeUpdate()`
Calls a method on which this decorator is applied before this entity update.
#### @AfterUpdate
`@AfterUpdate()`
Calls a method on which this decorator is applied after this entity update.
#### @BeforeRemove
`@BeforeRemove()`
Calls a method on which this decorator is applied before this entity removal.
#### @AfterRemove
`@AfterRemove()`
Calls a method on which this decorator is applied after this entity removal.
### Indices
#### @Index
`@Index(name?: string)`
Fields that needs to be indexed must be marked with this decorator.
#### @CompoundIndex
`@CompoundIndex(fields: string[])`
Compound indexes must be set on entity classes and must specify fields to be indexed.

View File

@ -1,70 +0,0 @@
## Entity Manager
EntityManager provides functionality to work with all your entities in a single connection.
Its like a Repository, but works with all entities.
There are several useful methods of the EntityManager:
* `getRepository(entity: Entity): Repository<Entity>`
Gets the repository of the given entity.
* `hasId(entity: Entity): boolean`
Sometimes you want to check if your entity already has id or not. This maybe useful in situations when you want to
check if your object is new or not.
* `create(entityClass: Function, plainJsObject?: Object): Entity`
Creates a new empty instance of entity. If `plainJsObject` is given then new entity will be created and all properties
from the `plainJsObject` that can be mapped to this entity will be copied to the new entity.
* `createMany(entityClass: Function, plainJsObjects: Object[]): Entity[]`
Creates multiple new entities based on array of plain javascript objects. Properties for each plain javascript object
will be copied to newly created entities if they should exist there.
* `initialize(entityClass: Function, object: Object): Promise<Entity>`
Creates a new entity from the given plan javascript object. If entity already exist in the database, then
it loads it (and everything related to it), replaces all values with the new ones from the given object
and returns this new entity. This new entity is actually a loaded from the db entity with all properties
replaced from the new object.
* `persist(entity: Entity): Promise<Entity>`
Persists (saves) a given entity in the database. If entity does not exist in the database then it inserts it,
else if entity already exist in the database then it updates it.
* `remove(entity: Entity): Promise<Entity>`
Removes a given entity from the database.
* `find(entityClass: Function, conditions?: Object, options?: FindOptions): Promise<Entity[]>`
Finds entities that match given conditions or given find options.
* `findOne(entityClass: Function, conditions?: Object, options?: FindOptions): Promise<Entity>`
Finds the first entity that match given conditions or given find options.
* `findOneById(entityClass: Function, id: any, options?: FindOptions): Promise<Entity>`
Finds entity with a given entity id.
* `findAndCount(entityClass: Function, conditions?: Object, options?: FindOptions): Promise<[Entity[], number]>`
Finds entities that match given conditions or given find options plus gets a overall count of this items (for
pagination purposes).
* `createQueryBuilder(entityClass: Function, alias: string): QueryBuilder<Entity>`
Creates a new query builder that can be used to build a sql query and get the results of the executed query. You can
learn more about query builder [here](docs/query-builder.md).
* `query(sql: string): Promise<any>`
Executes raw SQL query.
* `transaction(runInTransaction: () => Promise<any>): Promise<any>`
Executes everything in the given function in a single transaction.

View File

@ -1,3 +0,0 @@
## Indices and keys
TBD

View File

@ -1,54 +0,0 @@
## Naming strategies
NamingStrategy is an interface that defines how auto-generated names for such things like table name, or table column
will be named.
#### Interface
By default `DefaultNamingStrategy` is used.
You can implement your own strategies by creating a new class and implementing `NamingStrategy` interface.
There are three methods you need to implement:
* `tableName(className: string): string`
Gets table name from the class name. `DefaultNamingStrategy` transforms className to a sneak-case.
* `columnName(className: string): string`
Gets column name from the class property. `DefaultNamingStrategy` transforms className to a camelCase.
* `relationName(className: string): string`
Gets relation name from the class property. `DefaultNamingStrategy` transforms className to a camelCase.
#### Example
Lets create a simple naming strategy that will add a "_" before names of tables, columns and relations:
```typescript
export class MyNamingStrategy implements NamingStrategy {
tableName(className: string) {
return "_" + className;
}
columnName(propertyName: string) {
return "_" + propertyName;
}
relationName(propertyName: string) {
return "_" + propertyName;
}
}
```
We also need to specify our new naming strategy on connection manager creation:
```typescript
let connectionManager = new ConnectionManager();
connectionManager.namingStrategy = new MyNamingStrategy();
```
Now try to run and generate your database schema. New naming strategy should be used.

View File

@ -1,148 +0,0 @@
## Query Builder
Query Builder allows to build a SQL queries and loads your entities from the database based on the built
query.
#### create a query builder
To create a query builder use [EntityManager](entity-manager.md) or [Repository](repository.md):
```typescript
let repository = connection.getRepository(Photo);
let queryBuilder = repository.createQueryBuilder("photo");
```
First argument of the `createQueryBuilder` is an **alias** of the object you are selecting. Basically its the same
alias as **sql aliases**. You'll need it to make further selections.
#### build a query
There are bunch of methods to help to create a query using QueryBuilder:
* `queryBuilder.select(selection: string)`
* `queryBuilder.select(selection: string[])`
* `queryBuilder.select(...selection: string[])`
* `queryBuilder.addSelect(selection: string)`
* `queryBuilder.addSelect(selection: string[])`
* `queryBuilder.addSelect(...selection: string[])`
Sets / adds given selections to the sql's `SELECT` group. Here you usually select aliases.
* `queryBuilder.where(condition: string, parameters?: { [key: string]: any })`
* `queryBuilder.andWhere(condition: string, parameters?: { [key: string]: any })`
* `queryBuilder.orWhere(condition: string, parameters?: { [key: string]: any })`
Adds sql's `WHERE` condition. For `andWhere` method it will also add "AND" before the condition and for the
`orWhere` method it will also add "OR" before the condition. `parameters` is the array of parameters to be escaped,
just a convenient shortcut for the `QueryBuilder#addParameters` method.
* `queryBuilder.having(condition: string, parameters?: { [key: string]: any })`
* `queryBuilder.andHaving(condition: string, parameters?: { [key: string]: any })`
* `queryBuilder.orHaving(condition: string, parameters?: { [key: string]: any })`
Adds sql's `HAVING` condition. For `andHaving` method it will also add "AND" before the condition and for the
`orHaving` method it will also add "OR" before the condition. `parameters` is the array of parameters to be escaped,
just a convenient shortcut for the `QueryBuilder#addParameters` method.
* `queryBuilder.orderBy(sort: string, order: "ASC"|"DESC" = "ASC")`
* `queryBuilder.addOrderBy(sort: string, order: "ASC"|"DESC" = "ASC")`
Sets / adds sql's `ORDER BY` condition.
* `queryBuilder.groupBy(groupBy: string)`
* `queryBuilder.addGroupBy(groupBy: string)`
Sets / adds sql's `GROUP BY` condition.
* `queryBuilder.setLimit(limit: number)`
Set's sql's `LIMIT`. If you are implementing pagination, LIMIT is not what you want in most of cases, because
of how it works. It can work in some trivial queries, but in complex queries it fails. If you want to use pagination
then use `QueryBuilder#setMaxResults` instead.
* `queryBuilder.setOffset(limit: number)`
Set's sql's `OFFSET`. If you are implementing pagination, OFFSET is not what you want in most of cases, because
of how it works. It can work in some trivial queries, but in complex queries it fails. If you want to use pagination
then use `QueryBuilder#setFirstResult` instead.
* `queryBuilder.setFirstResult(firstResult: number)`
Real "LIMIT" to use in queries for the pagination purposes. Use it if you want pagination.
* `queryBuilder.setMaxResults(maxResults: number)`
Real "LIMIT" to use in queries for the pagination purposes. Use it if you want pagination.
* `setParameter(key: string, value: any)`
* `setParameters(parameters: Object)`
* `addParameters(parameters: Object)`
Sets a single parameter value / set an object of parameters / add all parameters from the object.
Parameters are escaped values to be used in your query, like in WHERE or HAVING expressions.
* `innerJoin(property: string, alias: string, conditionType?: "on"|"with", condition?: string)`
Adds sql's `INNER JOIN` selection on a given **property** of the object, and gives an **alias** to this selection.
You can also specify a SQL JOIN condition type and condition.
* `leftJoin(property: string, alias: string, conditionType?: "on"|"with", condition?: string)`
Adds sql's `LEFT JOIN` selection on a given **property** of the object, and gives an **alias** to this selection.
You can also specify a SQL JOIN condition type and condition.
* `innerJoinAndSelect(property: string, alias: string, conditionType?: "on"|"with", condition?: string)`
Adds sql's `INNER JOIN` selection on a given **property** of the object, and gives an **alias** to this selection.
You can also specify a SQL JOIN condition type and condition. Also adds an alias into `SELECT`.
* `leftJoinAndSelect(property: string, alias: string, conditionType?: "on"|"with", condition?: string)`
Adds sql's `LEFT JOIN` selection on a given **property** of the object, and gives an **alias** to this selection.
You can also specify a SQL JOIN condition type and condition. Also adds an alias into `SELECT`.
These were methods to help you to create a query. Here is example how to use some of them:
```typescript
let photoRepository = connection.getRepository(Photo);
photoRepository
.createQueryBuilder("photo") // first argument is an alias. Alias is what you are selecting - photos. You must specify it.
.innerJoinAndSelect("photo.metadata")
.leftJoinAndSelect("photo.albums")
.where("photo.isPublished=true")
.andWhere("photo.name=:photoName OR photo.name=:bearName")
.orderBy("photo.id", "DESC")
.setFirstResult(5)
.setMaxResults(10)
.setParameters({ photoName: "My", beaName: "Mishka" });
```
There is also a `getSql()` method that can be used to take a look what query QueryBuilder built for you
and debug your queries.
#### get results
There are several methods to help you to get results:
* `getResults()`
Gets all results of the query and transforms results to the your entities.
* `getSingleResult()`
Gets the first result of the query and transforms it to the entity.
* `getScalarResults()`
Gets all results of the query in a raw - it does not transform them to entities.
* `getSingleScalarResult()`
Gets the first result of the query in a raw - it does not transform it to entity.
* `getCount()`
Gets the count - number of rows returned by this selection. Can be used for pagination.
There is also a `execute()` method that simply executes your query and returns you a plain result returned
by the database driver.

View File

@ -1,41 +0,0 @@
## Relations
One of the best sides of TypeORM is relations support. You can build relations between your tables
easily without thinking of database schema and foreign keys. Relations are created using special decorators
on specific fields of entity objects.
* [@OneToOne and @OneToOneInverse decorators](#onetoone-and-onetooneinverse-decorators)
* [@OneToMany and @ManyToOne decorators](#onetomany-and-manytoone-decorators)
* [@ManyToMany and @ManyToManyInverse decorators](#manytomany-and-manytomanyinverse-decorators)
* [Self referencing](#self-referencing)
* [Relational decorators options](#relational-decorators-options)
### @OneToOne and @OneToOneInverse decorators
TBD
### @OneToMany and @ManyToOne decorators
TBD
### @ManyToMany and @ManyToManyInverse decorators
TBD
### Self referencing
TBD
### Relational decorators options
RelationOptions is an object with additional relation options:
* `name?: string` - column name for the relation in the database
* `cascadeInsert?: boolean` - allow cascade insert operations or not. If you set this to true, then any entity that
is new (means does not exist in the database) on the relation will be persisted too.
* `cascadeUpdate?: boolean` - allow cascade update operations or not. If you set this to true, then related entity
will be updated in the database if it was changed.
* `cascadeRemove?: boolean` - allow cascade remove operations or not. If you set this to true, then related entity
will be removed from the database if it was removed from this object.
* `onDelete?: string` - Database-level operation `ON DELETE` is an action that must be performed when related row in
the database has been removed.

View File

@ -1,67 +0,0 @@
## Repository
For each entity you have there is a Repository for it.
Repository provides functionality to work with your entity.
Repository works the same way as EntityManager, but is more specific for concrete entity class.
There are several useful methods of the Repository:
* `hasId(entity: Entity): boolean`
Sometimes you want to check if your entity already has id or not. This maybe useful in situations when you want to
check if your object is new or not.
* `create(plainJsObject?: Object): Entity`
Creates a new empty instance of entity. If `plainJsObject` is given then new entity will be created and all properties
from the `plainJsObject` that can be mapped to this entity will be copied to the new entity.
* `createMany(plainJsObjects: Object[]): Entity[]`
Creates multiple new entities based on array of plain javascript objects. Properties for each plain javascript object
will be copied to newly created entities if they should exist there.
* `initialize(object: Object): Promise<Entity>`
Creates a new entity from the given plan javascript object. If entity already exist in the database, then
it loads it (and everything related to it), replaces all values with the new ones from the given object
and returns this new entity. This new entity is actually a loaded from the db entity with all properties
replaced from the new object.
* `persist(entity: Entity): Promise<Entity>`
Persists (saves) a given entity in the database. If entity does not exist in the database then it inserts it,
else if entity already exist in the database then it updates it.
* `remove(entity: Entity): Promise<Entity>`
Removes a given entity from the database.
* `find(conditions?: Object, options?: FindOptions): Promise<Entity[]>`
Finds entities that match given conditions or given find options.
* `findOne(conditions?: Object, options?: FindOptions): Promise<Entity>`
Finds the first entity that match given conditions or given find options.
* `findOneById(id: any, options?: FindOptions): Promise<Entity>`
Finds entity with a given entity id.
* `findAndCount(conditions?: Object, options?: FindOptions): Promise<[Entity[], number]>`
Finds entities that match given conditions or given find options plus gets a overall count of this items (for
pagination purposes).
* `createQueryBuilder(alias: string): QueryBuilder<Entity>`
Creates a new query builder that can be used to build a sql query and get the results of the executed query. You can
learn more about query builder [here](docs/query-builder.md).
* `query(sql: string): Promise<any>`
Executes raw SQL query.
* `transaction(runInTransaction: () => Promise<any>): Promise<any>`
Executes everything in the given function in a single transaction.

View File

@ -1,127 +0,0 @@
## Subscribers and Entity Listeners
You can listen to events in the ORM. There two concepts you can use:
* [Subscribers](#subscribers)
* [Entity Listeners](#entity-listeners)
### Subscribers
First you need to create a new subscriber class and implement `EventSubscriberInterface` interface:
```typescript
import {EventSubscriber, UpdateEvent, RemoveEvent, InsertEvent} from "typeorm/listeners"
import {EventSubscriberInterface} from "typeorm";
@EventSubscriber()
export class MySubscriber implements EventSubscriberInterface<any> {
/**
* Called after entity insertion.
*/
beforeInsert(event: InsertEvent<any>) {
console.log(`BEFORE ENTITY INSERTED: `, event.entity);
}
/**
* Called after entity insertion.
*/
beforeUpdate(event: UpdateEvent<any>) {
console.log(`BEFORE ENTITY UPDATED: `, event.entity);
}
/**
* Called after entity insertion.
*/
beforeRemove(event: RemoveEvent<any>) {
console.log(`BEFORE ENTITY WITH ID ${event.entityId} REMOVED: `, event.entity);
}
/**
* Called after entity insertion.
*/
afterInsert(event: InsertEvent<any>) {
console.log(`AFTER ENTITY INSERTED: `, event.entity);
}
/**
* Called after entity insertion.
*/
afterUpdate(event: UpdateEvent<any>) {
console.log(`AFTER ENTITY UPDATED: `, event.entity);
}
/**
* Called after entity insertion.
*/
afterRemove(event: RemoveEvent<any>) {
console.log(`AFTER ENTITY WITH ID ${event.entityId} REMOVED: `, event.entity);
}
/**
* Called after entity is loaded.
*/
afterLoad(entity: any) {
console.log(`AFTER ENTITY LOADED: `, entity);
}
}
```
To register a subscriber you need to register it:
```typescript
connectionManager.importSubscribers([MySubscriber]);
```
### Entity Listeners
You can also use listeners in your entities. Such listeners can be convenient for a trivial operations.
```typescript
import {Table} from "typeorm/tables";
import {AfterLoad, AfterInsert, BeforeInsert, BeforeUpdate, AfterUpdate, BeforeRemove, AfterRemove} from "typeorm/listeners";
@Table("posts")
export class Post {
// ... columns ...
@AfterLoad()
generateRandomNumbers() {
console.log(`event: Post entity has been loaded and callback executed`);
}
@BeforeInsert()
doSomethingBeforeInsertion() {
console.log("event: Post entity will be inserted so soon...");
}
@AfterInsert()
doSomethingAfterInsertion() {
console.log("event: Post entity has been inserted and callback executed");
}
@BeforeUpdate()
doSomethingBeforeUpdate() {
console.log("event: Post entity will be updated so soon...");
}
@AfterUpdate()
doSomethingAfterUpdate() {
console.log("event: Post entity has been updated and callback executed");
}
@BeforeRemove()
doSomethingBeforeRemove() {
console.log("event: Post entity will be removed so soon...");
}
@AfterRemove()
doSomethingAfterRemove() {
console.log("event: Post entity has been removed and callback executed");
}
}
```

View File

@ -1,173 +0,0 @@
## Table columns
Entity consist of columns. For each entity column, column in the database will be created.
* [@Column decorator](#@column)
* [@PrimaryColumn decorator](#@primary-column)
* [@CreateDateColumn decorator](#@create-date-column)
* [@UpdateDateColumn decorator](#@update-date-column)
* [Column types](#column-type)
* [Column options](#column-options)
* [Columns usage example](#example)
#### @Column
Column decorator simply marks an entity property to be a table column.
There are several column decorator signatures:
```typescript
@Column(options?: ColumnOptions)
@Column(type?: ColumnType, options?: ColumnOptions)
```
#### @PrimaryColumn
PrimaryColumn marks an entity property as a column and creates a primary key for it.
There are several column decorator signatures:
```typescript
@PrimaryColumn(options?: ColumnOptions)
@PrimaryColumn(type?: ColumnType, options?: ColumnOptions)
```
#### @CreateDateColumn
CreateDateColumn adds a simple datetime column to the table. During its first persistence (e.g. insertion) it
sets current date as a value of the property object.
```typescript
@CreateDateColumn(options?: ColumnOptions)
```
#### @UpdateDateColumn
UpdateDateColumn adds a simple datetime column to the table. Each time object is persisted, this column value is updated
to the current date.
```typescript
@CreateDateColumn(options?: ColumnOptions)
```
#### ColumnType
ColumnType can be one of:
* `string` will be mapped to db's `varchar`
* `text` will be mapped to db's `text`
* `number` will be mapped to db's `double`
* `integer` will be mapped to db's `int`
* `int` will be mapped to db's `int`
* `smallint` will be mapped to db's `int`
* `bigint` will be mapped to db's `int`
* `float` will be mapped to db's `float`
* `double` will be mapped to db's `double`
* `decimal` will be mapped to db's `decimal`
* `date` will be mapped to db's `datetime`
* `time` will be mapped to db's `time`
* `datetime` will be mapped to db's `datetime`
* `timestamp` will be mapped to db's `timestamp`
* `boolean` will be mapped to db's `boolean`
* `json` will be mapped to db's `text`
* `simple_array` will be mapped to db's `text`
If you omit a column type, type will be guessed automatically based on variable type:
* `number` will be mapped to `float`
* `boolean` will be mapped to `boolean`
* `string` will be mapped to `varchar`
* `Date` will be mapped to `datetime`
#### ColumnOptions
ColumnOptions is an object with additional column options:
* `name?: string` - column name in the database
* `type?: ColumnType` - column type also can be specified via column options
* `length?: string` - column type's length. For example type = "string" and length = 100 means that ORM will create a
column with type varchar(100).
* `generated?: boolean` - specifies if this column will use AUTO_INCREMENT or not (e.g. generated number)
* `unique?: boolean` - specifies if column's value must be unique or not.
* `nullable?: boolean` - indicates if column's value can be set to NULL.
* `columnDefinition?: string` - Extra column definition. Should be used only in emergency situations.
Note that if you'll use this property auto schema generation will not work properly anymore.
* `comment?: string` - column comment
* `precision?: number` - The precision for a decimal (exact numeric) column (applies only for decimal column), which is the maximum
number of digits that are stored for the values.
* `scale?: number` - The scale for a decimal (exact numeric) column (applies only for decimal column), which represents the number
of digits to the right of the decimal point and must not be greater than precision.
* `collation?: string` - Column collation. Note that not all databases support it.
#### Example
```typescript
@Table("photo")
class Photo {
/**
* Primary column with auto increment key.
*/
@PrimaryColumn("int", { generated: true })
id: number;
/**
* Simple string column.
*/
@Column()
name: string;
/**
* Simple boolean column.
*/
@Column()
isPublished: boolean;
/**
* Simple numeric (float) column.
*/
@Column()
scale: number;
/**
* Simple numeric (integer) column.
*/
@Column("integer")
size: number;
/**
* Simple column that contains a date.
*/
@Column()
publishedDate: Date;
/**
* Simple column that contains a big text.
*/
@Column("text")
description: string;
/**
* Simple column that contains a short text.
*/
@Column({
length: 3
})
locale: string;
/**
* This column's value must be unique.
*/
@Column({
unique: true
})
slug: string;
/**
* This column's value can be nullable.
*/
@Column({
nullable: true
})
metadata: string;
}
```

View File

@ -1,57 +0,0 @@
## Tables and table inheritance
### Tables
ORM creates tables for each class that you decorated with `@Table` decorator and which you loaded into your
connection.
```typescript
@Table("photos")
export class Photo {
}
```
Each table must have a primary column (using `@PrimaryColumn` decotator) [*todo: really?*] and can contain
other [columns](table-columns.md).
### Table inheritance
If multiple tables has same properties you may want to find them a common abstraction and create a base
class for them. In this base class you'll have a common for all inherited classes columns. To ahieve this you
must mark your table with `@AbstractTable()` decorator:
```typescript
@AbstractTable()
export class BasePhoto {
@PrimaryColumn("int", { generated: true })
id: string;
@Column()
name: string;
@Column()
url: string;
}
@Table("public_photos")
export class Photo extends BasePhoto {
@Column()
authorName: string;
}
@Table("private_photos")
export class Photo extends BasePhoto {
@Column()
isPublished: boolean;
}
```
This will create you two tables `public_photos` and `private_photos` with 5 columns each.
Right now only columns are inherited. Relations are ignored.

View File

@ -1,17 +0,0 @@
## Updating database schema
Your database schema is managed automatically by ORM:
* tables are created for all entities
* columns are created for all entity columns
* foreign keys are set for all relations
* junction tables are created for all many-to-many relations
All this must be in sync to make ORM to work correctly. To make a synchronization there are two ways:
* set in [connection options](connection-and-connection-options.md#connection-options) `autoSchemaSync: true`.
In this case database schema will be automatically synchronized each time you run the application.
* use [schema update gulp plugin](todo) and run schema synchronization process each time you need it.
First approach is not recommended to use in production, however it can be handy during development.

View File

@ -164,7 +164,7 @@ export class Gulpfile {
*/
@Task()
coveragePre() {
return gulp.src(["./build/es5/src/**/*.js"])
return gulp.src(["./build/compiled/src/**/*.js"])
.pipe(istanbul())
.pipe(istanbul.hookRequire());
}
@ -178,7 +178,7 @@ export class Gulpfile {
chai.use(require("sinon-chai"));
chai.use(require("chai-as-promised"));
return gulp.src(["./build/es5/test/**/*.js"])
return gulp.src(["./build/compiled/test/**/*.js"])
.pipe(mocha())
.pipe(istanbul.writeReports());
}

14
ormconfig.json.dist Normal file
View File

@ -0,0 +1,14 @@
[
{
"name": "default",
"driver": {
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "admin",
"database": "test"
},
"autoSchemaSync": true
}
]

View File

@ -3,7 +3,7 @@
"private": true,
"version": "0.0.2-alpha.67",
"description": "Data-mapper ORM for Typescript",
"license": "Apache-2.0",
"license": "MIT",
"readmeFilename": "README.md",
"author": {
"name": "Umed Khudoiberdiev",

View File

@ -10,9 +10,9 @@ import {PostAuthor} from "./entity/PostAuthor";
const options: ConnectionOptions = {
driver: {
type: "postgres",
type: "mysql",
host: "localhost",
port: 5432,
port: 3306,
username: "root",
password: "admin",
database: "test"

View File

@ -1,8 +1,8 @@
{
"version": "2.0.2",
"version": "2.0.3",
"compilerOptions": {
"lib": ["es5", "es6", "dom"],
"outDir": "build/es5",
"outDir": "build/compiled",
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
@ -18,6 +18,8 @@
"strictNullChecks": true
},
"exclude": [
"tmp",
"temp",
"build",
"node_modules"
]