Merge branch 'master' into next

* master: (57 commits)
  update typescript to 2.7
  removing repeat types at postgres and sqlite
  Prevent transforms breaking on datetime types (closes #1140)
  fixing repeating column types for mysql / mariadb
  loose -> lose
  loose -> loss
  loose -> lose
  Updated the hidden column to reflect the query
  version bump
  Fix typo in connection-options.md
  Fixed typo in mongodb.md
  Update support.md
  Update support.md
  Update roadmap.md
  Update decorator-reference.md
  Update faq.md
  Update sequelize-migration.md
  Update example-with-express.md
  Update active-record-data-mapper.md
  Update using-cli.md
  ...

# Conflicts:
#	CHANGELOG.md
#	README.md
#	docs/relations.md
#	docs/roadmap.md
#	docs/tree-entities.md
#	package-lock.json
#	package.json
#	src/query-builder/UpdateQueryBuilder.ts
#	test/functional/entity-metadata-validator/basic/entity-metadata-validator.ts
#	test/functional/sqljs/auto-save.ts
This commit is contained in:
Umed Khudoiberdiev 2018-02-06 12:17:33 +05:00
commit 8ef1695311
61 changed files with 1442 additions and 866 deletions

View File

@ -55,9 +55,14 @@ By default its true.
* `@ClosureEntity` decorator has been removed. Instead `@Entity` + `@Tree("closure-table")` must be used
* added support for nested set and materialized path tree hierarchy patterns
## 0.1.13
* added simple-json column type ([#1448](https://github.com/typeorm/typeorm/pull/1488))
* fixed transform behaviour for timestamp columns ([#1140](https://github.com/typeorm/typeorm/issues/1140))
## 0.1.12
* EntitySubscriber now fires events on subclass entity ([#1369](https://github.com/typeorm/typeorm/issues/1369))
* fixed error with entity schema validator being async ([#1448](https://github.com/typeorm/typeorm/issues/1448))
## 0.1.11

34
ISSUE_TEMPLATE.md Normal file
View File

@ -0,0 +1,34 @@
**Issue type:**
[ ] question
[ ] bug report
[ ] feature request
[ ] documentation issue
**Database system/driver:**
[ ] `cordova`
[ ] `mongodb`
[ ] `mssql`
[ ] `mysql` / `mariadb`
[ ] `oracle`
[ ] `postgres`
[ ] `sqlite`
[ ] `sqljs`
[ ] `websql`
**TypeORM version:**
[ ] `latest`
[ ] `@next`
[ ] `0.x.x` (or put your version here)
**Steps to reproduce or a small repository showing the problem:**
<!--
To answer those questions you need to put "x" inside the square brackets, for example:
[x] `mysql`
[ ] `postgres`
Also, please format your code properly (by taking code blocks into ```ts .... ```)
!>

View File

@ -309,15 +309,16 @@ creating more entities.
## Step-by-Step Guide
What are you expecting from ORM?
First of all you are expecting it will create a database tables for you
and find / insert / update / delete your data without pain and having to write lot of hardly maintainable SQL queries.
This guide will show you how to setup TypeORM from scratch and make it to do what you are expecting from ORM.
First of all, you are expecting it will create database tables for you
and find / insert / update / delete your data without the pain of
having to write lots of hardly maintainable SQL queries.
This guide will show you how to setup TypeORM from scratch and make it do what you are expecting from ORM.
### Create a model
Working with database starts from creating a tables.
Working with database starts from creating tables.
How do you tell TypeORM to create a database table?
Answer is - thought the models.
Answer is - through the models.
Your models in your app - are your database tables.
For example, you have a `Photo` model:
@ -333,14 +334,14 @@ export class Photo {
```
And you want to store photos in your database.
To store things in the database first you need a database table.
And database tables are created from your models.
Not all models, but only those you defined as *entities*.
To store things in the database, first you need a database table,
and database tables are created from your models.
Not all models, but only those you define as *entities*.
### Create an entity
*Entity* is your model decorated by `@Entity` decorator.
Database table will be created for such model.
*Entity* is your model decorated by an `@Entity` decorator.
A database table will be created for such models.
You work with entities everywhere with TypeORM.
You can load/insert/update/remove and perform other operations with them.
@ -360,13 +361,13 @@ export class Photo {
}
```
Now database table will be created for `Photo` entity and we'll be able to work with it anywhere in our app.
Now, a database table will be created for the `Photo` entity and we'll be able to work with it anywhere in our app.
We have created a database table, however what table can exist without columns?
Let's create a few columns in our database table.
### Adding table columns
To add database columns you simply need to decorate entity's properties you want to make a columns
To add database columns, you simply need to decorate an entity's properties you want to make into a column
with a `@Column` decorator.
```typescript
@ -398,17 +399,16 @@ export class Photo {
Now `id`, `name`, `description`, `filename`, `views` and `isPublished` columns will be added to the `photo` table.
Column types in the database are inferred from the property types you used, e.g.
`number` will be converted into `integer`, `string` into `varchar`, `boolean` into `bool`, etc.
But you can use any column type your database support by implicitly specify a column type into `@Column` decorator.
But you can use any column type your database supports by implicitly specifying a column type into the `@Column` decorator.
We generated a database table with columns.
But there is one thing left.
Each database table must have a column with primary key.
We generated a database table with columns, but there is one thing left.
Each database table must have a column with a primary key.
### Creating a primary column
Each entity **must** have at least one primary column.
This is requirement and you can't avoid it.
To make a column a primary you need to use `@PrimaryColumn` decorator.
Each entity **must** have at least one primary key column.
This is a requirement and you can't avoid it.
To make a column a primary key, you need to use `@PrimaryColumn` decorator.
```typescript
import {Entity, Column, PrimaryColumn} from "typeorm";
@ -439,7 +439,7 @@ export class Photo {
### Creating an auto generated column
Now, let's say you want your id column to be auto-generated (this is known as auto-increment / sequence / serial / generated identity column).
To do that you need to change `@PrimaryColumn` decorator to `@PrimaryGeneratedColumn` decorator:
To do that, you need to change the `@PrimaryColumn` decorator to a `@PrimaryGeneratedColumn` decorator:
```typescript
import {Entity, Column, PrimaryGeneratedColumn} from "typeorm";
@ -503,8 +503,8 @@ export class Photo {
```
Column types are database-specific.
You can set any column type your database support.
More information on supported column types you can find [here](./docs/entities.md#column-types).
You can set any column type your database supports.
More information on supported column types can be found [here](./docs/entities.md#column-types).
### Creating a connection to the database
@ -618,8 +618,8 @@ createConnection(/*...*/).then(connection => {
```
Once your entity is saved it will get a newly generated id.
`save` method returns instance of same object you pass to it.
Its not a new copy of an object, it modifies its "id" and returns it.
`save` method returns an instance of the same object you pass to it.
It's not a new copy of the object, it modifies its "id" and returns it.
### Using async/await syntax
@ -809,10 +809,10 @@ export class PhotoMetadata {
}
```
Here, we are used a new decorator called `@OneToOne`. It allows us to create a one-to-one relation between two entities.
`type => Photo` is a function that returns the class of the entity with which we want to make our relation.
We are forced to use a function that returns a class, instead of using class directly, because of the language specifics.
We can also write it as a `() => Photo`, but we use `type => Photo` as convention to increase code readability.
Here, we are using a new decorator called `@OneToOne`. It allows us to create a one-to-one relationship between two entities.
`type => Photo` is a function that returns the class of the entity with which we want to make our relationship.
We are forced to use a function that returns a class, instead of using the class directly, because of the language specifics.
We can also write it as `() => Photo`, but we use `type => Photo` as a convention to increase code readability.
The type variable itself does not contain anything.
We also add a `@JoinColumn` decorator, which indicates that this side of the relationship will own the relationship.
@ -947,11 +947,11 @@ createConnection(/*...*/).then(async connection => {
}).catch(error => console.log(error));
```
Here photos will contain an array of photos from the database, and each photo will contain its photo metadata.
Here, photos will contain an array of photos from the database, and each photo will contain its photo metadata.
Learn more about Find Options in [this documentation](./docs/find-options.md).
Using find options is good and dead simple, but if you need more complex query you should use `QueryBuilder` instead.
`QueryBuilder` allows to use more complex queries in an elegant way:
Using find options is good and dead simple, but if you need a more complex query, you should use `QueryBuilder` instead.
`QueryBuilder` allows more complex queries to be used in an elegant way:
```typescript
import {createConnection} from "typeorm";
@ -971,9 +971,9 @@ createConnection(/*...*/).then(async connection => {
}).catch(error => console.log(error));
```
`QueryBuilder` allows to create and execute SQL query of almost any complexity.
When you work with `QueryBuilder` think like you are creating SQL query.
In this example "photo" and "metadata" are aliases applied to selected photos.
`QueryBuilder` allows creation and execution of SQL queries of almost any complexity.
When you work with `QueryBuilder`, think like you are creating an SQL query.
In this example, "photo" and "metadata" are aliases applied to selected photos.
You use aliases to access columns and properties of the selected data.
### Using cascades to automatically save related objects
@ -992,7 +992,7 @@ export class Photo {
}
```
Using `cascade` allows us not to separately save photo and separately save metadata objects now.
Using `cascade` allows us not to separately save photo and separately save metadata objects now.
Now we can simply save a photo object, and the metadata object will be saved automatically because of cascade options.
```typescript
@ -1206,7 +1206,7 @@ const loadedPhoto = await connection
### Using QueryBuilder
You can use QueryBuilder to build SQL query of almost any complexity. For example, you can do this:
You can use QueryBuilder to build SQL queries of almost any complexity. For example, you can do this:
```typescript
let photos = await connection

View File

@ -6,11 +6,11 @@
## What is the Active Record pattern?
In TypeORM you can use both, the Active Record and the Data Mapper patterns.
In TypeORM you can use both the Active Record and the Data Mapper patterns.
Using the Active Record approach, you define all your query methods inside the model itself, and you save, remove and load objects using model methods.
Using the Active Record approach, you define all your query methods inside the model itself, and you save, remove, and load objects using model methods.
Simply said the Active Record pattern is an approach to access your database within your models.
Simply said, the Active Record pattern is an approach to access your database within your models.
You can read more about the Active Record pattern on [Wikipedia](https://en.wikipedia.org/wiki/Active_record_pattern).
Example:
@ -36,8 +36,8 @@ export class User extends BaseEntity {
}
```
All active-record entities must extend the `BaseEntity` class which provides methods to work with the entity.
Example how to work with such entity:
All active-record entities must extend the `BaseEntity` class, which provides methods to work with the entity.
Example of how to work with such entity:
```typescript
@ -57,11 +57,11 @@ const newUsers = await User.find({ isActive: true });
const timber = await User.findOne({ firstName: "Timber", lastName: "Saw" });
```
`BaseEntity` has most of methods standard `Repository` has.
Most of the times you don't need to use `Repository` or `EntityManager` with active record entities.
`BaseEntity` has most of the methods of the standard `Repository`.
Most of the time you don't need to use `Repository` or `EntityManager` with active record entities.
Now let's say we want to create a function that returns users by first and last names.
We can create such function as a static method in a `User` class:
Now let's say we want to create a function that returns users by first and last name.
We can create such functions as a static method in a `User` class:
```typescript
import {BaseEntity, Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@ -99,13 +99,13 @@ const timber = await User.findByName("Timber", "Saw");
## What is the Data Mapper pattern?
In TypeORM you can use both Active Record and Data Mapper patterns.
In TypeORM you can use both the Active Record and Data Mapper patterns.
Using the Data Mapper, approach you define all your query methods separate classes called "repositories",
and you save, remove, load objects using repositories.
Using the Data Mapper approach, you define all your query methods in separate classes called "repositories",
and you save, remove, and load objects using repositories.
In data mapper your entities are very dumb - they just define their properties and may have some "dummy" methods.
Simply said data mapper is an approach to access your database within repositories instead of models.
Simply said, data mapper is an approach to access your database within repositories instead of models.
You can read more about data mapper on [Wikipedia](https://en.wikipedia.org/wiki/Data_mapper_pattern).
Example:
@ -130,7 +130,7 @@ export class User {
}
```
Example how to work with such entity:
Example of how to work with such entity:
```typescript
const userRepository = connection.getRepository(User);
@ -151,8 +151,8 @@ const newUsers = await userRepository.find({ isActive: true });
const timber = await userRepository.findOne({ firstName: "Timber", lastName: "Saw" });
```
Now let's say we want to create a function that returns users by first and last names.
We can create such function in a "custom repository".
Now let's say we want to create a function that returns users by first and last name.
We can create such a function in a "custom repository".
```typescript
import {EntityRepository, Repository} from "typeorm";
@ -186,6 +186,6 @@ The decision is up to you.
Both strategies have their own cons and pros.
One thing we should always keep in mind in software development is how we are going to maintain it.
The `Data Mapper` approach helps you to keep maintainability of your software which is more effective in bigger apps.
The `Active record` approach helps you to keep things simple which work good in a small apps.
And simplicity is always a key to better maintainability.
The `Data Mapper` approach helps you with maintainability of your software which is more effective in bigger apps.
The `Active record` approach helps you to keep things simple which works good in small apps.
And simplicity is always a key to better maintainability.

View File

@ -39,12 +39,12 @@ const users = await connection
});
```
This will execute a query to fetch all admin users and cache its result.
Next time when you execute same code it will get admin users from the cache.
Default cache time is equal to `1000 ms`, e.g. 1 second.
This means cache will be invalid 1 second after you called the query builder code.
In practice, it means that if users open user page 150 times within 3 seconds only three queries will be executed during this period.
All users inserted during the 1 second of caching won't be returned to the user.
This will execute a query to fetch all admin users and cache the results.
Next time you execute the same code, it will get all admin users from the cache.
Default cache lifetime is equal to `1000 ms`, e.g. 1 second.
This means the cache will be invalid 1 second after the query builder code is called.
In practice, this means that if users open the user page 150 times within 3 seconds, only three queries will be executed during this period.
Any users inserted during the 1 second cache window won't be returned to the user.
You can change cache time manually via `QueryBuilder`:
@ -104,8 +104,8 @@ const users = await connection
});
```
It will allow you to granular control your cache,
for example, clear cached results when you insert a new user:
This gives you granular control of your cache,
for example, clearing cached results when you insert a new user:
```typescript
await connection.queryResultCache.remove(["users_admins"]);

View File

@ -77,7 +77,7 @@ Be careful with this option and don't use this in production - otherwise you'll
This option is useful during debug and development.
* `synchronize` - Indicates if database schema should be auto created on every application launch.
Be careful with this option and don't use this in production - otherwise you can loose production data.
Be careful with this option and don't use this in production - otherwise you can lose production data.
This option is useful during debug and development.
As an alternative to it, you can use CLI and run schema:sync command.
Note that for MongoDB database it does not create schema, because MongoDB is schemaless.
@ -155,7 +155,7 @@ See [SSL options](https://github.com/mysqljs/mysql#ssl-options).
* `host` - Database host.
* `port` - Database host port. Default mysql port is `5432`.
* `port` - Database host port. Default postgres port is `5432`.
* `username` - Database username.

View File

@ -54,7 +54,7 @@ and you can access any method created inside it and any method in the standard e
## Custom repository extends standard AbstractRepository
Second way to create a custom repository is to extend `AbstractRepository`:
The second way to create a custom repository is to extend `AbstractRepository`:
```typescript
import {EntityRepository, AbstractRepository} from "typeorm";
@ -88,15 +88,15 @@ await userRepository.createAndSave("Timber", "Saw");
const timber = await userRepository.findByName("Timber", "Saw");
```
The difference between this type of repository and the previous one is, that it does not expose all methods `Repository` has.
The difference between this type of repository and the previous one is that it does not expose all the methods `Repository` has.
`AbstractRepository` does not have any public methods,
it only has protected methods like `manager` and `repository` which you can use in your own
it only has protected methods, like `manager` and `repository`, which you can use in your own
public methods.
Extending `AbstractRepository` is useful if you don't want to expose all methods the standard `Repository` has to the public.
## Custom repository without extends
Third way to create a repository is to not extend anything,
The third way to create a repository is to not extend anything,
but define a constructor which always accepts an entity manager instance:
```typescript
@ -136,13 +136,13 @@ const timber = await userRepository.findByName("Timber", "Saw");
This type of repository does not extend anything - you only need to define a constructor
which must accept `EntityManager`. Then you can use it everywhere in your repository methods.
Also this type of repository is not bound to a specific entity.
Thus you can operate with multiple entities inside them.
Also, this type of repository is not bound to a specific entity,
thus, you can operate with multiple entities inside them.
## Using custom repositories in transactions or why custom repositories cannot be services
Custom repositories cannot be services.
Because there isn't a single instance of a custom repository (just like regular repositories or entity manager) in the app.
Custom repositories cannot be services,
because there isn't a single instance of a custom repository (just like regular repositories or entity manager) in the app.
Besides the fact that there can be multiple connections in your app (where entity manager and repositories are different)
repositories and managers are different in transactions as well.
For example:

View File

@ -49,12 +49,12 @@ This code will create a database table named "users".
You can also specify some additional entity options:
* `name` - table name. If not specified then table name is generated from entity class name
* `database` - database name in selected DB server
* `schema` - schema name
* `engine` - database engine to be set during table creation (works only in some databases)
* `skipSync` - entities marked with this decorator are skipped from schema updates
* `orderBy` - specifies default ordering for entities when using `find` operations and `QueryBuilder`
* `name` - table name. If not specified, then table name is generated from entity class name.
* `database` - database name in selected DB server.
* `schema` - schema name.
* `engine` - database engine to be set during table creation (works only in some databases).
* `skipSync` - entities marked with this decorator are skipped from schema updates.
* `orderBy` - specifies default ordering for entities when using `find` operations and `QueryBuilder`.
Example:
@ -106,13 +106,13 @@ export class User {
* `type: ColumnType` - Column type. One of the [supported column types](entities.md#column-types).
* `name: string` - Column name in the database table.
By default the column name is generated from the name of the property.
You can change it by specifying your own name
* `length: string|number` - Column type's length. For example if you want to create `varchar(150)` type
You can change it by specifying your own name.
* `length: string|number` - Column type's length. For example, if you want to create `varchar(150)` type
you specify column type and length options.
* `nullable: boolean` - Makes column `NULL` or `NOT NULL` in the database.
By default column is `nullable: false`.
* `default: string` - Adds database-level column's `DEFAULT` value.
* `primary: boolean` - Marks column as primary. Same if you use `@PrimaryColumn`.
* `primary: boolean` - Marks column as primary. Same as using `@PrimaryColumn`.
* `unique: boolean` - Marks column as unique column (creates unique constraint).
* `comment: string` - Database's column comment. Not supported by all database types.
* `precision: number` - The precision for a decimal (exact numeric) column (applies only for decimal column), which is the maximum
@ -124,7 +124,7 @@ Used in some column types.
* `collation: string` - Defines a column collation.
* `enum: string[]|AnyEnum` - Used in `enum` column type to specify list of allowed enum values.
You can specify array of values or specify a enum class.
* `array: boolean` - Used for postgres column types which can be array (for example int[])
* `array: boolean` - Used for postgres column types which can be array (for example int[]).
Learn more about [entity columns](entities.md#entity-columns).
@ -148,7 +148,7 @@ Learn more about [entity columns](entities.md#entity-columns).
#### `@PrimaryGeneratedColumn`
Marks a property in your entity as a table generated primary column.
Marks a property in your entity as a table-generated primary column.
Column it creates is primary and its value is auto-generated.
Example:
@ -164,10 +164,10 @@ export class User {
There are two generation strategies:
* `increment` - uses AUTO_INCREMENT / SERIAL / SEQUENCE (depend on database type) to generate incremental number
* `uuid` - generates unique `uuid` string
* `increment` - uses AUTO_INCREMENT / SERIAL / SEQUENCE (depend on database type) to generate incremental number.
* `uuid` - generates unique `uuid` string.
Default generation strategy is `increment`, to change it to `uuid` simple pass it as first argument to decorator:
Default generation strategy is `increment`, to change it to `uuid`, simply pass it as the first argument to decorator:
```typescript
@Entity()
@ -219,7 +219,7 @@ export class User {
#### `@UpdateDateColumn`
Special column that is automatically set to the entity's update time
each time you call `save` of entity manager or repository.
each time you call `save` from entity manager or repository.
You don't need to write a value into this column - it will be automatically set.
```typescript
@ -235,7 +235,7 @@ export class User {
#### `@VersionColumn`
Special column that is automatically set to the entity's version (incremental number)
each time you call `save` of entity manager or repository.
each time you call `save` from entity manager or repository.
You don't need to write a value into this column - it will be automatically set.
```typescript
@ -250,7 +250,7 @@ export class User {
#### `@Generated`
Marks column to have a generated value. For example:
Marks column to be a generated value. For example:
```typescript
@Entity()
@ -263,7 +263,7 @@ export class User {
}
```
Value will be generated only once before inserting the entity into the database.
Value will be generated only once, before inserting the entity into the database.
## Relation decorators
@ -380,8 +380,8 @@ Learn more about [many-to-many relations](many-to-many-relations.md).
#### `@JoinColumn`
Defines which side of the relation contains join column with foreign key and
allows to customize join column name and referenced column name.
Defines which side of the relation contains the join column with a foreign key and
allows you to customize the join column name and referenced column name.
Example:
```typescript
@ -401,8 +401,8 @@ export class Post {
#### `@JoinTable`
Used for `many-to-many` relations and describes join columns of the "junction" table.
Junction table is a special separate table created automatically by TypeORM with columns referenced to the related entities.
You can change the column names inside the junction table and their referenced columns as easy as with `@JoinColumn` decorator. You can also change the name of the generated "junction" table.
Junction table is a special, separate table created automatically by TypeORM with columns referenced to the related entities.
You can change the column names inside the junction table and their referenced columns with the `@JoinColumn` decorator. You can also change the name of the generated "junction" table.
Example:
```typescript
@ -427,13 +427,13 @@ export class Post {
```
If the destination table has composite primary keys,
then array of properties must be send to `@JoinTable` decorator.
then an array of properties must be sent to the `@JoinTable` decorator.
#### `@RelationId`
Loads id (or ids) of specific relation into property.
For example if you have many-to-one `category` in your `Post` entity
you can have category id by marking a new property with `@RelationId`.
Loads id (or ids) of specific relations into properties.
For example, if you have a many-to-one `category` in your `Post` entity,
you can have a new category id by marking a new property with `@RelationId`.
Example:
```typescript
@ -449,7 +449,7 @@ export class Post {
}
```
This functionality works for all kind of relations including `many-to-many`:
This functionality works for all kind of relations, including `many-to-many`:
```typescript
@Entity()
@ -465,7 +465,7 @@ export class Post {
```
Relation id is used only for representation.
The underlying relation is not added/removed/changed when chaning the value.
The underlying relation is not added/removed/changed when chaining the value.
## Subscriber and listener decorators
@ -605,8 +605,8 @@ Learn more about [listeners](listeners-and-subscribers.md).
#### `@EventSubscriber`
Marks a class as an event subscriber which can listen to specific entity events or any entity events.
Events are firing using `QueryBuilder` and repository/manager methods.
Marks a class as an event subscriber which can listen to specific entity events or any entity's events.
Events are fired using `QueryBuilder` and repository/manager methods.
Example:
```typescript
@ -632,7 +632,7 @@ export class PostSubscriber implements EntitySubscriberInterface<Post> {
```
You can implement any method from `EntitySubscriberInterface`.
To listen to any entity you just omit `listenTo` method and use `any`:
To listen to any entity, you just omit the `listenTo` method and use `any`:
```typescript
@EventSubscriber()
@ -654,11 +654,11 @@ Learn more about [subscribers](listeners-and-subscribers.md).
#### `@Index`
This decorator allows to create database index for a specific column or columns.
It also allows to mark column or columns to be unique.
Decorator can be applied to columns or entity itself.
Use it on a column when index on a single column is needed.
And use it on the entity when a single index on multiple columns is required.
This decorator allows you to create a database index for a specific column or columns.
It also allows you to mark column or columns to be unique.
This decorator can be applied to columns or an entity itself.
Use it on a column when an index on a single column is needed
and use it on the entity when a single index on multiple columns is required.
Examples:
```typescript
@ -697,7 +697,7 @@ Learn more about [indices](indices.md).
#### `@Transaction`, `@TransactionManager` and `@TransactionRepository`
`@Transaction` is used on a method and wraps all its execution into a single database transaction.
All database queries must be performed using the by `@TransactionManager` provided manager
All database queries must be performed using the `@TransactionManager` provided manager
or with the transaction repositories injected with `@TransactionRepository`.
Examples:
@ -730,7 +730,7 @@ Learn more about [transactions](transactions.md).
#### `@EntityRepository`
Marks custom class as entity repository.
Marks a custom class as an entity repository.
Example:
```typescript
@ -751,4 +751,4 @@ Learn more about [custom entity repositories](working-with-entity-manager.md).
Note: some decorators (like `@Tree`, `@ChildEntity`, etc.) aren't
documented in this reference because they are treated as experimental at the moment.
Expect to see their documentation in the future.
Expect to see their documentation in the future.

View File

@ -10,6 +10,7 @@
* [Column types for `sqlite` / `websql`](#column-types-for-sqlite--websql--cordova)
* [Column types for `mssql`](#column-types-for-mssql)
* [`simple-array` column type](#simple-array-column-type)
* [`simple-json` column type](#simple-json-column-type)
* [Columns with generated values](#columns-with-generated-values)
* [Column options](#column-options)
@ -236,34 +237,33 @@ or
### Column types for `mysql` / `mariadb`
`int`, `tinyint`, `smallint`, `mediumint`, `bigint`, `decimal`, `float`, `double`,
`decimal`, `real`, `datetime`, `time`, `timestamp`, `int`, `tinyint`, `smallint`, `mediumint`, `bigint`,
`character`, `varchar`, `char`, `tinyblob`, `tinytext`, `mediumblob`, `mediumtext`, `blob`, `text`,
`longblob`, `longtext`, `date`, `year`, `enum`, `json`
`real`, `datetime`, `time`, `timestamp`, `character`, `varchar`, `char`, `tinyblob`,
`tinytext`, `mediumblob`, `mediumtext`, `blob`, `text`, `longblob`, `longtext`, `date`,
`year`, `enum`, `json`
### Column types for `postgres`
`int2`, `int2`, `int4`, `int8`, `integer`, `smallint`, `bigint`, `decimal`, `numeric`, `decimal`,
`numeric`, `real`, `double precision`, `time`, `time with time zone`, `time without time zone`,
`timestamp`, `timestamp without time zone`, `timestamp with time zone`, `int`, `smallint`, `bigint`,
`character varying`, `character`, `varchar`, `char`, `int2`, `integer`, `int4`, `int8`,
`float4`, `float8`, `smallserial`, `serial2`, `serial`, `serial4`, `bigserial`, `serial8`,
`money`, `boolean`, `bool`, `text`, `citext`, `bytea`, `date`, `interval`, `point`, `line`, `lseg`, `box`,
`int`, `int2`, `int4`, `int8`, `integer`, `smallint`, `bigint`, `float4`, `float8`,
`numeric`, `decimal`, `real`, `double precision`, `time`, `time with time zone`,
`time without time zone`, `timestamp`, `timestamp without time zone`, `timestamp with time zone`,
`character varying`, `character`, `varchar`, `char`, `text`, `citext`,
`smallserial`, `serial2`, `serial`, `serial4`, `bigserial`, `serial8`,
`money`, `boolean`, `bool` `bytea`, `date`, `interval`, `point`, `line`, `lseg`, `box`,
`circle`, `path`, `polygon`, `cidr`, `inet`, `macaddr`, `bit`, `bit varying`,
`varbit`, `tsvector`, `tsquery`, `uuid`, `xml`, `json`, `jsonb`
### Column types for `sqlite` / `websql` / `cordova`
`int`, `int2`, `int2`, `int8`, `integer`, `tinyint`, `smallint`, `mediumint`, `bigint`, `decimal`,
`numeric`, `float`, `double`, `decimal`, `numeric`, `real`, `double precision`, `datetime`,
`int`, `tinyint`, `smallint`, `mediumint`, `bigint`, `varying character`, `character`, `native character`,
`varchar`, `nchar`, `nvarchar2`, `int2`, `integer`, `int8`, `unsigned big int`, `boolean`,
`int`, `int2`, `int8`, `integer`, `tinyint`, `smallint`, `mediumint`, `bigint`, `decimal`,
`numeric`, `float`, `double`, `real`, `double precision`, `datetime`, `varying character`,
`character`, `native character`, `varchar`, `nchar`, `nvarchar2`, `unsigned big int`, `boolean`,
`blob`, `text`, `clob`, `date`
### Column types for `mssql`
`int`, `tinyint`, `smallint`, `bigint`, `dec`, `decimal`, `numeric`, `float`, `dec`, `decimal`,
`numeric`, `real`, `datetime`, `datetime2`, `datetimeoffset`, `time`, `timestamp`,
`int`, `tinyint`, `smallint`, `bigint`, `nvarchar`, `varchar`, `char`, `nchar`, `binary`, `varbinary`,
`nvarchar`, `varchar`, `char`, `nchar`, `binary`, `varbinary`,
`bit`, `smallmoney`, `money`, `text`, `ntext`, `image`, `smalldatetime`, `date`, `xml`, `varbinary`,
`cursor`, `hierarchyid`, `sql_variant`, `table`
@ -300,6 +300,34 @@ just like you stored them.
Note you **MUST NOT** have any comma in values you write.
### `simple-json` column type
There is a special column type called `simple-json` which can store any values which can be stored in database
via JSON.stringify.
Very useful when you do not have json type in your database and you want to store and load object
without any hustle.
For example:
```typescript
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column("simple-json")
profile: { name: string, nickname: string };
}
```
```typescript
const user = new User();
user.profile = { name: "John", nickname: "Malkovich" };
```
Will be stored in a single database column as `{"name":"John","nickname":"Malkovich"}` value.
When you'll load data from the database, you will have your object/array/primitive back via JSON.parse
### Columns with generated values
You can create column with generated value using `@Generated` decorator. For example:
@ -365,6 +393,7 @@ You can specify array of values or specify a enum class.
* `array: boolean` - Used for postgres column types which can be array (for example int[])
* `transformer: { from(value: DatabaseType): EntityType, to(value: EntityType): DatabaseType }` - Used to
marshal properties of arbitrary type `EntityType` into a type `DatabaseType` supported by the database.
* `select: boolean` - Defines whether or not to hide this column by default when making queries. When set to `false`, the column data will not show with a standard query. By default column is `select: true`
Note: most of those column options are RDBMS-specific and aren't available in `MongoDB`.

View File

@ -57,7 +57,7 @@ const userId = manager.getId(user); // userId === 1
```
* `create` - Creates a new instance of `User`. Optionally accepts an object literal with user properties
which will be written into newly created user object
which will be written into newly created user object.
```typescript
const user = manager.create(User); // same as const user = new User();
@ -68,15 +68,15 @@ const user = manager.create(User, {
}); // same as const user = new User(); user.firstName = "Timber"; user.lastName = "Saw";
```
* `merge` - Merges multiple entities into a single entity
* `merge` - Merges multiple entities into a single entity.
```typescript
const user = new User();
manager.merge(User, user, { firstName: "Timber" }, { lastName: "Saw" }); // same as user.firstName = "Timber"; user.lastName = "Saw";
```
* `preload` - 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
* `preload` - Creates a new entity from the given plain javascript object. If the 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 the new entity. The new entity is actually loaded from the database entity with all properties
replaced from the new object.
@ -94,10 +94,10 @@ const user = await manager.preload(User, partialUser);
```
* `save` - Saves a given entity or array of entities.
If the entity already exist in the database then it's updated.
If the entity does not exist in the database yet it's inserted.
If the entity already exists in the database, then it's updated.
If the entity does not exist in the database yet, it's inserted.
It saves all given entities in a single transaction (in the case of entity manager is not transactional).
Also supports partial updating since all undefined properties are skipped.
Also supports partial updating since all undefined properties are skipped. In order to make a value `NULL`, you must manually set the property to equal `null`.
```typescript
await manager.save(user);
@ -109,7 +109,7 @@ await manager.save([
```
* `remove` - Removes a given entity or array of entities.
It removes all given entities in a single transaction (in the case of entity manager is not transactional).
It removes all given entities in a single transaction (in the case of entity, manager is not transactional).
```typescript
await manager.remove(user);
@ -209,7 +209,7 @@ const categoryRepository = manager.getTreeRepository(Category);
```
* `getMongoRepository` - Gets `MongoRepository` to perform operations on a specific entity.
Learn more about [MongoDB](./mongodb.md) documentation.
Learn more about [MongoDB](./mongodb.md).
```typescript
const userRepository = manager.getMongoRepository(User);
@ -222,9 +222,9 @@ const userRepository = manager.getMongoRepository(User);
const myUserRepository = manager.getCustomRepository(UserRepository);
```
* `release` - Releases query runner of a entity manager.
* `release` - Releases query runner of an entity manager.
Used only when query runner was created and managed manually.
```typescript
await manager.release();
```
```

View File

@ -6,8 +6,8 @@
## Initial setup
Lets create a simple application called "user" which stores users in the database
and allow to create, update, remove, get a list of all users and a single user by id
Let's create a simple application called "user" which stores users in the database
and allows us to create, update, remove, and get a list of all users, as well as a single user by id
within web api.
First, create a directory called "user":
@ -32,7 +32,7 @@ npm i typescript --save-dev
```
Then let's create a `tsconfig.json` file which contains the configuration required for the application to
compile and run. Create it using your favorite editor and put following configuration:
compile and run. Create it using your favorite editor and put the following configuration:
```json
{
@ -47,7 +47,7 @@ compile and run. Create it using your favorite editor and put following configur
}
```
Now lets create main application endpoint - `app.ts` inside `src` directory:
Now let's create a main application endpoint - `app.ts` inside the `src` directory:
```
mkdir src
@ -55,30 +55,30 @@ cd src
touch app.ts
```
Let's add simple `console.log` inside it:
Let's add a simple `console.log` inside it:
```typescript
console.log("Application is up and running");
```
Now its time to run our application.
To run it you need to compile your typescript project first:
Now it's time to run our application.
To run it, you need to compile your typescript project first:
```
tsc
```
Once you compile it you should have `src/app.js` file generated.
You can run it:
Once you compile it, you should have a `src/app.js` file generated.
You can run it using:
```
node src/app.js
```
You should see "Application is up and running" message in your console just right after you run the application.
You should see the, "Application is up and running" message in your console right after you run the application.
You must compile your files each time you make a change.
Alternatively you can setup watcher or install [ts-node](http://github.com/ts-node/ts-node) to avoid manual compilation each time.
Alternatively, you can setup watcher or install [ts-node](http://github.com/ts-node/ts-node) to avoid manual compilation each time.
## Adding Express to the application
@ -93,7 +93,7 @@ npm i express body-parser @types/express @types/body-parser --save
* `@types/express` is used to have a type information when using express
* `@types/body-parser` is used to have a type information when using body parser
Let's edit `src/app.ts` file and add express-related logic:
Let's edit the `src/app.ts` file and add express-related logic:
```typescript
import * as express from "express";
@ -131,13 +131,13 @@ app.listen(3000);
```
Now you can compile and run your project.
You should have a express server running now with working routes.
However those routes do not return any content yet.
You should have an express server running now with working routes.
However, those routes do not return any content yet.
## Adding TypeORM to the application
Finally lets add TypeORM to the application.
In this example we will use `mysql` driver.
Finally, let's add TypeORM to the application.
In this example, we will use `mysql` driver.
Setup process for other drivers is similar.
Let's install the required packages first:
@ -242,7 +242,7 @@ export function UsersListAction(req: Request, res: Response) {
}
```
You even don't need `getConnection` in this example - you can directly use `getRepository` function:
You don't even need `getConnection` in this example - you can directly use the `getRepository` function:
```typescript
import {getRepository} from "typeorm";

View File

@ -13,8 +13,8 @@
## How do I update a database schema?
One of the main responsibility of TypeORM is to keep your database tables in sync with your entities.
There are two ways that help you to achieve this:
One of the main responsibilities of TypeORM is to keep your database tables in sync with your entities.
There are two ways that help you achieve this:
* Use `synchronize: true` in your connection options:
@ -26,7 +26,7 @@ There are two ways that help you to achieve this:
});
```
This option automatically synces your database tables with the given entities each time you run this code.
This option automatically syncs your database tables with the given entities each time you run this code.
This option is perfect during development, but in production you may not want this option to be enabled.
* Use command line tools and run schema sync manually in the command line:
@ -36,15 +36,15 @@ There are two ways that help you to achieve this:
```
This command will execute schema synchronization.
Note, to make command line tools to work, you must create a ormconfig.json file.
Note, to make command line tools work, you must create an ormconfig.json file.
Schema sync is extremely fast.
If you are considering to disable synchronize option during development because of performance issues,
If you are considering the disable synchronize option during development because of performance issues,
first check how fast it is.
## How do I change a column name in the database?
By default column names are generated from property names.
By default, column names are generated from property names.
You can simply change it by specifying a `name` column option:
```typescript
@ -108,36 +108,36 @@ export class Photo {
```
This example does not have a `@JoinColumn` which is incorrect.
Why? Because to make a real relation we need to create a column in the database.
Why? Because to make a real relation, we need to create a column in the database.
We need to create a column `userId` in `photo` or `photoId` in `user`.
But which column should be created - `userId` or `photoId`?
TypeORM cannot decide it for you.
To make a decision you must use `@JoinColumn` on one of the sides.
TypeORM cannot decide for you.
To make a decision, you must use `@JoinColumn` on one of the sides.
If you put `@JoinColumn` in `Photo` then a column called `userId` will be created in the `photo` table.
If you put `@JoinColumn` in `User` then a column called `photoId` will be created in the `user` table.
The side with `@JoinColumn` will be called "owner side of the relationship".
The other side of the relation, without `@JoinColumn` is called "inverse (non-owner) side of relationship".
The side with `@JoinColumn` will be called the "owner side of the relationship".
The other side of the relation, without `@JoinColumn`, is called the "inverse (non-owner) side of relationship".
Same in a `@ManyToMany` relation you use `@JoinTable` to show the owner side of the relation.
It is the same in a `@ManyToMany` relation. You use `@JoinTable` to show the owner side of the relation.
In `@ManyToOne` or `@OneToMany` relations `@JoinColumn` is not necessary because
both decorators are different and where you put `@ManyToOne` decorator that table will have relational column.
In `@ManyToOne` or `@OneToMany` relations, `@JoinColumn` is not necessary because
both decorators are different, and the table where you put the `@ManyToOne` decorator will have the relational column.
`@JoinColumn` and `@JoinTable` decorators can also be used to specify additional
join column / junction table settings, like join column name or junction table name.
## How do I add extra columns into many-to-many (junction) table?
Its not possible to add extra columns into the table created by a many-to-many relation.
You'll need to create a separate entity and bind it using two many-to-one relations with target entities
(effect will be same as creating a many-to-many table),
It's not possible to add extra columns into a table created by a many-to-many relation.
You'll need to create a separate entity and bind it using two many-to-one relations with the target entities
(the effect will be same as creating a many-to-many table),
and add extra columns in there.
## How to use TypeORM with a dependency injection tool?
In TypeORM you can use service containers. Service containers allow you to inject custom services in some places, like in subscribers or custom naming strategies. Or for example, you can get access to ConnectionManager from any place using a service container.
In TypeORM you can use service containers. Service containers allow you to inject custom services in some places, like in subscribers or custom naming strategies. For example, you can get access to ConnectionManager from any place using a service container.
Here is a example for how you can setup typedi service containers with TypeORM. But note, that you can setup any service container with TypeORM.
Here is an example for how you can set up typedi service containers with TypeORM. Note: you can setup any service container with TypeORM.
```typescript
import {useContainer, createConnection} from "typeorm";
@ -150,20 +150,20 @@ createConnection({/* ... */});
## How to handle outDir TypeScript compiler option?
When you are using the `outDir` compiler option don't forget to copy assets and resources your app is using into the output directory.
Or make sure to setup correct paths to those assets.
When you are using the `outDir` compiler option, don't forget to copy assets and resources your app is using into the output directory.
Otherwise, make sure to setup correct paths to those assets.
One important thing to know is, that when you remove or move entities, the old entities are left untouched inside the ouput directory.
For example you create a `Post` entity and rename it to `Blog`.
You no longer have `Post.ts` in your project, however `Post.js` is left inside the output directory.
Now when TypeORM reads entities from your output directory it sees two entities - `Post` and `Blog`.
One important thing to know is that when you remove or move entities, the old entities are left untouched inside the ouput directory.
For example, you create a `Post` entity and rename it to `Blog`,
you no longer have `Post.ts` in your project. However, `Post.js` is left inside the output directory.
Now, when TypeORM reads entities from your output directory, it sees two entities - `Post` and `Blog`.
This may be a source of bugs.
That's why when you remove and move entities with `outDir` enabled its strongly recommended to remove your output directory and recompile the project again.
That's why when you remove and move entities with `outDir` enabled, it's strongly recommended to remove your output directory and recompile the project again.
## How to use TypeORM with ts-node?
You can prevent compiling files each time using [ts-node](https://github.com/TypeStrong/ts-node).
If you are using ts-node you can specify `ts` entities inside your connection options:
If you are using ts-node, you can specify `ts` entities inside your connection options:
```
{
@ -174,9 +174,9 @@ If you are using ts-node you can specify `ts` entities inside your connection op
Also, if you are compiling js files into the same folder where your typescript files are,
make sure to use the `outDir` compiler option to prevent
[this issues](https://github.com/TypeStrong/ts-node/issues/432).
[this issue](https://github.com/TypeStrong/ts-node/issues/432).
Also if you want to use the ts-node CLI you can execute TypeORM the following way:
Also, if you want to use the ts-node CLI, you can execute TypeORM the following way:
```
ts-node ./node_modules/bin/typeorm schema:sync

View File

@ -71,7 +71,7 @@ export class Post {
### `@BeforeUpdate`
You can define a method with any name in the entity and mark it with `@BeforeUpdate`
and TypeORM will call it before an existing entity is updated using repository/manager `save`.
and TypeORM will call it before an existing entity is updated using repository/manager `save`. Keep in mind, however, that this will occur only when information is changed in the model. If you run `save` without modifying anything from the model, `@BeforeUpdate` and `@AfterUpdate` will not run.
Example:
```typescript

View File

@ -48,12 +48,12 @@ If you want to enable logging of failed queries only then only add `error`:
There are other options you can use:
* `query` - log all queries
* `error` - log all failed queries and error
* `schema` - log the schema build process
* `warn` - log internal orm warnings
* `info` - log internal orm informative messages
* `log` - log internal orm log messages
* `query` - logs all queries.
* `error` - logs all failed queries and errors.
* `schema` - logs the schema build process.
* `warn` - logs internal orm warnings.
* `info` - logs internal orm informative messages.
* `log` - logs internal orm log messages.
You can specify as many options as needed.
If you want to enable all logging you can simply specify `logging: "all"`:
@ -68,7 +68,7 @@ If you want to enable all logging you can simply specify `logging: "all"`:
## Log long-running queries
If you have performance issues you can log queries that take too much time to execute
If you have performance issues, you can log queries that take too much time to execute
by setting `maxQueryExecutionTime` in connection options:
```typescript
@ -85,12 +85,12 @@ This code will log all queries which run more then `1 second`.
TypeORM ships with 4 different types of logger:
* `advanced-console` - this is default which logs all messages onto the console using color
and sql syntax highlighting (using [chalk](https://github.com/chalk/chalk))
* `advanced-console` - this is the default logger which logs all messages into the console using color
and sql syntax highlighting (using [chalk](https://github.com/chalk/chalk)).
* `simple-console` - this is a simple console logger which is exactly the same as the advanced logger, but it does not use any color highlighting.
This logger can be used if you have problems / or don't like colorized logs
* `file` - this logger writes all logs into `ormlogs.log` in the root folder of your project (near `package.json` and `ormconfig.json`)
* `debug` - this logger uses [debug package](https://github.com/visionmedia/debug), to turn on logging set env variable `DEBUG=typeorm:*` (note logging option has no effect on this logger)
This logger can be used if you have problems / or don't like colorized logs.
* `file` - this logger writes all logs into `ormlogs.log` in the root folder of your project (near `package.json` and `ormconfig.json`).
* `debug` - this logger uses [debug package](https://github.com/visionmedia/debug), to turn on logging set your env variable `DEBUG=typeorm:*` (note logging option has no effect on this logger).
You can enable any of them in connection options:
@ -135,8 +135,8 @@ createConnection({
});
```
If you defined your connection options in `ormconfig` file,
then you can use it and override following way:
If you defined your connection options in the `ormconfig` file,
then you can use it and override it in the following way:
```typescript
import {createConnection, getConnectionOptions} from "typeorm";
@ -152,8 +152,8 @@ getConnectionOptions().then(connectionOptions => {
});
```
Logger methods can accept `QueryRunner` when its available. Its helpful if you want to log additional data.
Also via query runner you can get access to additional data passed during persist/remove. For example:
Logger methods can accept `QueryRunner` when it's available. It's helpful if you want to log additional data.
Also, via query runner, you can get access to additional data passed during persist/remove. For example:
```typescript
// user sends request during entity save
@ -164,4 +164,4 @@ logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) {
const requestUrl = queryRunner && queryRunner.data["request"] ? "(" + queryRunner.data["request"].url + ") " : "";
console.log(requestUrl + "executing query: " + sql);
}
```
```

View File

@ -71,8 +71,8 @@ Before creating a new migration you need to setup your connection options proper
Here we setup two options:
* `"migrations": ["migration/*.js"]` - indicates that typeorm must load migrations from the given "migration" directory
* `"cli": { "migrationsDir": "migration" }` - indicates that the CLI must create new migrations in the "migration" directory
* `"migrations": ["migration/*.js"]` - indicates that typeorm must load migrations from the given "migration" directory.
* `"cli": { "migrationsDir": "migration" }` - indicates that the CLI must create new migrations in the "migration" directory.
Once you setup connection options you can create a new migration using CLI:
@ -80,12 +80,12 @@ Once you setup connection options you can create a new migration using CLI:
typeorm migrations:create -n PostRefactoring
```
To use CLI commands you need to install typeorm globally (`npm i typeorm -g`).
Also make sure your locally typeorm version matches the global version.
To use CLI commands, you need to install typeorm globally (`npm i typeorm -g`).
Also, make sure your local typeorm version matches the global version.
Learn more about the [TypeORM CLI](./using-cli.md).
Here, `PostRefactoring` is the name of the migration - you can specify any name you want.
After you run the command you can see a new file generated in "migration" directory,
After you run the command you can see a new file generated in the "migration" directory
named `{TIMESTAMP}-PostRefactoring.ts` where `{TIMESTAMP}` is the current timestamp when the migration was generated.
Now you can open the file and add your migration sql queries there.
@ -114,7 +114,7 @@ There are two methods you must fill with your migration code: `up` and `down`.
`down` method is used to revert the last migration.
Inside both `up` and `down` you have a `QueryRunner` object.
All database operations are executing using this object.
All database operations are executed using this object.
Learn more about [query runner](./query-runner.md).
Let's see what the migration looks like with our `Post` changes:
@ -138,7 +138,7 @@ export class PostRefactoringTIMESTAMP implements MigrationInterface {
## Running and reverting migrations
Once you have a migration to run on production you can run them using a CLI command:
Once you have a migration to run on production, you can run them using a CLI command:
```
typeorm migrations:run
@ -161,14 +161,14 @@ If you need to revert multiple migrations you must call this command multiple ti
TypeORM is able to automatically generate migration files with schema changes you made.
Let's say you have a `Post` entity with a `title` column. And you have changed the name `title` to `name`.
Let's say you have a `Post` entity with a `title` column, and you have changed the name `title` to `name`.
You can run following command:
```
typeorm migrations:generate -n PostRefactoring
```
And it will generate a new migration called `{TIMESTAMP}-PostRefactoring.ts` with following content:
And it will generate a new migration called `{TIMESTAMP}-PostRefactoring.ts` with the following content:
```typescript
import {MigrationInterface, QueryRunner} from "typeorm";

View File

@ -190,7 +190,7 @@ const timber = await manager.findOne(User, { firstName: "Timber", lastName: "Saw
Just like separate like `MongoEntityManager` there is a `MongoRepository` with extended `Repository`:
```typescript
import {getMongoManager} from "typeorm";
import {getMongoRepository} from "typeorm";
const userRepository = getMongoRepository(User); // or connection.getMongoManager
const timber = await userRepository.findOne({ firstName: "Timber", lastName: "Saw" });

View File

@ -1,11 +1,11 @@
# Working with Relations
`RelationQueryBuilder` is special type of `QueryBuilder` which allows you to work with your relations.
Using it you can bind entities to each other in the database without need to load any entities.
Or you can load related entities easily.
`RelationQueryBuilder` is a special type of `QueryBuilder` which allows you to work with your relations.
Using it, you can bind entities to each other in the database without the need to load any entities,
or you can load related entities easily.
Examples:
For example we have a `Post` entity and it has a many-to-many relation to `Category` called `categories`.
For example, we have a `Post` entity and it has a many-to-many relation to `Category` called `categories`.
Let's add a new category to this many-to-many relation:
```typescript
@ -29,17 +29,17 @@ post.categories.push(category);
await postRepository.save(post);
```
But more efficient because it does a minimal number of operations and binds entities in the database,
unlike calling bulky `save` method call.
But more efficient, because it does a minimal number of operations, and binds entities in the database,
unlike calling a bulky `save` method call.
Also, other benefit of such an approach is that you don't need to load every related entity before pushing into it.
For example if you have ten thousand categories inside a single post, adding a new post to this list may become problematic for you,
because the standard way of doing this, is to load the post with all ten thousand categories, push a new category
and save it. This results in very heavy performance and basically inapplicable in production results.
Also, another benefit of such an approach is that you don't need to load every related entity before pushing into it.
For example, if you have ten thousand categories inside a single post, adding new posts to this list may become problematic for you,
because the standard way of doing this is to load the post with all ten thousand categories, push a new category,
and save it. This results in very heavy performance costs and is basically inapplicable in production results.
However, using `RelationQueryBuilder` solves this problem.
Also, there is no real need to use entities when you "bind" things, you can use entity ids instead.
For example, lets add a category with id = 3 into post with id = 1:
Also, there is no real need to use entities when you "bind" things, since you can use entity ids instead.
For example, let's add a category with id = 3 into post with id = 1:
```typescript
import {getConnection} from "typeorm";
@ -51,7 +51,7 @@ await getConnection()
.add(3);
```
If you are using composite primary keys you have to pass them as id map, for example:
If you are using composite primary keys, you have to pass them as an id map, for example:
```typescript
import {getConnection} from "typeorm";
@ -63,7 +63,7 @@ await getConnection()
.add({ firstCategoryId: 2, secondCategoryId: 4 });
```
Same way you add new entities, you can remove them:
You can remove entities the same way you add them:
```typescript
import {getConnection} from "typeorm";
@ -104,8 +104,8 @@ await getConnection()
```
Besides updating relations, the relational query builder also allows you to load relational entities.
For example, lets say inside a `Post` entity we have a many-to-many `categories` relation and a many-to-one `user` relation.
To load those relations you can use following code:
For example, lets say inside a `Post` entity we have a many-to-many `categories` relation and a many-to-one `user` relation,
to load those relations you can use following code:
```typescript
import {getConnection} from "typeorm";

View File

@ -86,20 +86,20 @@ question.categories = [category1, category2];
await connection.manager.save(question);
```
As you can see in this example we did not called `save` for `category1` and `category2`.
As you can see in this example we did not call `save` for `category1` and `category2`.
They will be automatically inserted, because we set `cascade` to true.
Keep in mind - great power comes with great responsibility.
Cascades may seem a good and easy way to work with relations,
Cascades may seem like a good and easy way to work with relations,
but they may also bring bugs and security issues when some undesired object is being saved into the database.
Also, they provide a less explicit way of saving new objects into the database.
## `@JoinColumn` options
`@JoinColumn` not only defines which side of the relation contains the join column with a foreign key,
but also allows to customize join column name and referenced column name.
but also allows you to customize join column name and referenced column name.
When we set `@JoinColumn` it creates a column in the database automatically named `propertyName + referencedColumnName`.
When we set `@JoinColumn`, it automtically creates a column in the database named `propertyName + referencedColumnName`.
For example:
```typescript
@ -117,7 +117,7 @@ If you want to change this name in the database you can specify a custom join co
category: Category;
```
Join columns are always a reference to some columns (using a foreign key).
Join columns are always a reference to some other columns (using a foreign key).
By default your relation always refers to the primary column of the related entity.
If you want to create relation with other columns of the related entity -
you can specify them in `@JoinColumn` as well:
@ -129,7 +129,7 @@ category: Category;
```
The relation now refers to `name` of the `Category` entity, instead of `id`.
Column name for such relation will become `categoryName`
Column name for that relation will become `categoryName`
## `@JoinTable` options
@ -154,5 +154,5 @@ You can also change the name of the generated "junction" table.
categories: Category[];
```
If destination table has composite primary keys,
then array of properties must be send to `@JoinTable`.
If the destination table has composite primary keys,
then an array of properties must be sent to `@JoinTable`.

View File

@ -70,16 +70,16 @@ const user = repository.create({
}); // same as const user = new User(); user.firstName = "Timber"; user.lastName = "Saw";
```
* `merge` - Merges multiple entities into a single entity
* `merge` - Merges multiple entities into a single entity.
```typescript
const user = new User();
repository.merge(user, { firstName: "Timber" }, { lastName: "Saw" }); // same as user.firstName = "Timber"; user.lastName = "Saw";
```
* `preload` - Creates a new entity from the given plain javascript object. If the 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 the new entity. The new entity is actually an entity loaded from the db with all properties
* `preload` - Creates a new entity from the given plain javascript object. If the entity already exists 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 the new entity. The new entity is actually an entity loaded from the database with all properties
replaced from the new object.
```typescript
@ -98,7 +98,7 @@ const user = await repository.preload(partialUser);
* `save` - Saves a given entity or array of entities.
If the entity already exist in the database, it is updated.
If the entity does not exist in the database, it is inserted.
It saves all given entities in a single transaction (in the case of entity manager is not transactional).
It saves all given entities in a single transaction (in the case of entity, manager is not transactional).
Also supports partial updating since all undefined properties are skipped.
```typescript
@ -111,7 +111,7 @@ await repository.save([
```
* `remove` - Removes a given entity or array of entities.
It removes all given entities in a single transaction (in the case of entity manager is not transactional).
It removes all given entities in a single transaction (in the case of entity, manager is not transactional).
```typescript
await repository.remove(user);

View File

@ -4,15 +4,15 @@ See what amazing new features we are expecting to land in the next TypeORM versi
## Note on 1.0.0 release
We are planning to release a final stable `1.0.0` version in summer 2018.
However TypeORM is already actively used in number of big production systems.
Main API is already very stable.
TypeORM follows a semantic versioning and until `1.0.0` breaking changes may appear in `0.x.x` versions,
however since API is already quite stable we don't expect too much breaking changes.
We are planning to release a final stable `1.0.0` version somewhere in Summer 2018.
However, TypeORM is already actively used in a number of big production systems.
The main API is already very stable.
TypeORM follows a semantic versioning and until `1.0.0`, breaking changes may appear in `0.x.x` versions.
However, since the API is already quite stable we don't expect too many breaking changes.
## How to install latest development version?
To install latest development version use following command:
To install latest development version use the following command:
```
npm i typeorm@next
@ -35,7 +35,7 @@ npm i typeorm@next
## 0.2.0
- [ ] research NativeScript support
- [x] implement soft deletion
- [x] implement soft deletion
- [x] add more tree-table features: nested set and materialized path; more repository methods
- [ ] fix Oracle driver issues and make oracle stable and ready for production use
- [ ] implement migrations generator for all drivers

View File

@ -23,6 +23,7 @@
* [Set locking](#set-locking)
* [Partial selection](#partial-selection)
* [Using subqueries](#using-subqueries)
* [Hidden Columns](#hidden-columns)
## What is `QueryBuilder`
@ -100,9 +101,9 @@ There are several ways how you can create a `Query Builder`:
.getOne();
```
There are 5 diffrent `QueryBuilder`s available:
There are 5 different `QueryBuilder` types available:
* `SelectQueryBuilder` used to build and execute `SELECT` queries. Example:
* `SelectQueryBuilder` - used to build and execute `SELECT` queries. Example:
```typescript
import {getConnection} from "typeorm";
@ -115,7 +116,7 @@ There are 5 diffrent `QueryBuilder`s available:
.getOne();
```
* `InsertQueryBuilder` used to build and execute `INSERT` queries. Example:
* `InsertQueryBuilder` - used to build and execute `INSERT` queries. Example:
```typescript
import {getConnection} from "typeorm";
@ -131,7 +132,7 @@ There are 5 diffrent `QueryBuilder`s available:
.execute();
```
* `UpdateQueryBuilder` used to build and execute `UPDATE` queries. Example:
* `UpdateQueryBuilder` - used to build and execute `UPDATE` queries. Example:
```typescript
import {getConnection} from "typeorm";
@ -143,7 +144,7 @@ There are 5 diffrent `QueryBuilder`s available:
.where("id = :id", { id: 1 })
.execute();
```
* `DeleteQueryBuilder` used to build and execute `DELETE` queries. Example:
* `DeleteQueryBuilder` - used to build and execute `DELETE` queries. Example:
```typescript
import {getConnection} from "typeorm";
@ -156,15 +157,15 @@ There are 5 diffrent `QueryBuilder`s available:
.execute();
```
* `RelationQueryBuilder` used to build and execute relation-specific operations [TBD].
* `RelationQueryBuilder` - used to build and execute relation-specific operations [TBD].
You can switch between different types of query builder within any of them,
once you do it - you will get a new instance of query builder (unlike all other methods).
once you do, you will get a new instance of query builder (unlike all other methods).
## Getting values using `QueryBuilder`
To get a single result from the database,
for example to get a user by id or name you must use `getOne`:
for example to get a user by id or name, you must use `getOne`:
```typescript
const timber = await getRepository(User)
@ -174,7 +175,7 @@ const timber = await getRepository(User)
```
To get multiple results from the database,
for example to get all users from the database use `getMany`:
for example, to get all users from the database, use `getMany`:
```typescript
const users = await getRepository(User)
@ -183,11 +184,11 @@ const users = await getRepository(User)
```
There are two types of results you can get using select query builder: **entities** or **raw results**.
Most of the times you need to select real entities from your database, for example users.
For this purpose you use `getOne` and `getMany`.
Most of the time, you need to select real entities from your database, for example, users.
For this purpose, you use `getOne` and `getMany`.
But sometimes you need to select some specific data, let's say the *sum of all user photos*.
This data is not an entity, its called raw data.
To get raw data you use `getRawOne` and `getRawMany`.
This data is not an entity, it's called raw data.
To get raw data, you use `getRawOne` and `getRawMany`.
Examples:
```typescript
@ -213,7 +214,7 @@ const photosSums = await getRepository(User)
We used `createQueryBuilder("user")`. But what is "user"?
It's just a regular SQL alias.
We use aliases everywhere in except when we work with selected data.
We use aliases everywhere, except when we work with selected data.
`createQueryBuilder("user")` is equivalent to:
@ -223,13 +224,13 @@ createQueryBuilder()
.from(User, "user")
```
Which will result into following sql query:
Which will result in the following sql query:
```sql
SELECT ... FROM users user
```
In this SQL query `users` is the table name and `user` is an alias we assign to this table.
In this SQL query, `users` is the table name, and `user` is an alias we assign to this table.
Later we use this alias to access the table:
```typescript
@ -239,15 +240,15 @@ createQueryBuilder()
.where("user.name = :name", { name: "Timber" })
```
Which produce following SQL query:
Which produces the following SQL query:
```sql
SELECT ... FROM users user WHERE user.name = 'Timber'
```
See, we used the users table using the `user` alias we assigned when we created a query builder.
See, we used the users table by using the `user` alias we assigned when we created a query builder.
One query builder is not limited to one alias, they can have are multiple aliases.
One query builder is not limited to one alias, they can have multiple aliases.
Each select can have its own alias,
you can select from multiple tables each with its own alias,
you can join multiple tables each with its own alias.
@ -256,9 +257,9 @@ You can use those aliases to access tables are you selecting (or data you are se
## Using parameters to escape data
We used `where("user.name = :name", { name: "Timber" })`.
What does `{ name: "Timber" }` stands for? It's a parameter we used to prevent SQL injection.
What does `{ name: "Timber" }` stand for? It's a parameter we used to prevent SQL injection.
We could have written: `where("user.name = '" + name + "')`,
however this is not safe as it opens the code to SQL injections.
however this is not safe, as it opens the code to SQL injections.
The safe way is to use this special syntax: `where("user.name = :name", { name: "Timber" })`,
where `:name` is a parameter name and the value is specified in an object: `{ name: "Timber" }`.
@ -282,7 +283,7 @@ createQueryBuilder("user")
.where("user.name = :name", { name: "Timber" })
```
Will produce:
Which will produce:
```sql
SELECT ... FROM users user WHERE user.name = 'Timber'
@ -296,13 +297,13 @@ createQueryBuilder("user")
.andWhere("user.lastName = :lastName", { lastName: "Saw" });
```
Will produce the following SQL query:
Which will produce the following SQL query:
```sql
SELECT ... FROM users user WHERE user.firstName = 'Timber' AND user.lastName = 'Saw'
```
You can add `OR` into an exist `WHERE` expression:
You can add `OR` into an existing `WHERE` expression:
```typescript
createQueryBuilder("user")
@ -310,7 +311,7 @@ createQueryBuilder("user")
.orWhere("user.lastName = :lastName", { lastName: "Saw" });
```
Will produce the following SQL query:
Which will produce the following SQL query:
```sql
SELECT ... FROM users user WHERE user.firstName = 'Timber' OR user.lastName = 'Saw'
@ -319,9 +320,9 @@ SELECT ... FROM users user WHERE user.firstName = 'Timber' OR user.lastName = 'S
You can combine as many `AND` and `OR` expressions as you need.
If you use `.where` more than once you'll override all previous `WHERE` expressions.
Note: be careful with `orWhere` - if you use complex expressions with both `AND` and `OR` expressions
Note: be careful with `orWhere` - if you use complex expressions with both `AND` and `OR` expressions,
keep in mind that they are stacked without any pretences.
Sometimes you'll need to create a where string instead and avoid using `orWhere`.
Sometimes you'll need to create a where string instead, and avoid using `orWhere`.
## Adding `HAVING` expression
@ -332,7 +333,7 @@ createQueryBuilder("user")
.having("user.name = :name", { name: "Timber" })
```
Will produce following SQL query:
Which will produce following SQL query:
```sql
SELECT ... FROM users user HAVING user.name = 'Timber'
@ -346,7 +347,7 @@ createQueryBuilder("user")
.andHaving("user.lastName = :lastName", { lastName: "Saw" });
```
Will produce the following SQL query:
Which will produce the following SQL query:
```sql
SELECT ... FROM users user HAVING user.firstName = 'Timber' AND user.lastName = 'Saw'
@ -360,7 +361,7 @@ createQueryBuilder("user")
.orHaving("user.lastName = :lastName", { lastName: "Saw" });
```
Will produce the following SQL query:
Which will produce the following SQL query:
```sql
SELECT ... FROM users user HAVING user.firstName = 'Timber' OR user.lastName = 'Saw'
@ -371,20 +372,20 @@ If you use `.having` more than once you'll override all previous `HAVING` expres
## Adding `ORDER BY` expression
Adding a `ORDER BY` expression is easy as:
Adding an `ORDER BY` expression is easy as:
```typescript
createQueryBuilder("user")
.orderBy("user.id")
```
Will produce:
Which will produce:
```sql
SELECT ... FROM users user ORDER BY user.id
```
You can change the order direction from ascendant to descendant (or versa):
You can change the ordering direction from ascending to descending (or versa):
```typescript
createQueryBuilder("user")
@ -402,7 +403,7 @@ createQueryBuilder("user")
.addOrderBy("user.id");
```
You can also usea map of order-by fields:
You can also use a map of order-by fields:
```typescript
createQueryBuilder("user")
@ -423,7 +424,7 @@ createQueryBuilder("user")
.groupBy("user.id")
```
This Will produce the following SQL query:
Which will produce the following SQL query:
```sql
SELECT ... FROM users user GROUP BY user.id
@ -453,28 +454,28 @@ Which will produce the following SQL query:
SELECT ... FROM users user LIMIT 10
```
The resulting SQL query depends of database type.
Note LIMIT may not work as you may expect if you are using complex queries with joins or subqueries.
If you are using pagination its recommended to use `take` instead.
The resulting SQL query depends on the type of database (SQL, mySQL, Postgres, etc).
Note: LIMIT may not work as you may expect if you are using complex queries with joins or subqueries.
If you are using pagination, it's recommended to use `take` instead.
## Adding `OFFSET` expression
Adding SQL `OFFSET` expression is easy as:
Adding an SQL `OFFSET` expression is easy as:
```typescript
createQueryBuilder("user")
.offset(10)
```
Will produce the following SQL query:
Which will produce the following SQL query:
```sql
SELECT ... FROM users user OFFSET 10
```
The resulting SQL query depends of database type.
Note OFFSET may not work as you may expect if you are using complex queries with joins or subqueries.
If you are using pagination its recommended to use `skip` instead.
The resulting SQL query depends on the type of database (SQL, mySQL, Postgres, etc).
Note: OFFSET may not work as you may expect if you are using complex queries with joins or subqueries.
If you are using pagination, it's recommended to use `skip` instead.
## Joining relations
@ -516,7 +517,7 @@ export class Photo {
}
```
Now let's say you want to load user "Timber" with all his photos:
Now let's say you want to load user "Timber" with all of his photos:
```typescript
const user = await createQueryBuilder("user")
@ -525,7 +526,7 @@ const user = await createQueryBuilder("user")
.getOne();
```
You'll get following result:
You'll get the following result:
```typescript
{
@ -541,10 +542,10 @@ You'll get following result:
}
```
As you can see `leftJoinAndSelect` automatically loaded all of timber's photos.
As you can see `leftJoinAndSelect` automatically loaded all of Timber's photos.
The first argument is the relation you want to load and the second argument is an alias you assign to this relation's table.
You can use this alias anywhere in query builder.
For example, lets take all timber's photos which aren't removed.
For example, let's take all Timber's photos which aren't removed.
```typescript
const user = await createQueryBuilder("user")
@ -571,7 +572,7 @@ const user = await createQueryBuilder("user")
.getOne();
```
This will generate following sql query:
This will generate the following sql query:
```sql
SELECT user.*, photo.* FROM users user
@ -581,7 +582,7 @@ SELECT user.*, photo.* FROM users user
## Inner and left joins
If you want to use `INNER JOIN` instead of `JEFT JOIN` just use `innerJoinAndSelect` instead:
If you want to use `INNER JOIN` instead of `LEFT JOIN` just use `innerJoinAndSelect` instead:
```typescript
const user = await createQueryBuilder("user")
@ -598,14 +599,14 @@ SELECT user.*, photo.* FROM users user
WHERE user.name = 'Timber'
```
Difference between `LEFT JOIN` and `INNER JOIN` is that `INNER JOIN` won't return a user if it does not have any photos.
The difference between `LEFT JOIN` and `INNER JOIN` is that `INNER JOIN` won't return a user if it does not have any photos.
`LEFT JOIN` will return you the user even if it doesn't have photos.
To learn more about different join types refer to the SQL documentation.
To learn more about different join types, refer to the [SQL documentation](https://msdn.microsoft.com/en-us/library/zt8wzxy4.aspx).
## Join without selection
You can join data without its selection.
To do that use `leftJoin` or `innerJoin`:
To do that, use `leftJoin` or `innerJoin`:
```typescript
const user = await createQueryBuilder("user")
@ -622,11 +623,11 @@ SELECT user.* FROM users user
WHERE user.name = 'Timber'
```
This will select timber if he has photos, but won't return his photos.
This will select Timber if he has photos, but won't return his photos.
## Joining any entity or table
You can not only join relations, but also other not related entities or tables.
You can join not only relations, but also other unrelated entities or tables.
Examples:
```typescript
@ -660,14 +661,14 @@ const user = await createQueryBuilder("user")
.getOne();
```
This will load timber's profile photo and set it to `user.profilePhoto`.
This will load Timber's profile photo and set it to `user.profilePhoto`.
If you want to load and map a single entity use `leftJoinAndMapOne`.
If you want to load and map multiple entities use `leftJoinAndMapMany`.
## Getting the generated query
Sometimes you may want to get the SQL query generated by `QueryBuilder`.
To do it use `getSql`:
To do so, use `getSql`:
```typescript
const sql = createQueryBuilder("user")
@ -691,11 +692,11 @@ This query will return users and print the used sql statement to the console.
## Getting raw results
There are two types of results you can get using select query builder: **entities** and **raw results**.
Most of times you need to select real entities from your database, for example users.
For this purpose you use `getOne` and `getMany`.
But sometimes you need to select specific data, let's say the *sum of all user photos*.
Such data is not a entity, its called raw data.
To get raw data you use `getRawOne` and `getRawMany`.
Most of the time, you need to select real entities from your database, for example, users.
For this purpose, you use `getOne` and `getMany`.
However, sometimes you need to select specific data, like the *sum of all user photos*.
Such data is not a entity, it's called raw data.
To get raw data, you use `getRawOne` and `getRawMany`.
Examples:
```typescript
@ -719,8 +720,8 @@ const photosSums = await getRepository(User)
## Streaming result data
You can use `stream` which returns you stream.
Streaming returns you raw data and you must handle entities transformation manually:
You can use `stream` which returns you a stream.
Streaming returns you raw data and you must handle entity transformation manually:
```typescript
const stream = await getRepository(User)
@ -731,8 +732,8 @@ const stream = await getRepository(User)
## Using pagination
Most of the times when you develope an application you need pagination functionality.
This is used if you have pagination, page slider or infinite scroll components in your application.
Most of the time when you develop an application, you need pagination functionality.
This is used if you have pagination, page slider, or infinite scroll components in your application.
```typescript
const users = await getRepository(User)
@ -752,7 +753,7 @@ const users = await getRepository(User)
.getMany();
```
This will give you all users with their photos except first 10.
This will give you all except the first 10 users with their photos.
You can combine those methods:
```typescript
@ -767,14 +768,14 @@ const users = await getRepository(User)
This will skip the first 5 users and take 10 users after them.
`take` and `skip` may look like we are using `limit` and `offset`, but they don't.
`take` and `skip` may look like we are using `limit` and `offset`, but they aren't.
`limit` and `offset` may not work as you expect once you have more complicated queries with joins or subqueries.
Using `take` and `skip` will prevent those issues.
## Set locking
QueryBuilder supports both optimistic and pessimistic locking.
To use pessimistic read locking use following method:
To use pessimistic read locking use the following method:
```typescript
const users = await getRepository(User)
@ -783,7 +784,7 @@ const users = await getRepository(User)
.getMany();
```
To use pessimistic write locking use following method:
To use pessimistic write locking use the following method:
```typescript
const users = await getRepository(User)
@ -792,7 +793,7 @@ const users = await getRepository(User)
.getMany();
```
To use optimistic locking use following method:
To use optimistic locking use the following method:
```typescript
const users = await getRepository(User)
@ -801,11 +802,11 @@ const users = await getRepository(User)
.getMany();
```
Optimistic locking works in conjunction with `@Version` and `@UpdatedDate` decorators.
Optimistic locking works in conjunction with both `@Version` and `@UpdatedDate` decorators.
## Partial selection
If you want to select only some entity properties you can use the following syntax:
If you want to select only some entity properties, you can use the following syntax:
````typescript
const users = await getRepository(User)
@ -817,7 +818,7 @@ const users = await getRepository(User)
.getMany();
````
This will only select `id` and `name` of `User`.
This will only select the `id` and `name` of `User`.
## Using subqueries
@ -832,7 +833,7 @@ const posts = qb
.getMany();
```
More elegant way to do the same:
A more elegant way to do the same:
```typescript
const posts = await connection.getRepository(Post)
@ -897,7 +898,7 @@ const posts = await connection
If you want to add a subselect as a "second from" use `addFrom`.
You can use subselects in a `SELECT` statements as well:
You can use subselects in `SELECT` statements as well:
```typescript
const posts = await connection
@ -912,3 +913,37 @@ const posts = await connection
.from(Post, "post")
.getRawMany();
```
## Hidden Columns
If the model you are querying has a column with a `select: false` column, you must use the `addSelect` function in order to retreive the information from the column.
Let's say you have the following entity:
```typescript
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column({select: false})
password: string;
}
```
Using a standard `find` or query, you will not recieve the `name` property for the model. However, if you do the following:
```typescript
const users = await connection.getRepository(User)
.createQueryBuilder()
.select("user.id", "id")
.addSelect("user.password")
.getMany();
```
You will get the property `password` in your query.

View File

@ -138,9 +138,9 @@ export class Task {
}
```
Its highly recommended to define one entity class per file.
It's highly recommended to define one entity class per file.
TypeORM allows you to use your classes as database models
and provides you a declarative way to define what part of your model
and provides a declarative way to define what part of your model
will become part of your database table.
The power of TypeScript gives you type hinting and other useful features that you can use in classes.
@ -257,13 +257,13 @@ or
const employee = Employee.create({ name: "John Doe", title: "senior engineer" });
```
if you want to load an exist entity from the database and replace some of its properties you can use following method:
if you want to load an existing entity from the database and replace some of its properties you can use the following method:
```typescript
const employee = await Employee.preload({ id: 1, name: "John Doe" });
```
To access properties in sequelize you do following:
To access properties in sequelize you do the following:
```typescript
console.log(employee.get('name'));
@ -295,4 +295,4 @@ In TypeORM you do:
@Index(["firstName", "lastName"], { unique: true })
export class User {
}
```
```

View File

@ -2,18 +2,18 @@
### Found a bug or want to propose a new feature?
If you found a bug, issue, or you just want to propose a new feature create [an issue on github](https://github.com/typeorm/typeorm/issues).
If you found a bug, issue, or you just want to propose a new feature, create [an issue on github](https://github.com/typeorm/typeorm/issues).
### Have a question?
If you have a question you can ask it on [StackOverflow](https://stackoverflow.com/questions/tagged/typeorm).
If you have a question, you can ask it on [StackOverflow](https://stackoverflow.com/questions/tagged/typeorm).
### Want a community support?
### Want community support?
If you want a community support or simply want to chat with friendly TypeORM enthusiasts and users you can do it in [gitter](https://gitter.im/typeorm/typeorm).
If you want community support, or simply want to chat with friendly TypeORM enthusiasts and users, you can do it in [gitter](https://gitter.im/typeorm/typeorm).
### Want a professional commercial support?
### Want professional commercial support?
The TypeORM core team is always ready to provide professional commercial support.
We are ready to work with any team in any part of the world.
[Please contact us](mailto:support@typeorm.io).
[Please contact us](mailto:support@typeorm.io).

View File

@ -81,8 +81,8 @@ using the `@TransactionRepository() customRepository: CustomRepository`.
`QueryRunner` provides a single database connection.
Transactions are organized using query runners.
Single transaction can be established only on a single query runner.
You can manally create a query runner instance and control transaction state manually using it.
Single transactions can only be established on a single query runner.
You can manually create a query runner instance and use it to manually control transaction state.
Example:
```typescript
@ -125,8 +125,8 @@ try {
There are 3 methods to control transactions in `QueryRunner`:
* `startTransaction` - starts a new transaction inside the query runner instance
* `commitTransaction` - commits all changes made using the query runner instance
* `rollbackTransaction` - rolls all changes made using the query runner instance back
* `startTransaction` - starts a new transaction inside the query runner instance.
* `commitTransaction` - commits all changes made using the query runner instance.
* `rollbackTransaction` - rolls all changes made using the query runner instance back.
Learn more about [Query Runner](./query-runner.md).
Learn more about [Query Runner](./query-runner.md).

View File

@ -1,7 +1,7 @@
# Tree Entities
TypeORM supports the Adjacency list and Closure table patterns for storing tree structures.
To learn more about hierarchy table take a look at [this awesome presentation by Bill Karwin](https://www.slideshare.net/billkarwin/models-for-hierarchical-data).
To learn more about hierarchy table take a look at [this awesome presentation by Bill Karwin](https://www.slideshare.net/billkarwin/models-for-hierarchical-data).
* [Adjacency list](#adjacency-list)
* [Nested set](#nested-set)
@ -13,7 +13,8 @@ To learn more about hierarchy table take a look at [this awesome presentation by
Adjacency list is a simple model with self-referencing.
The benefit of this approach is simplicity,
drawback is that you can't load big tree in once because of join limitations.
drawback is that you can't load big trees in all at once because of join limitations.
To learn more about the benefits and use of Adjacency Lists look at [this article by Matthew Schinckel](http://schinckel.net/2014/09/13/long-live-adjacency-lists/).
Example:
```typescript
@ -43,7 +44,7 @@ export class Category {
## Nested set
Nested set is another pattern of storing tree structures in the database.
Its very efficient for reads, but bad for writes.
Its very efficient for reads, but bad for writes.
You cannot have multiple roots in nested set.
Example:
@ -98,7 +99,7 @@ export class Category {
## Closure table
Closure table stores relations between parent and child in a separate table in a special way.
Its efficient for both reads and writes.
It's efficient in both reads and writes.
Example:
```typescript

View File

@ -33,8 +33,8 @@ It creates all files needed for a basic project with TypeORM:
* src/index.ts
Then you can run `npm install` to install all dependencies.
Once all dependencies are installed you need to modify `ormconfig.json` and insert your own database settings.
After that you can run your application by running `npm start`.
Once all dependencies are installed, you need to modify `ormconfig.json` and insert your own database settings.
After that, you can run your application by running `npm start`.
All files are generated in the current directory.
If you want to generate them in a special directory you can use `--name`:
@ -72,9 +72,9 @@ You can create a new entity using CLI:
typeorm entity:create -n User
```
where `User` is entity file and class name.
where `User` is an entity file and class name.
Running the command will create a new empty entity in `entitiesDir` of the project.
To setup `entitiesDir` of the project you must add it in connection options:
To setup the `entitiesDir` of the project you must add it in connection options:
```
{
@ -103,8 +103,8 @@ You can create a new subscriber using CLI:
typeorm subscriber:create -n UserSubscriber
```
where `UserSubscriber` is subscriber file and class name.
Running following command will create a new empty subscriber in the `subscribersDir` of the project.
where `UserSubscriber` is a subscriber file and class name.
Running the following command will create a new empty subscriber in the `subscribersDir` of the project.
To setup `subscribersDir` you must add it in connection options:
```
@ -134,7 +134,7 @@ You can create a new migration using CLI:
typeorm migration:create -n UserMigration
```
where `UserMigration` is migration file and class name.
where `UserMigration` is a migration file and class name.
Running the command will create a new empty migration in the `migrationsDir` of the project.
To setup `migrationsDir` you must add it in connection options:
@ -165,7 +165,7 @@ and writes all sql queries that must be executed to update the database.
typeorm migration:generate -n UserMigration
```
Rule of thumb is to generate a migration after each entity change.
The rule of thumb is to generate a migration after each entity change.
Learn more about [Migrations](./migrations.md).
@ -181,7 +181,7 @@ Learn more about [Migrations](./migrations.md).
## Revert migrations
To revert last executed migration use following command:
To revert the last executed migration use the following command:
```
typeorm migrations:revert
@ -199,8 +199,8 @@ typeorm schema:sync
```
Be careful running this command in production -
schema sync may bring you data loose if you don't use it wisely.
Check sql queries it will run before running this query on production.
schema sync may cause data loss if you don't use it wisely.
Check which sql queries it will run before running on production.
## Log sync database schema queries without actual running them
@ -212,13 +212,13 @@ typeorm schema:log
## Drop database schema
To complete drop a database schema use:
To completely drop a database schema use:
```
typeorm schema:drop
```
Be careful with this command on production since it completely remove data from your database.
Be careful with this command on production since it completely removes data from your database.
## Run any sql query
@ -243,4 +243,4 @@ You can check what typeorm version you have installed (both local and global) by
```
typeorm version
```
```

View File

@ -2,8 +2,8 @@
`Repository` is just like `EntityManager` but its operations are limited to a concrete entity.
You can access repository via `getRepository(Entity)`
or from `Connection#getRepository` or from `EntityManager#getRepository`.
You can access repository via `getRepository(Entity)`,
`Connection#getRepository`, or `EntityManager#getRepository`.
Example:
```typescript
@ -17,7 +17,7 @@ await userRepository.save(user);
```
There are 3 types of repositories:
* `Repository` - Regular repository for any entity
* `Repository` - Regular repository for any entity.
* `TreeRepository` - Repository, extensions of `Repository` used for tree-entities
(like entities marked with `@Tree` decorator).
Has special methods to work with tree structures.

View File

@ -375,6 +375,7 @@ export class Gulpfile {
@SequenceTask("ci-tests")
ciTests() {
return [
"clean",
"compile",
"tslint",
"wait",

958
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -74,7 +74,7 @@
"ts-node": "^3.3.0",
"tslint": "^5.6.0",
"tslint-stylish": "^2.1.0",
"typescript": "^2.6.1"
"typescript": "^2.7.1"
},
"dependencies": {
"app-root-path": "^2.0.1",

View File

@ -70,7 +70,7 @@ export interface BaseConnectionOptions {
/**
* Indicates if database schema should be auto created on every application launch.
* Be careful with this option and don't use this in production - otherwise you can loose production data.
* Be careful with this option and don't use this in production - otherwise you can lose production data.
* This option is useful during debug and development.
* Alternative to it, you can use CLI and run schema:sync command.
*
@ -87,7 +87,7 @@ export interface BaseConnectionOptions {
/**
* Drops the schema each time connection is being established.
* Be careful with this option and don't use this in production - otherwise you'll loose all production data.
* Be careful with this option and don't use this in production - otherwise you'll lose all production data.
* This option is useful during debug and development.
*/
readonly dropSchema?: boolean;
@ -158,4 +158,4 @@ export interface BaseConnectionOptions {
};
}
}

View File

@ -309,6 +309,9 @@ export class MysqlDriver implements Driver {
} else if (columnMetadata.type === "simple-array") {
return DateUtils.simpleArrayToString(value);
} else if (columnMetadata.type === "simple-json") {
return DateUtils.simpleJsonToString(value);
}
return value;
@ -318,31 +321,34 @@ export class MysqlDriver implements Driver {
* Prepares given value to a value to be persisted, based on its column type or metadata.
*/
prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
if (columnMetadata.transformer)
value = columnMetadata.transformer.from(value);
if (value === null || value === undefined)
return value;
if (columnMetadata.type === Boolean) {
return value ? true : false;
value = value ? true : false;
} else if (columnMetadata.type === "datetime" || columnMetadata.type === Date) {
return DateUtils.normalizeHydratedDate(value);
value = DateUtils.normalizeHydratedDate(value);
} else if (columnMetadata.type === "date") {
return DateUtils.mixedDateToDateString(value);
value = DateUtils.mixedDateToDateString(value);
} else if (columnMetadata.type === "json") {
return typeof value === "string" ? JSON.parse(value) : value;
value = typeof value === "string" ? JSON.parse(value) : value;
} else if (columnMetadata.type === "time") {
return DateUtils.mixedTimeToString(value);
value = DateUtils.mixedTimeToString(value);
} else if (columnMetadata.type === "simple-array") {
return DateUtils.stringToSimpleArray(value);
value = DateUtils.stringToSimpleArray(value);
} else if (columnMetadata.type === "simple-json") {
value = DateUtils.stringToSimpleJson(value);
}
if (columnMetadata.transformer)
value = columnMetadata.transformer.from(value);
return value;
}
@ -371,6 +377,9 @@ export class MysqlDriver implements Driver {
} else if (column.type === "simple-array") {
return "text";
} else if (column.type === "simple-json") {
return "text";
} else {
return column.type as string || "";
}

View File

@ -289,6 +289,9 @@ export class OracleDriver implements Driver {
} else if (columnMetadata.type === "simple-array") {
return DateUtils.simpleArrayToString(value);
} else if (columnMetadata.type === "simple-json") {
return DateUtils.simpleJsonToString(value);
}
return value;
@ -298,31 +301,34 @@ export class OracleDriver implements Driver {
* Prepares given value to a value to be persisted, based on its column type or metadata.
*/
prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
if (columnMetadata.transformer)
value = columnMetadata.transformer.from(value);
if (value === null || value === undefined)
return value;
if (columnMetadata.type === Boolean) {
return value ? true : false;
value = value ? true : false;
} else if (columnMetadata.type === "datetime") {
return DateUtils.normalizeHydratedDate(value);
value = DateUtils.normalizeHydratedDate(value);
} else if (columnMetadata.type === "date") {
return DateUtils.mixedDateToDateString(value);
value = DateUtils.mixedDateToDateString(value);
} else if (columnMetadata.type === "time") {
return DateUtils.mixedTimeToString(value);
value = DateUtils.mixedTimeToString(value);
} else if (columnMetadata.type === "json") {
return JSON.parse(value);
value = JSON.parse(value);
} else if (columnMetadata.type === "simple-array") {
return DateUtils.stringToSimpleArray(value);
value = DateUtils.stringToSimpleArray(value);
} else if (columnMetadata.type === "simple-json") {
value = DateUtils.stringToSimpleJson(value);
}
if (columnMetadata.transformer)
value = columnMetadata.transformer.from(value);
return value;
}
@ -346,6 +352,9 @@ export class OracleDriver implements Driver {
} else if (column.type === "simple-array") {
type += "text";
} else if (column.type === "simple-json") {
type += "text";
} else {
type += column.type;
}

View File

@ -238,13 +238,13 @@ export class PostgresDriver implements Driver {
try {
await this.executeQuery(connection, `CREATE extension IF NOT EXISTS "uuid-ossp"`);
} catch (_) {
logger.log("warn", "At least one of the entities has uuid column, but the 'uuid-ossp' extension cannot be installed automatically. Please it manually using superuser rights");
logger.log("warn", "At least one of the entities has uuid column, but the 'uuid-ossp' extension cannot be installed automatically. Please install it manually using superuser rights");
}
if (hasCitextColumns)
try {
await this.executeQuery(connection, `CREATE extension IF NOT EXISTS "citext"`);
} catch (_) {
logger.log("warn", "At least one of the entities has citext column, but the 'citext' extension cannot be installed automatically. Please it manually using superuser rights");
logger.log("warn", "At least one of the entities has citext column, but the 'citext' extension cannot be installed automatically. Please install it manually using superuser rights");
}
release();
ok();
@ -314,6 +314,9 @@ export class PostgresDriver implements Driver {
} else if (columnMetadata.type === "simple-array") {
return DateUtils.simpleArrayToString(value);
} else if (columnMetadata.type === "simple-json") {
return DateUtils.simpleJsonToString(value);
}
return value;
@ -323,32 +326,35 @@ export class PostgresDriver implements Driver {
* Prepares given value to a value to be persisted, based on its column type or metadata.
*/
prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
if (columnMetadata.transformer)
value = columnMetadata.transformer.from(value);
if (value === null || value === undefined)
return value;
if (columnMetadata.type === Boolean) {
return value ? true : false;
value = value ? true : false;
} else if (columnMetadata.type === "datetime"
|| columnMetadata.type === Date
|| columnMetadata.type === "timestamp"
|| columnMetadata.type === "timestamp with time zone"
|| columnMetadata.type === "timestamp without time zone") {
return DateUtils.normalizeHydratedDate(value);
value = DateUtils.normalizeHydratedDate(value);
} else if (columnMetadata.type === "date") {
return DateUtils.mixedDateToDateString(value);
value = DateUtils.mixedDateToDateString(value);
} else if (columnMetadata.type === "time") {
return DateUtils.mixedTimeToString(value);
value = DateUtils.mixedTimeToString(value);
} else if (columnMetadata.type === "simple-array") {
return DateUtils.stringToSimpleArray(value);
value = DateUtils.stringToSimpleArray(value);
} else if (columnMetadata.type === "simple-json") {
value = DateUtils.stringToSimpleJson(value);
}
if (columnMetadata.transformer)
value = columnMetadata.transformer.from(value);
return value;
}
@ -409,6 +415,9 @@ export class PostgresDriver implements Driver {
} else if (column.type === "simple-array") {
type += "text";
} else if (column.type === "simple-json") {
type += "text";
} else {
type += column.type;
}

View File

@ -228,6 +228,9 @@ export class AbstractSqliteDriver implements Driver {
} else if (columnMetadata.type === "simple-array") {
return DateUtils.simpleArrayToString(value);
} else if (columnMetadata.type === "simple-json") {
return DateUtils.simpleJsonToString(value);
}
return value;
@ -237,28 +240,31 @@ export class AbstractSqliteDriver implements Driver {
* Prepares given value to a value to be hydrated, based on its column type or metadata.
*/
prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
if (columnMetadata.transformer)
value = columnMetadata.transformer.from(value);
if (value === null || value === undefined)
return value;
if (columnMetadata.type === Boolean || columnMetadata.type === "boolean") {
return value ? true : false;
value = value ? true : false;
} else if (columnMetadata.type === "datetime" || columnMetadata.type === Date) {
return DateUtils.normalizeHydratedDate(value);
value = DateUtils.normalizeHydratedDate(value);
} else if (columnMetadata.type === "date") {
return DateUtils.mixedDateToDateString(value);
value = DateUtils.mixedDateToDateString(value);
} else if (columnMetadata.type === "time") {
return DateUtils.mixedTimeToString(value);
value = DateUtils.mixedTimeToString(value);
} else if (columnMetadata.type === "simple-array") {
return DateUtils.stringToSimpleArray(value);
value = DateUtils.stringToSimpleArray(value);
} else if (columnMetadata.type === "simple-json") {
value = DateUtils.stringToSimpleJson(value);
}
if (columnMetadata.transformer)
value = columnMetadata.transformer.from(value);
return value;
}
@ -323,6 +329,9 @@ export class AbstractSqliteDriver implements Driver {
} else if (column.type === "simple-array") {
return "text";
} else if (column.type === "simple-json") {
return "text";
} else {
return column.type as string || "";
}

View File

@ -303,6 +303,9 @@ export class SqlServerDriver implements Driver {
} else if (columnMetadata.type === "simple-array") {
return DateUtils.simpleArrayToString(value);
} else if (columnMetadata.type === "simple-json") {
return DateUtils.simpleJsonToString(value);
}
return value;
@ -312,32 +315,35 @@ export class SqlServerDriver implements Driver {
* Prepares given value to a value to be persisted, based on its column type or metadata.
*/
prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
if (columnMetadata.transformer)
value = columnMetadata.transformer.from(value);
if (value === null || value === undefined)
return value;
if (columnMetadata.type === Boolean) {
return value ? true : false;
value = value ? true : false;
} else if (columnMetadata.type === "datetime"
|| columnMetadata.type === Date
|| columnMetadata.type === "datetime2"
|| columnMetadata.type === "smalldatetime"
|| columnMetadata.type === "datetimeoffset") {
return DateUtils.normalizeHydratedDate(value);
value = DateUtils.normalizeHydratedDate(value);
} else if (columnMetadata.type === "date") {
return DateUtils.mixedDateToDateString(value);
value = DateUtils.mixedDateToDateString(value);
} else if (columnMetadata.type === "time") {
return DateUtils.mixedTimeToString(value);
value = DateUtils.mixedTimeToString(value);
} else if (columnMetadata.type === "simple-array") {
return DateUtils.stringToSimpleArray(value);
value = DateUtils.stringToSimpleArray(value);
} else if (columnMetadata.type === "simple-json") {
value = DateUtils.stringToSimpleJson(value);
}
if (columnMetadata.transformer)
value = columnMetadata.transformer.from(value);
return value;
}
@ -366,6 +372,9 @@ export class SqlServerDriver implements Driver {
} else if (column.type === "simple-array") {
return "ntext";
} else if (column.type === "simple-json") {
return "ntext";
} else if (column.type === "integer") {
return "int";

View File

@ -66,6 +66,8 @@ export type SimpleColumnType =
"simple-array" // typeorm-specific, automatically mapped to string
// |"string" // typeorm-specific, automatically mapped to varchar depend on platform
|"simple-json" // typeorm-specific, automatically mapped to string
// numeric types
|"bit" // mssql
|"int2" // postgres, sqlite

View File

@ -113,6 +113,11 @@ export class WebsqlDriver extends AbstractSqliteDriver {
if (value instanceof Function) {
return value();
}
// Websql doesn't support queries boolean values. Therefore 1 and 0 has to be used.
else if ((typeof value) === "boolean") {
escapedParameters.push((value ? 1 : 0));
return "?";
} else {
if (value instanceof ArrayParameter) value = value.value;
escapedParameters.push(value);

View File

@ -0,0 +1,12 @@
/**
* Thrown when user tries to build an UPDATE query with LIMIT but the database does not support it.
*/
export class LimitOnUpdateNotSupportedError extends Error {
constructor() {
super(`Your database does not support LIMIT on UPDATE statements.`);
Object.setPrototypeOf(this, LimitOnUpdateNotSupportedError.prototype);
this.name = "LimitOnUpdateNotSupportedError";
}
}

View File

@ -7,7 +7,7 @@ export class LockNotSupportedOnGivenDriverError extends Error {
constructor() {
super();
Object.setPrototypeOf(this, LockNotSupportedOnGivenDriverError.prototype);
this.message = `Locking not supported on giver driver.`;
this.message = `Locking not supported on given driver.`;
}
}

View File

@ -1326,7 +1326,8 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> implements
const hasMainAlias = this.expressionMap.selects.some(select => select.selection === join.alias.name);
if (hasMainAlias) {
allSelects.push({ selection: this.escape(join.alias.name!) + ".*" });
excludedSelects.push({ selection: this.escape(join.alias.name!) });
const excludedSelect = this.expressionMap.selects.find(select => select.selection === join.alias.name);
excludedSelects.push(excludedSelect!);
}
}
});

View File

@ -15,6 +15,8 @@ import {MysqlDriver} from "../driver/mysql/MysqlDriver";
import {WebsqlDriver} from "../driver/websql/WebsqlDriver";
import {BroadcasterResult} from "../subscriber/BroadcasterResult";
import {AbstractSqliteDriver} from "../driver/sqlite-abstract/AbstractSqliteDriver";
import {OrderByCondition} from "../find-options/OrderByCondition";
import {LimitOnUpdateNotSupportedError} from "../error/LimitOnUpdateNotSupportedError";
/**
* Allows to build complex sql queries in a fashion way and execute those queries.
@ -39,6 +41,8 @@ export class UpdateQueryBuilder<Entity> extends QueryBuilder<Entity> implements
*/
getQuery(): string {
let sql = this.createUpdateExpression();
sql += this.createOrderByExpression();
sql += this.createLimitExpression();
return sql.trim();
}
@ -226,6 +230,71 @@ export class UpdateQueryBuilder<Entity> extends QueryBuilder<Entity> implements
*/
returning(returning: string|string[]): this;
/**
* Sets ORDER BY condition in the query builder.
* If you had previously ORDER BY expression defined,
* calling this function will override previously set ORDER BY conditions.
*
* Calling order by without order set will remove all previously set order bys.
*/
orderBy(): this;
/**
* Sets ORDER BY condition in the query builder.
* If you had previously ORDER BY expression defined,
* calling this function will override previously set ORDER BY conditions.
*/
orderBy(sort: string, order?: "ASC"|"DESC", nulls?: "NULLS FIRST"|"NULLS LAST"): this;
/**
* Sets ORDER BY condition in the query builder.
* If you had previously ORDER BY expression defined,
* calling this function will override previously set ORDER BY conditions.
*/
orderBy(order: OrderByCondition): this;
/**
* Sets ORDER BY condition in the query builder.
* If you had previously ORDER BY expression defined,
* calling this function will override previously set ORDER BY conditions.
*/
orderBy(sort?: string|OrderByCondition, order: "ASC"|"DESC" = "ASC", nulls?: "NULLS FIRST"|"NULLS LAST"): this {
if (sort) {
if (sort instanceof Object) {
this.expressionMap.orderBys = sort as OrderByCondition;
} else {
if (nulls) {
this.expressionMap.orderBys = { [sort as string]: { order, nulls } };
} else {
this.expressionMap.orderBys = { [sort as string]: order };
}
}
} else {
this.expressionMap.orderBys = {};
}
return this;
}
/**
* Adds ORDER BY condition in the query builder.
*/
addOrderBy(sort: string, order: "ASC"|"DESC" = "ASC", nulls?: "NULLS FIRST"|"NULLS LAST"): this {
if (nulls) {
this.expressionMap.orderBys[sort] = { order, nulls };
} else {
this.expressionMap.orderBys[sort] = order;
}
return this;
}
/**
* Sets LIMIT - maximum number of rows to be selected.
*/
limit(limit?: number): this {
this.expressionMap.limit = limit;
return this;
}
/**
* Optional returning/output clause.
*/
@ -387,6 +456,42 @@ export class UpdateQueryBuilder<Entity> extends QueryBuilder<Entity> implements
}
}
/**
* Creates "ORDER BY" part of SQL query.
*/
protected createOrderByExpression() {
const orderBys = this.expressionMap.allOrderBys;
if (Object.keys(orderBys).length > 0)
return " ORDER BY " + Object.keys(orderBys)
.map(columnName => {
if (typeof orderBys[columnName] === "string") {
return this.replacePropertyNames(columnName) + " " + orderBys[columnName];
} else {
return this.replacePropertyNames(columnName) + " " + (orderBys[columnName] as any).order + " " + (orderBys[columnName] as any).nulls;
}
})
.join(", ");
return "";
}
/**
* Creates "LIMIT" parts of SQL query.
*/
protected createLimitExpression(): string {
let limit: number|undefined = this.expressionMap.limit;
if (limit) {
if (this.connection.driver instanceof MysqlDriver) {
return " LIMIT " + limit;
} else {
throw new LimitOnUpdateNotSupportedError();
}
}
return "";
}
/**
* Gets array of values need to be inserted into the target table.
*/

View File

@ -174,6 +174,14 @@ export class DateUtils {
return value;
}
static simpleJsonToString(value: any): string {
return JSON.stringify(value);
}
static stringToSimpleJson(value: string) {
return JSON.parse(value);
}
// -------------------------------------------------------------------------
// Private Static Methods
// -------------------------------------------------------------------------

View File

@ -62,6 +62,7 @@ describe("database schema > column types > mssql", () => {
post.time = "15:30:00";
post.datetimeoffset = new Date();
post.simpleArray = ["A", "B", "C"];
post.simpleJson = { param: "VALUE" };
await postRepository.save(post);
const loadedPost = (await postRepository.findOne(1))!;
@ -102,6 +103,7 @@ describe("database schema > column types > mssql", () => {
loadedPost.simpleArray[0].should.be.equal(post.simpleArray[0]);
loadedPost.simpleArray[1].should.be.equal(post.simpleArray[1]);
loadedPost.simpleArray[2].should.be.equal(post.simpleArray[2]);
loadedPost.simpleJson.param.should.be.equal(post.simpleJson.param);
table!.findColumnByName("id")!.type.should.be.equal("int");
table!.findColumnByName("name")!.type.should.be.equal("nvarchar");
@ -143,6 +145,7 @@ describe("database schema > column types > mssql", () => {
table!.findColumnByName("timeObj")!.type.should.be.equal("time");
table!.findColumnByName("datetimeoffset")!.type.should.be.equal("datetimeoffset");
table!.findColumnByName("simpleArray")!.type.should.be.equal("ntext");
table!.findColumnByName("simpleJson")!.type.should.be.equal("ntext");
})));

View File

@ -114,10 +114,12 @@ export class Post {
datetimeoffset: Date;
// -------------------------------------------------------------------------
// TypeOrm Specific Type
// TypeOrm Specific Types
// -------------------------------------------------------------------------
@Column("simple-array")
simpleArray: string[];
@Column("simple-json")
simpleJson: { param: string };
}

View File

@ -57,6 +57,7 @@ describe("database schema > column types > mysql", () => {
post.classEnum1 = FruitEnum.Apple;
post.json = { id: 1, name: "Post" };
post.simpleArray = ["A", "B", "C"];
post.simpleJson = { param: "VALUE" };
await postRepository.save(post);
const loadedPost = (await postRepository.findOne(1))!;
@ -91,6 +92,7 @@ describe("database schema > column types > mysql", () => {
loadedPost.simpleArray[0].should.be.equal(post.simpleArray[0]);
loadedPost.simpleArray[1].should.be.equal(post.simpleArray[1]);
loadedPost.simpleArray[2].should.be.equal(post.simpleArray[2]);
loadedPost.simpleJson.param.should.be.equal(post.simpleJson.param);
table!.findColumnByName("id")!.type.should.be.equal("int");
table!.findColumnByName("id")!.length!.should.be.equal("11");
@ -139,6 +141,7 @@ describe("database schema > column types > mysql", () => {
table!.findColumnByName("classEnum1")!.enum![2].should.be.equal("banana");
table!.findColumnByName("json")!.type.should.be.equal("json");
table!.findColumnByName("simpleArray")!.type.should.be.equal("text");
table!.findColumnByName("simpleJson")!.type.should.be.equal("text");
})));

View File

@ -93,4 +93,6 @@ export class Post {
@Column("simple-array")
simpleArray: string[];
@Column("simple-json")
simpleJson: { param: string };
}

View File

@ -80,6 +80,7 @@ describe("database schema > column types > postgres", () => {
post.xml = "<book><title>Manual</title><chapter>...</chapter></book>";
post.array = [1, 2, 3];
post.simpleArray = ["A", "B", "C"];
post.simpleJson = { param: "VALUE" };
await postRepository.save(post);
const loadedPost = (await postRepository.findOne(1))!;
@ -144,6 +145,7 @@ describe("database schema > column types > postgres", () => {
loadedPost.simpleArray[0].should.be.equal(post.simpleArray[0]);
loadedPost.simpleArray[1].should.be.equal(post.simpleArray[1]);
loadedPost.simpleArray[2].should.be.equal(post.simpleArray[2]);
loadedPost.simpleJson.param.should.be.equal(post.simpleJson.param);
table!.findColumnByName("id")!.type.should.be.equal("integer");
table!.findColumnByName("name")!.type.should.be.equal("character varying");
@ -200,6 +202,7 @@ describe("database schema > column types > postgres", () => {
table!.findColumnByName("array")!.type.should.be.equal("integer");
table!.findColumnByName("array")!.isArray!.should.be.true;
table!.findColumnByName("simpleArray")!.type.should.be.equal("text");
table!.findColumnByName("simpleJson")!.type.should.be.equal("text");
})));

View File

@ -236,10 +236,12 @@ export class Post {
array: number[];
// -------------------------------------------------------------------------
// TypeOrm Specific Type
// TypeOrm Specific Types
// -------------------------------------------------------------------------
@Column("simple-array")
simpleArray: string[];
@Column("simple-json")
simpleJson: { param: string };
}

View File

@ -55,6 +55,7 @@ describe("database schema > column types > sqlite", () => {
post.datetime = new Date();
post.datetime.setMilliseconds(0);
post.simpleArray = ["A", "B", "C"];
post.simpleJson = { param: "VALUE" };
await postRepository.save(post);
const loadedPost = (await postRepository.findOne(1))!;
@ -90,6 +91,7 @@ describe("database schema > column types > sqlite", () => {
loadedPost.simpleArray[0].should.be.equal(post.simpleArray[0]);
loadedPost.simpleArray[1].should.be.equal(post.simpleArray[1]);
loadedPost.simpleArray[2].should.be.equal(post.simpleArray[2]);
loadedPost.simpleJson.param.should.be.equal(post.simpleJson.param);
table!.findColumnByName("id")!.type.should.be.equal("integer");
table!.findColumnByName("name")!.type.should.be.equal("varchar");
@ -120,6 +122,7 @@ describe("database schema > column types > sqlite", () => {
table!.findColumnByName("date")!.type.should.be.equal("date");
table!.findColumnByName("datetime")!.type.should.be.equal("datetime");
table!.findColumnByName("simpleArray")!.type.should.be.equal("text");
table!.findColumnByName("simpleJson")!.type.should.be.equal("text");
})));

View File

@ -109,10 +109,12 @@ export class Post {
datetime: Date;
// -------------------------------------------------------------------------
// TypeOrm Specific Type
// TypeOrm Specific Types
// -------------------------------------------------------------------------
@Column("simple-array")
simpleArray: string[];
@Column("simple-json")
simpleJson: { param: string };
}

View File

@ -23,7 +23,7 @@ describe("persistence > partial persist", () => {
// Specifications
// -------------------------------------------------------------------------
it("should persist partial entities without data loose", () => Promise.all(connections.map(async connection => {
it("should persist partial entities without data loss", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);

View File

@ -3,7 +3,9 @@ import {expect} from "chai";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils";
import {Connection} from "../../../../src/connection/Connection";
import {User} from "./entity/User";
import {MysqlDriver} from "../../../../src/driver/mysql/MysqlDriver";
import {SqlServerDriver} from "../../../../src/driver/sqlserver/SqlServerDriver";
import {LimitOnUpdateNotSupportedError} from "../../../../src/error/LimitOnUpdateNotSupportedError";
import {Photo} from "./entity/Photo";
describe("query builder > update", () => {
@ -58,6 +60,8 @@ describe("query builder > update", () => {
.where("name = :name", { name: () => connection.driver instanceof SqlServerDriver ? "SUBSTRING('Alex Messer Dimovich', 1, 11)" : "SUBSTR('Alex Messer Dimovich', 1, 11)" })
.execute();
const loadedUser1 = await connection.getRepository(User).findOne({ name: "Dima" });
expect(loadedUser1).to.exist;
loadedUser1!.name.should.be.equal("Dima");
@ -148,4 +152,37 @@ describe("query builder > update", () => {
})));
it("should perform update with limit correctly", () => Promise.all(connections.map(async connection => {
const user1 = new User();
user1.name = "Alex Messer";
const user2 = new User();
user2.name = "Muhammad Mirzoev";
const user3 = new User();
user3.name = "Brad Porter";
await connection.manager.save([user1, user2, user3]);
const limitNum = 2;
const nameToFind = "Dima Zotov";
if (connection.driver instanceof MysqlDriver) {
await connection.createQueryBuilder()
.update(User)
.set({ name: nameToFind })
.limit(limitNum)
.execute();
const loadedUsers = await connection.getRepository(User).find({ name: nameToFind });
expect(loadedUsers).to.exist;
loadedUsers!.length.should.be.equal(limitNum);
} else {
await connection.createQueryBuilder()
.update(User)
.set({ name: nameToFind })
.limit(limitNum)
.execute().should.be.rejectedWith(LimitOnUpdateNotSupportedError);
}
})));
});

View File

@ -93,4 +93,4 @@ describe("sqljs driver > autosave off", () => {
expect(saves).to.be.equal(0);
})));
});
});

View File

@ -0,0 +1,22 @@
import {Entity} from "../../../../src/decorator/entity/Entity";
import {Column} from "../../../../src/decorator/columns/Column";
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
const transformer = {
from(value: Date): number {
return value.getTime();
},
to(value: number): Date {
return new Date(value);
}
};
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column({type: Date, transformer})
ts: number;
}

View File

@ -0,0 +1,31 @@
import "reflect-metadata";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
import {Connection} from "../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {expect} from "chai";
describe("github issues > #1140 timestamp column and value transformer causes TypeError", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchema: true,
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("correctly store/load timestamp columns", () => Promise.all(connections.map(async connection => {
const date = new Date();
date.setMilliseconds(0); // Because some databases don't have millisecond resolution
const dateNumber = date.getTime();
const post = new Post();
post.ts = dateNumber;
await connection.manager.save(post);
const loadedPosts = await connection.manager.find(Post);
loadedPosts.length.should.be.equal(1);
expect(loadedPosts[0].ts).to.be.equal(dateNumber);
})));
});

View File

@ -0,0 +1,14 @@
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "../../../../src/index";
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
active: boolean;
}

View File

@ -0,0 +1,30 @@
import "reflect-metadata";
import { expect } from "chai";
import { Connection } from "../../../src/connection/Connection";
import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../utils/test-utils";
import { User } from "./entity/user";
describe("github issues > #1441 Does not load data with websql by running findone and contition boolean (Ionic)", () => {
let connections: Connection[] = [];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
enabledDrivers: ["websql"]
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("should to create a query using a boolean conditional that returns result", () => Promise.all(connections.map(async connection => {
const user = new User();
user.name = "Timber";
user.active = true;
await user.save();
let loadeduser = await User.findOne({ active: true });
expect(loadeduser).not.to.be.undefined;
})));
});

View File

@ -0,0 +1,11 @@
import { Entity, PrimaryColumn, Column } from "../../../../src/index";
@Entity()
export class Item {
@PrimaryColumn()
itemId: number;
@Column()
planId: number;
}

View File

@ -0,0 +1,11 @@
import { Entity, PrimaryColumn, Column } from "../../../../src/index";
@Entity()
export class Plan {
@PrimaryColumn()
planId: number;
@Column()
planName: string;
}

View File

@ -0,0 +1,59 @@
import "reflect-metadata";
import { expect } from "chai";
import { Connection } from "../../../src/connection/Connection";
import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../utils/test-utils";
import { Plan } from "./entity/Plan";
import { Item } from "./entity/Item";
describe("github issues > #1476 subqueries", () => {
let connections: Connection[] = [];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
enabledDrivers: ["mysql", "mariadb", "sqlite", "sqljs"]
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("should", () => Promise.all(connections.map(async connection => {
const planRepo = connection.getRepository(Plan);
const itemRepo = connection.getRepository(Item);
const plan1 = new Plan();
plan1.planId = 1;
plan1.planName = "Test";
await planRepo.save(plan1);
const item1 = new Item();
item1.itemId = 1;
item1.planId = 1;
const item2 = new Item();
item2.itemId = 2;
item2.planId = 1;
await itemRepo.save([item1, item2]);
const plans = await planRepo
.createQueryBuilder("b")
.leftJoinAndSelect(
subQuery => {
return subQuery
.select(`COUNT("planId")`, `total`)
.addSelect(`planId`)
.from(Item, "items")
.groupBy(`items.planId`);
}, "i", `i.planId = b.planId`)
.getRawMany();
expect(plans).to.not.be.undefined;
const plan = plans![0];
expect(plan.b_planId).to.be.equal(1);
expect(plan.b_planName).to.be.equal("Test");
expect(plan.total).to.be.equal(2);
expect(plan.planId).to.be.equal(1);
})));
});