mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
Merge branch 'master' into query-builder-refactoring
# Conflicts: # src/metadata/JoinColumnMetadata.ts # src/query-builder/QueryBuilder.ts # test/functional/connection/connection.ts
This commit is contained in:
commit
3a696bcec6
@ -30,6 +30,14 @@ each for its own `findOne*` or `find*` methods
|
||||
* fixes [#285](https://github.com/typeorm/typeorm/issues/285) - issue when cli commands rise `CannotCloseNotConnectedError`
|
||||
* fixes [#309](https://github.com/typeorm/typeorm/issues/309) - issue when `andHaving` didn't work without calling `having` on `QueryBuilder`
|
||||
|
||||
# 0.0.10
|
||||
|
||||
* added `ObjectLiteral` and `ObjectType` into main exports
|
||||
* fixed issue fixes [#345](https://github.com/typeorm/typeorm/issues/345).
|
||||
* fixed issue with migration not saving into the database correctly.
|
||||
Note its a breaking change if you have run migrations before and have records in the database table,
|
||||
make sure to apply corresponding changes. More info in [#360](https://github.com/typeorm/typeorm/issues/360) issue.
|
||||
|
||||
# 0.0.9 (latest)
|
||||
|
||||
* fixed bug with indices from columns are not being inherited from parent entity [#242](https://github.com/typeorm/typeorm/issues/242)
|
||||
|
||||
50
DEVELOPER.md
50
DEVELOPER.md
@ -80,7 +80,46 @@ You can link (or simply copy/paste) this directory into your project and test Ty
|
||||
|
||||
## Running Tests Locally
|
||||
|
||||
Setup your environment configuration by copying `ormconfig.json.dist` into `ormconfig.json` and
|
||||
It would be greatly appreciated if PRs that change code come with appropriate tests.
|
||||
|
||||
To create a test for a specific issue opened on github, create a file: `test/github-issues/<num>/issue-<num>.ts` where
|
||||
`<num>` is the corresponding github issue. For example, if you were creating a PR to fix github issue #363, you'd
|
||||
create `test/github-issues/363/issue-363.ts`.
|
||||
|
||||
Most tests will benefit from using this template as a starting point:
|
||||
|
||||
```ts
|
||||
import "reflect-metadata";
|
||||
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
import {expect} from "chai";
|
||||
|
||||
describe("github issues > #<issue number> <issue title>", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should <put a detailed description of what it should do here>", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
// tests go here
|
||||
|
||||
})));
|
||||
|
||||
// you can add additional tests if needed
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
If you place entities in `./entity/<entity-name>.ts` relative to your `issue-<num>.ts` file,
|
||||
they will automatically be loaded.
|
||||
|
||||
To run the tests, setup your environment configuration by copying `ormconfig.json.dist` into `ormconfig.json` and
|
||||
replacing parameters with your own.
|
||||
|
||||
Then run tests:
|
||||
@ -92,6 +131,15 @@ npm test
|
||||
You should execute test suites before submitting a PR to github.
|
||||
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.
|
||||
|
||||
>**Hint:** you can use the `--grep` flag to pass a Regex to `gulp-mocha`. Only the tests have have `describe`/`it`
|
||||
>statements that match the Regex will be run. For example:
|
||||
>
|
||||
>```shell
|
||||
>npm test -- --grep="github issues > #363"
|
||||
>```
|
||||
>
|
||||
>This is useful when trying to get a specific test or subset of tests to pass.
|
||||
|
||||
## Using Docker
|
||||
|
||||
To run your tests you need dbms installed on your machine. Alternatively, you can use docker
|
||||
|
||||
201
README.md
201
README.md
@ -1,23 +1,38 @@
|
||||
# TypeORM
|
||||
<div align="center">
|
||||
<a href="https://typeorm.github.io/">
|
||||
<img src="./resources/logo_big.png" width="492" height="228">
|
||||
</a>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://travis-ci.org/typeorm/typeorm">
|
||||
<img src="https://travis-ci.org/typeorm/typeorm.svg?branch=master">
|
||||
</a>
|
||||
<a href="https://badge.fury.io/js/typeorm">
|
||||
<img src="https://badge.fury.io/js/typeorm.svg">
|
||||
</a>
|
||||
<a href="https://david-dm.org/typeorm/typeorm">
|
||||
<img src="https://david-dm.org/typeorm/typeorm.svg">
|
||||
</a>
|
||||
<a href="https://david-dm.org/typeorm/typeorm#info=devDependencies">
|
||||
<img src="https://david-dm.org/typeorm/typeorm/dev-status.svg">
|
||||
</a>
|
||||
<a href="https://gitter.im/typeorm/typeorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge">
|
||||
<img src="https://badges.gitter.im/typeorm/typeorm.svg">
|
||||
</a>
|
||||
<br>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
[](https://travis-ci.org/typeorm/typeorm)
|
||||
[](https://badge.fury.io/js/typeorm)
|
||||
[](https://david-dm.org/typeorm/typeorm)
|
||||
[](https://david-dm.org/typeorm/typeorm#info=devDependencies)
|
||||
[](https://gitter.im/typeorm/typeorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
[](https://typeorm.github.io/)
|
||||
|
||||
> Please support a project by simply putting a github star.
|
||||
Share this library with friends on twitter and everywhere else you can.
|
||||
> Please support a project by simply putting a Github star.
|
||||
Share this library with friends on Twitter and everywhere else you can.
|
||||
|
||||
> ORM is in active development, but main API is pretty stable.
|
||||
If you notice bug or have something not working please report an issue, we'll try to fix it as soon as possible.
|
||||
More documentation and features expected to be soon. Feel free to contribute.
|
||||
|
||||
> 0.0.9 is released! Most notable changes are in the [changelog](./CHANGELOG.md).
|
||||
> For the latest release changes see [changelog](./CHANGELOG.md).
|
||||
|
||||
TypeORM is an [Object Relational Mapper](1) (ORM) for node.js written in
|
||||
TypeORM is an [Object Relational Mapper](1) (ORM) for Node.js written in
|
||||
TypeScript that can be used with TypeScript or JavaScript (ES5, ES6, ES7).
|
||||
Its goal to always support latest JavaScript features and provide features
|
||||
that help you to develop any kind of applications that use database - from
|
||||
@ -30,7 +45,7 @@ your objects
|
||||
* map your selections from tables to javascript objects and map table columns
|
||||
to javascript object's properties
|
||||
* create one-to-one, many-to-one, one-to-many, many-to-many relations between tables
|
||||
* and much more ...
|
||||
* and much more...
|
||||
|
||||
TypeORM uses Data Mapper pattern, unlike all other JavaScript ORMs that
|
||||
currently exist, which means you can write loosely coupled, scalable,
|
||||
@ -52,9 +67,9 @@ TypeORM is highly influenced by other ORMs, such as [Hibernate](http://hibernate
|
||||
|
||||
`npm install reflect-metadata --save`
|
||||
|
||||
and use it somewhere in the global place of your app:
|
||||
and import it somewhere in the global place of your app (for example in `app.ts`):
|
||||
|
||||
* `require("reflect-metadata")` in your app's entry point (for example `app.ts`)
|
||||
`import "reflect-metadata";`
|
||||
|
||||
3. You may need to install node typings:
|
||||
|
||||
@ -66,7 +81,7 @@ TypeORM is highly influenced by other ORMs, such as [Hibernate](http://hibernate
|
||||
|
||||
`npm install mysql --save`
|
||||
|
||||
* for **Postgres**
|
||||
* for **PostgreSQL**
|
||||
|
||||
`npm install pg --save`
|
||||
|
||||
@ -96,12 +111,12 @@ Also make sure you are using TypeScript compiler version > **2.1** and you have
|
||||
"experimentalDecorators": true,
|
||||
```
|
||||
|
||||
You'll also need to enable `es6` in the `lib` section of compiler options, or install `es6-shim` from `@typings`.
|
||||
You'll also need to enable `es6` in the `lib` section of compiler options, or install `es6-shim` from `@types`.
|
||||
|
||||
#### Node.js version
|
||||
|
||||
TypeORM was tested with Node.JS version 4 and above.
|
||||
If you have errors during app bootstrap, try to upgrade your node.js version to the latest version.
|
||||
TypeORM was tested with Node.js version 4 and above.
|
||||
If you have errors during app bootstrap, try to upgrade your Node.js version to the latest version.
|
||||
|
||||
#### Usage in the browser with WebSQL (experimental)
|
||||
|
||||
@ -129,7 +144,7 @@ export class Photo {
|
||||
|
||||
### Creating entity
|
||||
|
||||
Now lets make it entity:
|
||||
Now let's make it entity:
|
||||
|
||||
```typescript
|
||||
import {Entity} from "typeorm";
|
||||
@ -214,7 +229,7 @@ export class Photo {
|
||||
|
||||
### Create auto-increment / generated / sequence / identity column
|
||||
|
||||
Now, lets say you want to make your id column to be auto-generated (this is known as auto-increment / sequence / generated identity column).
|
||||
Now, let's say you want to make your id column to be auto-generated (this is known as auto-increment / sequence / generated identity column).
|
||||
To do that you need to change your column's type to integer and set a `{ generated: true }` in your primary column's options:
|
||||
|
||||
```typescript
|
||||
@ -278,10 +293,10 @@ export class Photo {
|
||||
|
||||
### Custom column data types
|
||||
|
||||
Next step, lets fix our data types. By default, string is mapped to a varchar(255)-like type (depend of database type).
|
||||
Next step, let's fix our data types. By default, string is mapped to a varchar(255)-like type (depend of database type).
|
||||
Number is mapped to a float/double-like type (depend of database type).
|
||||
We don't want all our columns to be limited varchars or excessive floats.
|
||||
Lets setup correct data types:
|
||||
Let's setup correct data types:
|
||||
|
||||
```typescript
|
||||
import {Entity, Column, PrimaryGeneratedColumn} from "typeorm";
|
||||
@ -313,7 +328,7 @@ export class Photo {
|
||||
|
||||
### Creating connection with the database
|
||||
|
||||
Now, when our entity is created, lets create `app.ts` file and setup our connection there:
|
||||
Now, when our entity is created, let's create `app.ts` file and setup our connection there:
|
||||
|
||||
```typescript
|
||||
import "reflect-metadata";
|
||||
@ -334,11 +349,11 @@ createConnection({
|
||||
],
|
||||
autoSchemaSync: true,
|
||||
}).then(connection => {
|
||||
// here you can start to work with your entities
|
||||
// Here you can start to work with your entities
|
||||
}).catch(error => console.log(error));
|
||||
```
|
||||
|
||||
We are using mysql in this example, but you can use any other database.
|
||||
We are using MySQL in this example, but you can use any other database.
|
||||
To use another database simply change type in the driver options to the database type you are using:
|
||||
mysql, mariadb, postgres, sqlite, mssql or oracle.
|
||||
Also make sure to use your own host, port, username, password and database settings.
|
||||
@ -371,7 +386,7 @@ createConnection({
|
||||
],
|
||||
autoSchemaSync: true,
|
||||
}).then(connection => {
|
||||
// here you can start to work with your entities
|
||||
// Here you can start to work with your entities
|
||||
}).catch(error => console.log(error));
|
||||
```
|
||||
|
||||
@ -392,12 +407,11 @@ Now you if run your `app.ts`, connection with database will be initialized and d
|
||||
| isPublished | boolean | |
|
||||
+-------------+--------------+----------------------------+
|
||||
```
|
||||
|
||||
Now you can run your `app.ts`, connection with database will be initialized, and database table for your Photo will be created.
|
||||
|
||||
|
||||
### Creating and inserting photo into the database
|
||||
|
||||
Now lets create a new photo to save it in the database:
|
||||
Now let's create a new photo to save it in the database:
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
@ -422,7 +436,7 @@ createConnection(/*...*/).then(connection => {
|
||||
|
||||
### Using async/await syntax
|
||||
|
||||
Lets use latest TypeScript advantages and use async/await syntax instead:
|
||||
Let's use latest TypeScript advantages and use async/await syntax instead:
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
@ -448,7 +462,7 @@ createConnection(/*...*/).then(async connection => {
|
||||
We just created a new photo and saved it in the database.
|
||||
We used `EntityManager` to save it.
|
||||
Using entity managers you can manipulate any entity in your app.
|
||||
Now lets load our saved entity:
|
||||
Now let's load our saved entity:
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
@ -467,7 +481,7 @@ savedPhotos will be an array of Photo objects with the data loaded from the data
|
||||
|
||||
### Using Repositories
|
||||
|
||||
Now lets refactor our code and use `Repository` instead of EntityManager.
|
||||
Now let's refactor our code and use `Repository` instead of EntityManager.
|
||||
Each entity has its own repository which handles all operations with its entity.
|
||||
When you deal with entities a lot, Repositories are more convenient to use then EntityManager:
|
||||
|
||||
@ -498,7 +512,7 @@ createConnection(/*...*/).then(async connection => {
|
||||
|
||||
### Loading photos from the database
|
||||
|
||||
Lets try more load operations using Repository:
|
||||
Let's try more load operations using Repository:
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
@ -523,15 +537,15 @@ createConnection(/*...*/).then(async connection => {
|
||||
console.log("All published photos: ", allPublishedPhotos);
|
||||
|
||||
let [allPhotos, photosCount] = await photoRepository.findAndCount();
|
||||
console.log("All photos: ", allPublishedPhotos);
|
||||
console.log("Photos count: ", allPublishedPhotos);
|
||||
console.log("All photos: ", allPhotos);
|
||||
console.log("Photos count: ", photosCount);
|
||||
|
||||
}).catch(error => console.log(error));
|
||||
```
|
||||
|
||||
### Updating photo in the database
|
||||
|
||||
Now lets load a single photo from the database, update it and save it:
|
||||
Now let's load a single photo from the database, update it and save it:
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
@ -553,7 +567,6 @@ Now photo with `id = 1` will be updated in the database.
|
||||
|
||||
Now let's remove our photo from the database:
|
||||
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
import {Photo} from "./entity/Photo";
|
||||
@ -569,10 +582,10 @@ createConnection(/*...*/).then(async connection => {
|
||||
|
||||
Now photo with `id = 1` will be removed from the database.
|
||||
|
||||
### creating a one-to-one relation
|
||||
### Creating a one-to-one relation
|
||||
|
||||
Lets create a one-to-one relation with another class.
|
||||
Lets create a new class called PhotoMetadata.ts which will contain a PhotoMetadata class which supposed to contain our photo's additional meta-information:
|
||||
Let's create a one-to-one relation with another class.
|
||||
Let's create a new class called `PhotoMetadata.ts` which will contain a PhotoMetadata class which supposed to contain our photo's additional meta-information:
|
||||
|
||||
```typescript
|
||||
import {Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn} from "typeorm";
|
||||
@ -612,7 +625,7 @@ We can also write it as a `() => Photo`, but we use `type => Photo as convention
|
||||
Type variable itself does not contain anything.
|
||||
|
||||
We also put `@JoinColumn` decorator, which indicates that this side of the relationship will be owning relationship.
|
||||
Relations can be a uni-directional and bi-directional.
|
||||
Relations can be a unidirectional and bidirectional.
|
||||
Only one side of relational can be owner.
|
||||
Using this decorator is required on owner side of the relationship.
|
||||
|
||||
@ -620,7 +633,7 @@ If you run the app you'll see a new generated table, and it will contain a colum
|
||||
|
||||
```shell
|
||||
+-------------+--------------+----------------------------+
|
||||
| photo |
|
||||
| photometadata |
|
||||
+-------------+--------------+----------------------------+
|
||||
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
|
||||
| height | int(11) | |
|
||||
@ -632,9 +645,9 @@ If you run the app you'll see a new generated table, and it will contain a colum
|
||||
+-------------+--------------+----------------------------+
|
||||
```
|
||||
|
||||
### persisting an object with one-to-one relation
|
||||
### Persisting an object with one-to-one relation
|
||||
|
||||
Now lets save a photo, its metadata and attach them to each other.
|
||||
Now let's save a photo, its metadata and attach them to each other.
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
@ -643,14 +656,14 @@ import {PhotoMetadata} from "./entity/PhotoMetadata";
|
||||
|
||||
createConnection(/*...*/).then(async connection => {
|
||||
|
||||
// create a photo
|
||||
// Create a photo
|
||||
let photo = new Photo();
|
||||
photo.name = "Me and Bears";
|
||||
photo.description = "I am near polar bears";
|
||||
photo.filename = "photo-with-bears.jpg"
|
||||
photo.isPublished = true;
|
||||
|
||||
// create a photo metadata
|
||||
// Create a photo metadata
|
||||
let metadata = new PhotoMetadata();
|
||||
metadata.height = 640;
|
||||
metadata.width = 480;
|
||||
@ -659,29 +672,29 @@ createConnection(/*...*/).then(async connection => {
|
||||
metadata.orientation = "portait";
|
||||
metadata.photo = photo; // this way we connect them
|
||||
|
||||
// get entity repositories
|
||||
// Get entity repositories
|
||||
let photoRepository = connection.getRepository(Photo);
|
||||
let metadataRepository = connection.getRepository(PhotoMetadata);
|
||||
|
||||
// first we should persist a photo
|
||||
// First we should persist a photo
|
||||
await photoRepository.persist(photo);
|
||||
|
||||
// photo is saved. Now we need to persist a photo metadata
|
||||
// Photo is saved. Now we need to persist a photo metadata
|
||||
await metadataRepository.persist(metadata);
|
||||
|
||||
// done
|
||||
console.log("metadata is saved, and relation between metadata and photo is created in the database too");
|
||||
// Done
|
||||
console.log("Metadata is saved, and relation between metadata and photo is created in the database too");
|
||||
|
||||
}).catch(error => console.log(error));
|
||||
```
|
||||
|
||||
### Adding inverse side of a relation
|
||||
|
||||
Relations can be a uni-directional and bi-directional.
|
||||
Now, relation between PhotoMetadata and Photo is uni-directional.
|
||||
Relations can be a unidirectional and bidirectional.
|
||||
Now, relation between PhotoMetadata and Photo is unidirectional.
|
||||
Owner of the relation is PhotoMetadata and Photo doesn't know anything about PhotoMetadata.
|
||||
This makes complicated accessing a photo metadata from the photo objects.
|
||||
To fix it we should add inverse relation and make relations between PhotoMetadata and Photo bi-directional.
|
||||
To fix it we should add inverse relation and make relations between PhotoMetadata and Photo bidirectional.
|
||||
Let's modify our entities:
|
||||
|
||||
```typescript
|
||||
@ -724,9 +737,9 @@ Owning side of relationship contain a column with a foreign key in the database.
|
||||
|
||||
### Loading object with their relations
|
||||
|
||||
Now lets load our photo, and its photo metadata in a single query.
|
||||
Now let's load our photo, and its photo metadata in a single query.
|
||||
There are two ways to do it - one you can use `FindOptions`, second is to use QueryBuilder.
|
||||
Lets use FindOptions first.
|
||||
Let's use FindOptions first.
|
||||
`Repository.find` method allows you to specify object with FindOptions interface.
|
||||
Using this you can customize your query to perform more complex queries.
|
||||
|
||||
@ -759,7 +772,7 @@ We also used `innerJoinAndSelect` to inner and join and select the data from pho
|
||||
In `"photo.metadata"` "photo" is an alias you used, and "metadata" is a property name with relation of the object you are selecting.
|
||||
`"metadata"`: is a new alias to the data returned by join expression.
|
||||
|
||||
Lets use `QueryBuilder` for the same purpose. QueryBuilder allows to use more complex queries in an elegant way:
|
||||
Let's use `QueryBuilder` for the same purpose. QueryBuilder allows to use more complex queries in an elegant way:
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
@ -778,7 +791,7 @@ createConnection(/*...*/).then(async connection => {
|
||||
}).catch(error => console.log(error));
|
||||
```
|
||||
|
||||
### using cascade options to automatically save related objects
|
||||
### Using cascade options to automatically save related objects
|
||||
|
||||
We can setup cascade options in our relations, in the cases when we want our related object to be persisted whenever other object is saved.
|
||||
Let's change our photo's `@OneToOne` decorator a bit:
|
||||
@ -807,14 +820,14 @@ Now we can simply persist a photo object, and metadata object will persist autom
|
||||
```typescript
|
||||
createConnection(options).then(async connection => {
|
||||
|
||||
// create photo object
|
||||
// Create photo object
|
||||
let photo = new Photo();
|
||||
photo.name = "Me and Bears";
|
||||
photo.description = "I am near polar bears";
|
||||
photo.filename = "photo-with-bears.jpg"
|
||||
photo.isPublished = true;
|
||||
|
||||
// create photo metadata object
|
||||
// Create photo metadata object
|
||||
let metadata = new PhotoMetadata();
|
||||
metadata.height = 640;
|
||||
metadata.width = 480;
|
||||
@ -824,10 +837,10 @@ createConnection(options).then(async connection => {
|
||||
|
||||
photo.metadata = metadata; // this way we connect them
|
||||
|
||||
// get repository
|
||||
// Get repository
|
||||
let photoRepository = connection.getRepository(Photo);
|
||||
|
||||
// first we should persist a photo
|
||||
// Persisting a photo also persist the metadata
|
||||
await photoRepository.persist(photo);
|
||||
|
||||
console.log("Photo is saved, photo metadata is saved too.")
|
||||
@ -835,11 +848,11 @@ createConnection(options).then(async connection => {
|
||||
}).catch(error => console.log(error));
|
||||
```
|
||||
|
||||
### creating a many-to-one / one-to-many relation
|
||||
### Creating a many-to-one / one-to-many relation
|
||||
|
||||
Lets create a many-to-one / one-to-many relation.
|
||||
Lets say a photo has one author, and each author can have many photos.
|
||||
First, lets create Author class:
|
||||
Let's create a many-to-one / one-to-many relation.
|
||||
Let's say a photo has one author, and each author can have many photos.
|
||||
First, let's create Author class:
|
||||
|
||||
```typescript
|
||||
import {Entity, Column, PrimaryGeneratedColumn, OneToMany, JoinColumn} from "typeorm";
|
||||
@ -854,7 +867,7 @@ export class Author {
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@OneToMany(type => Photo, photo => photo.author) // note: we will create author property in the Photo class below
|
||||
@OneToMany(type => Photo, photo => photo.author) // Note: we will create author property in the Photo class below
|
||||
photos: Photo[];
|
||||
}
|
||||
```
|
||||
@ -862,7 +875,7 @@ export class Author {
|
||||
Author contains an inverse side of a relationship.
|
||||
OneToMany is always an inverse side of relation, and it can't exist without ManyToOne of the other side of relationship.
|
||||
|
||||
Now lets add owner side of relationship into the Photo entity:
|
||||
Now let's add owner side of relationship into the Photo entity:
|
||||
|
||||
```typescript
|
||||
import {Entity, Column, PrimaryGeneratedColumn, ManyToOne} from "typeorm";
|
||||
@ -909,11 +922,11 @@ It will also modify photo table - add a new column author and create a foreign k
|
||||
+-------------+--------------+----------------------------+
|
||||
```
|
||||
|
||||
### creating a many-to-many relation
|
||||
### Creating a many-to-many relation
|
||||
|
||||
Lets create a many-to-one / many-to-many relation.
|
||||
Lets say a photo can be in many albums, and multiple can have many photos.
|
||||
Lets create an `Album` class:
|
||||
Let's create a many-to-one / many-to-many relation.
|
||||
Let's say a photo can be in many albums, and multiple can have many photos.
|
||||
Let's create an `Album` class:
|
||||
|
||||
```typescript
|
||||
import {Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable} from "typeorm";
|
||||
@ -927,30 +940,30 @@ export class Album {
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToMany(type => Photo, photo => photo.albums, { // note: we will create "albums" property in the Photo class below
|
||||
cascadeInsert: true, // allow to insert a new photo on album save
|
||||
cascadeUpdate: true, // allow to update a photo on album save
|
||||
cascadeRemove: true // allow to remove a photo on album remove
|
||||
@ManyToMany(type => Photo, photo => photo.albums, { // Note: we will create "albums" property in the Photo class below
|
||||
cascadeInsert: true, // Allow to insert a new photo on album save
|
||||
cascadeUpdate: true, // Allow to update a photo on album save
|
||||
cascadeRemove: true // Allow to remove a photo on album remove
|
||||
})
|
||||
@JoinTable()
|
||||
photos: Photo[] = []; // we initialize array for convinience here
|
||||
photos: Photo[] = []; // We initialize array for convinience here
|
||||
}
|
||||
```
|
||||
|
||||
`@JoinTable` is required to specify that this is owner side of the relationship.
|
||||
|
||||
Now lets add inverse side of our relation to the `Photo` class:
|
||||
Now let's add inverse side of our relation to the `Photo` class:
|
||||
|
||||
```typescript
|
||||
export class Photo {
|
||||
/// ... other columns
|
||||
|
||||
@ManyToMany(type => Album, album => album.photos, {
|
||||
cascadeInsert: true, // allow to insert a new album on photo save
|
||||
cascadeUpdate: true, // allow to update an album on photo save
|
||||
cascadeRemove: true // allow to remove an album on photo remove
|
||||
cascadeInsert: true, // Allow to insert a new album on photo save
|
||||
cascadeUpdate: true, // Allow to update an album on photo save
|
||||
cascadeRemove: true // Allow to remove an album on photo remove
|
||||
})
|
||||
albums: Album[] = []; // we initialize array for convinience here
|
||||
albums: Album[] = []; // We initialize array for convinience here
|
||||
}
|
||||
```
|
||||
|
||||
@ -974,19 +987,19 @@ const options: CreateConnectionOptions = {
|
||||
};
|
||||
```
|
||||
|
||||
Now lets insert albums and photos to our database:
|
||||
Now let's insert albums and photos to our database:
|
||||
|
||||
```typescript
|
||||
let connection = await createConnection(options);
|
||||
|
||||
// create a few albums
|
||||
// Create a few albums
|
||||
let album1 = new Album();
|
||||
album1.name = "Bears";
|
||||
|
||||
let album2 = new Album();
|
||||
album2.name = "Me";
|
||||
|
||||
// create a few photos
|
||||
// Create a few photos
|
||||
let photo1 = new Photo();
|
||||
photo1.name = "Me and Bears";
|
||||
photo1.description = "I am near polar bears";
|
||||
@ -999,23 +1012,23 @@ photo2.description = "I am near polar bears";
|
||||
photo2.filename = "photo-with-bears.jpg";
|
||||
photo2.albums.push(album2);
|
||||
|
||||
// get entity repository
|
||||
// Get entity repository
|
||||
let photoRepository = connection.getRepository(Photo);
|
||||
|
||||
// first save a first photo
|
||||
// we only save the photos, albums are persisted
|
||||
// First save a first photo
|
||||
// We only save the photos, albums are persisted
|
||||
// automatically because of cascade options
|
||||
await photoRepository.persist(photo1);
|
||||
|
||||
// second save a first photo
|
||||
// Second save a first photo
|
||||
await photoRepository.persist(photo2);
|
||||
|
||||
console.log("Both photos have been saved");
|
||||
```
|
||||
|
||||
### using QueryBuilder
|
||||
### Using QueryBuilder
|
||||
|
||||
You can use QueryBuilder to build even more complex queries. For example you can do this:
|
||||
You can use QueryBuilder to build even more complex queries. For example, you can do this:
|
||||
|
||||
```typescript
|
||||
let photoRepository = connection.getRepository(Photo);
|
||||
@ -1069,6 +1082,8 @@ There are few repositories which you can clone and start with:
|
||||
* [Example how to use TypeORM with JavaScript](https://github.com/typeorm/javascript-example)
|
||||
* [Example how to use TypeORM with JavaScript and Babel](https://github.com/typeorm/babel-example)
|
||||
* [Example how to use TypeORM with TypeScript and SystemJS in Browser](https://github.com/typeorm/browser-example)
|
||||
* [Example how to use Express and TypeORM with TypeScript](https://github.com/typeorm/typescript-express-example)
|
||||
* [Example how to use Koa and TypeORM with TypeScript](https://github.com/typeorm/typescript-koa-example)
|
||||
|
||||
## Extensions
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ const sourcemaps = require("gulp-sourcemaps");
|
||||
const istanbul = require("gulp-istanbul");
|
||||
const remapIstanbul = require("remap-istanbul/lib/gulpRemapIstanbul");
|
||||
const ts = require("gulp-typescript");
|
||||
const args = require('yargs').argv;
|
||||
|
||||
@Gulpclass()
|
||||
export class Gulpfile {
|
||||
@ -297,6 +298,7 @@ export class Gulpfile {
|
||||
return gulp.src(["./build/compiled/test/**/*.js"])
|
||||
.pipe(mocha({
|
||||
bail: true,
|
||||
grep: !!args.grep ? new RegExp(args.grep) : undefined,
|
||||
timeout: 15000
|
||||
}))
|
||||
.pipe(istanbul.writeReports());
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@ -310,7 +310,7 @@ export class Connection {
|
||||
* Imports naming strategies from the given paths (directories) and registers them in the current connection.
|
||||
*/
|
||||
importNamingStrategiesFromDirectories(paths: string[]): this {
|
||||
this.importEntities(importClassesFromDirectories(paths));
|
||||
this.importNamingStrategies(importClassesFromDirectories(paths));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@ -80,12 +80,16 @@ export interface ConnectionOptions {
|
||||
*
|
||||
* Note that for MongoDB database it does not create schema, because MongoDB is schemaless.
|
||||
* Instead, it syncs just by creating indices.
|
||||
*
|
||||
* todo: rename it simply to synchronize: boolean ?
|
||||
*/
|
||||
readonly autoSchemaSync?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if migrations should be auto run on every application launch.
|
||||
* Alternative to it, you can use CLI and run migration:create command.
|
||||
*
|
||||
* todo: rename it simply to runMigrations: boolean ?
|
||||
*/
|
||||
readonly autoMigrationsRun?: boolean;
|
||||
|
||||
|
||||
@ -3,6 +3,9 @@ import {NamingStrategyMetadataArgs} from "../metadata-args/NamingStrategyMetadat
|
||||
|
||||
/**
|
||||
* Decorator registers a new naming strategy to be used in naming things.
|
||||
*
|
||||
* todo: deprecate using naming strategies this way. use it without decorators
|
||||
* todo: but add multiple default naming strategies for use
|
||||
*/
|
||||
export function NamingStrategy(name?: string): Function {
|
||||
return function (target: Function) {
|
||||
|
||||
@ -30,4 +30,3 @@ export function CreateDateColumn(options?: ColumnOptions): Function {
|
||||
getMetadataArgsStorage().columns.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import {ColumnOptions} from "../options/ColumnOptions";
|
||||
import {ColumnTypes} from "../../metadata/types/ColumnTypes";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {PrimaryColumnCannotBeNullableError} from "../error/PrimaryColumnCannotBeNullableError";
|
||||
import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs";
|
||||
|
||||
@ -80,4 +80,10 @@ export interface ColumnOptions {
|
||||
*/
|
||||
readonly localTimezone?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if column's type will be set as a fixed-length data type.
|
||||
* Works only with "string" columns.
|
||||
*/
|
||||
readonly fixedLength?: boolean;
|
||||
|
||||
}
|
||||
|
||||
@ -643,7 +643,7 @@ export class MongoQueryRunner implements QueryRunner {
|
||||
/**
|
||||
* Creates a database type from a given column metadata.
|
||||
*/
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean }) {
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean, fixedLength?: boolean }): string {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
|
||||
@ -684,11 +684,15 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
/**
|
||||
* Creates a database type from a given column metadata.
|
||||
*/
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean }) {
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean, fixedLength?: boolean }): string {
|
||||
|
||||
switch (typeOptions.type) {
|
||||
case "string":
|
||||
return "varchar(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
if (typeOptions.fixedLength) {
|
||||
return "char(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
} else {
|
||||
return "varchar(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
}
|
||||
case "text":
|
||||
return "text";
|
||||
case "boolean":
|
||||
|
||||
@ -196,7 +196,7 @@ export class OracleDriver implements Driver {
|
||||
const keys = Object.keys(parameters).map(parameter => "(:" + parameter + "\\b)").join("|");
|
||||
sql = sql.replace(new RegExp(keys, "g"), (key: string) => {
|
||||
escapedParameters.push(parameters[key.substr(1)]);
|
||||
return ":" + key;
|
||||
return key;
|
||||
}); // todo: make replace only in value statements, otherwise problems
|
||||
return [sql, escapedParameters];
|
||||
}
|
||||
|
||||
@ -742,10 +742,14 @@ AND cons.constraint_name = cols.constraint_name AND cons.owner = cols.owner ORDE
|
||||
/**
|
||||
* Creates a database type from a given column metadata.
|
||||
*/
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean }) {
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean, fixedLength?: boolean }): string {
|
||||
switch (typeOptions.type) {
|
||||
case "string":
|
||||
return "varchar2(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
if (typeOptions.fixedLength) {
|
||||
return "char(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
} else {
|
||||
return "varchar2(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
}
|
||||
case "text":
|
||||
return "clob";
|
||||
case "boolean":
|
||||
|
||||
@ -258,15 +258,15 @@ export class PostgresQueryRunner implements QueryRunner {
|
||||
const tableNamesString = tableNames.map(name => "'" + name + "'").join(", ");
|
||||
const tablesSql = `SELECT * FROM information_schema.tables WHERE table_catalog = '${this.dbName}' AND table_schema = '${this.schemaName}' AND table_name IN (${tableNamesString})`;
|
||||
const columnsSql = `SELECT * FROM information_schema.columns WHERE table_catalog = '${this.dbName}' AND table_schema = '${this.schemaName}'`;
|
||||
const indicesSql = `SELECT t.relname AS table_name, i.relname AS index_name, a.attname AS column_name FROM pg_class t, pg_class i, pg_index ix, pg_attribute a
|
||||
const indicesSql = `SELECT t.relname AS table_name, i.relname AS index_name, a.attname AS column_name FROM pg_class t, pg_class i, pg_index ix, pg_attribute a, pg_namespace ns
|
||||
WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid
|
||||
AND a.attnum = ANY(ix.indkey) AND t.relkind = 'r' AND t.relname IN (${tableNamesString}) ORDER BY t.relname, i.relname`;
|
||||
const foreignKeysSql = `SELECT table_name, constraint_name FROM information_schema.table_constraints WHERE table_catalog = '${this.dbName}' AND constraint_type = 'FOREIGN KEY'`;
|
||||
const uniqueKeysSql = `SELECT * FROM information_schema.table_constraints WHERE table_catalog = '${this.dbName}' AND constraint_type = 'UNIQUE'`;
|
||||
AND a.attnum = ANY(ix.indkey) AND t.relkind = 'r' AND t.relname IN (${tableNamesString}) AND t.relnamespace = ns.OID AND ns.nspname ='${this.schemaName}' ORDER BY t.relname, i.relname`;
|
||||
const foreignKeysSql = `SELECT table_name, constraint_name FROM information_schema.table_constraints WHERE table_catalog = '${this.dbName}' AND table_schema = '${this.schemaName}' AND constraint_type = 'FOREIGN KEY'`;
|
||||
const uniqueKeysSql = `SELECT * FROM information_schema.table_constraints WHERE table_catalog = '${this.dbName}' AND table_schema = '${this.schemaName}' AND constraint_type = 'UNIQUE'`;
|
||||
const primaryKeysSql = `SELECT c.column_name, tc.table_name, tc.constraint_name FROM information_schema.table_constraints tc
|
||||
JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name)
|
||||
JOIN information_schema.columns AS c ON c.table_schema = tc.constraint_schema AND tc.table_name = c.table_name AND ccu.column_name = c.column_name
|
||||
where constraint_type = 'PRIMARY KEY' and tc.table_catalog = '${this.dbName}'`;
|
||||
where constraint_type = 'PRIMARY KEY' AND c.table_schema = '${this.schemaName}' and tc.table_catalog = '${this.dbName}'`;
|
||||
const [dbTables, dbColumns, dbIndices, dbForeignKeys, dbUniqueKeys, primaryKeys]: ObjectLiteral[][] = await Promise.all([
|
||||
this.query(tablesSql),
|
||||
this.query(columnsSql),
|
||||
@ -744,10 +744,14 @@ where constraint_type = 'PRIMARY KEY' and tc.table_catalog = '${this.dbName}'`;
|
||||
/**
|
||||
* Creates a database type from a given column metadata.
|
||||
*/
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean }): string {
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean, fixedLength?: boolean }): string {
|
||||
switch (typeOptions.type) {
|
||||
case "string":
|
||||
return "character varying(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
if (typeOptions.fixedLength) {
|
||||
return "character(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
} else {
|
||||
return "character varying(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
}
|
||||
case "text":
|
||||
return "text";
|
||||
case "boolean":
|
||||
|
||||
@ -727,7 +727,7 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
/**
|
||||
* Creates a database type from a given column metadata.
|
||||
*/
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean }) {
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean, fixedLength?: boolean }): string {
|
||||
switch (typeOptions.type) {
|
||||
case "string":
|
||||
return "character varying(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
|
||||
@ -791,10 +791,14 @@ WHERE columnUsages.TABLE_CATALOG = '${this.dbName}' AND tableConstraints.TABLE_C
|
||||
/**
|
||||
* Creates a database type from a given column metadata.
|
||||
*/
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean }) {
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean, fixedLength?: boolean }): string {
|
||||
switch (typeOptions.type) {
|
||||
case "string":
|
||||
return "nvarchar(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
if (typeOptions.fixedLength) {
|
||||
return "nchar(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
} else {
|
||||
return "nvarchar(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
}
|
||||
case "text":
|
||||
return "ntext";
|
||||
case "boolean":
|
||||
|
||||
@ -737,7 +737,7 @@ export class WebsqlQueryRunner implements QueryRunner {
|
||||
/**
|
||||
* Creates a database type from a given column metadata.
|
||||
*/
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean }) {
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean, fixedLength?: boolean }): string {
|
||||
switch (typeOptions.type) {
|
||||
case "string":
|
||||
return "character varying(" + (typeOptions.length ? typeOptions.length : 255) + ")";
|
||||
|
||||
@ -103,7 +103,9 @@ export class EntityManager extends BaseEntityManager {
|
||||
if (target.length === 0)
|
||||
return Promise.resolve(target);
|
||||
|
||||
return this.getRepository<Entity[]>(target[0].constructor).persist(entity as Entity[], options);
|
||||
return Promise.all(target.map((t, i) => {
|
||||
return this.getRepository<Entity>(t.constructor).persist((entity as Entity[])[i], options);
|
||||
}));
|
||||
} else {
|
||||
return this.getRepository<Entity>(target.constructor).persist(entity as Entity, options);
|
||||
}
|
||||
@ -178,7 +180,9 @@ export class EntityManager extends BaseEntityManager {
|
||||
} else {
|
||||
// todo: throw exception if constructor in target is not set
|
||||
if (target instanceof Array) {
|
||||
return this.getRepository<Entity[]>(target[0].constructor).remove(entity as Entity[], options);
|
||||
return Promise.all(target.map((t, i) => {
|
||||
return this.getRepository<Entity>(t.constructor).remove((entity as Entity[])[i], options);
|
||||
}));
|
||||
} else {
|
||||
return this.getRepository<Entity>(target.constructor).remove(entity as Entity, options);
|
||||
}
|
||||
|
||||
14
src/index.ts
14
src/index.ts
@ -18,6 +18,8 @@ import {MongoRepository} from "./repository/MongoRepository";
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
export * from "./container";
|
||||
export * from "./common/ObjectType";
|
||||
export * from "./common/ObjectLiteral";
|
||||
export * from "./decorator/columns/Column";
|
||||
export * from "./decorator/columns/CreateDateColumn";
|
||||
export * from "./decorator/columns/DiscriminatorColumn";
|
||||
@ -253,12 +255,12 @@ export function getEntityManager(connectionName: string = "default"): EntityMana
|
||||
/**
|
||||
* Gets repository for the given entity class.
|
||||
*/
|
||||
export function getRepository<Entity>(entityClass: ObjectType<Entity>, connectionName: string): Repository<Entity>;
|
||||
export function getRepository<Entity>(entityClass: ObjectType<Entity>, connectionName?: string): Repository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets repository for the given entity name.
|
||||
*/
|
||||
export function getRepository<Entity>(entityName: string, connectionName: string): Repository<Entity>;
|
||||
export function getRepository<Entity>(entityName: string, connectionName?: string): Repository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets repository for the given entity class or name.
|
||||
@ -270,12 +272,12 @@ export function getRepository<Entity>(entityClassOrName: ObjectType<Entity>|stri
|
||||
/**
|
||||
* Gets tree repository for the given entity class.
|
||||
*/
|
||||
export function getTreeRepository<Entity>(entityClass: ObjectType<Entity>, connectionName: string): TreeRepository<Entity>;
|
||||
export function getTreeRepository<Entity>(entityClass: ObjectType<Entity>, connectionName?: string): TreeRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets tree repository for the given entity name.
|
||||
*/
|
||||
export function getTreeRepository<Entity>(entityName: string, connectionName: string): TreeRepository<Entity>;
|
||||
export function getTreeRepository<Entity>(entityName: string, connectionName?: string): TreeRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets tree repository for the given entity class or name.
|
||||
@ -287,12 +289,12 @@ export function getTreeRepository<Entity>(entityClassOrName: ObjectType<Entity>|
|
||||
/**
|
||||
* Gets mongodb repository for the given entity class.
|
||||
*/
|
||||
export function getMongoRepository<Entity>(entityClass: ObjectType<Entity>, connectionName: string): MongoRepository<Entity>;
|
||||
export function getMongoRepository<Entity>(entityClass: ObjectType<Entity>, connectionName?: string): MongoRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets mongodb repository for the given entity name.
|
||||
*/
|
||||
export function getMongoRepository<Entity>(entityName: string, connectionName: string): MongoRepository<Entity>;
|
||||
export function getMongoRepository<Entity>(entityName: string, connectionName?: string): MongoRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets mongodb repository for the given entity class or name.
|
||||
|
||||
@ -128,6 +128,12 @@ export class ColumnMetadata {
|
||||
*/
|
||||
readonly localTimezone?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if column's type will be set as a fixed-length data type.
|
||||
* Works only with "string" columns.
|
||||
*/
|
||||
readonly fixedLength?: boolean;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Private Properties
|
||||
// ---------------------------------------------------------------------
|
||||
@ -176,6 +182,8 @@ export class ColumnMetadata {
|
||||
this.timezone = args.options.timezone;
|
||||
if (args.options.localTimezone)
|
||||
this.localTimezone = args.options.localTimezone;
|
||||
if (args.options.fixedLength)
|
||||
this.fixedLength = args.options.fixedLength;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
@ -223,7 +223,7 @@ export class MigrationExecutor {
|
||||
protected getMigrations(): Migration[] {
|
||||
const migrations = this.connection.getMigrations().map(migration => {
|
||||
const migrationClassName = (migration.constructor as any).name;
|
||||
const migrationTimestamp = parseInt(migrationClassName.substr(-10));
|
||||
const migrationTimestamp = parseInt(migrationClassName.substr(-13));
|
||||
if (!migrationTimestamp)
|
||||
throw new Error(`Migration class name should contain a class name at the end of the file. ${migrationClassName} migration name is wrong.`);
|
||||
|
||||
|
||||
@ -79,7 +79,7 @@ export interface QueryRunner {
|
||||
/**
|
||||
* Converts a column type of the metadata to the database column's type.
|
||||
*/
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean }): any;
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean, fixedLength?: boolean }): string;
|
||||
|
||||
/**
|
||||
* Checks if "DEFAULT" values in the column metadata and in the database schema are equal.
|
||||
|
||||
@ -1,10 +1,16 @@
|
||||
import "reflect-metadata";
|
||||
import {expect} from "chai";
|
||||
import {Post} from "./entity/Post";
|
||||
import {Guest as GuestV1} from "./entity/v1/Guest";
|
||||
import {Comment as CommentV1} from "./entity/v1/Comment";
|
||||
import {Guest as GuestV2} from "./entity/v2/Guest";
|
||||
import {Comment as CommentV2} from "./entity/v2/Comment";
|
||||
import {View} from "./entity/View";
|
||||
import {Category} from "./entity/Category";
|
||||
import {closeTestingConnections, createTestingConnections, setupSingleTestingConnection} from "../../utils/test-utils";
|
||||
import {createConnection} from "../../../src/index";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
import {PostgresDriver} from "../../../src/driver/postgres/PostgresDriver";
|
||||
import {Repository} from "../../../src/repository/Repository";
|
||||
import {TreeRepository} from "../../../src/repository/TreeRepository";
|
||||
import {getConnectionManager} from "../../../src/index";
|
||||
@ -381,33 +387,89 @@ describe("Connection", () => {
|
||||
|
||||
});
|
||||
|
||||
describe("Different names of the same content of the schema", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
beforeEach(async () => {
|
||||
const [connection1] = await createTestingConnections({
|
||||
name: "test",
|
||||
enabledDrivers: ["postgres"],
|
||||
entities: [CommentV1, GuestV1],
|
||||
schemaName: "test-schema",
|
||||
dropSchemaOnConnection: true,
|
||||
});
|
||||
const [connection2] = await createTestingConnections({
|
||||
name: "another",
|
||||
enabledDrivers: ["postgres"],
|
||||
entities: [CommentV1, GuestV1],
|
||||
schemaName: "another-schema",
|
||||
dropSchemaOnConnection: true
|
||||
});
|
||||
connections = [connection1, connection2];
|
||||
});
|
||||
after(() => closeTestingConnections(connections));
|
||||
it("should not interfere with each other", async () => {
|
||||
await Promise.all(connections.map(c => c.syncSchema()));
|
||||
await closeTestingConnections(connections);
|
||||
const [connection1] = await createTestingConnections({
|
||||
name: "test",
|
||||
enabledDrivers: ["postgres"],
|
||||
entities: [CommentV2, GuestV2],
|
||||
schemaName: "test-schema",
|
||||
dropSchemaOnConnection: false,
|
||||
schemaCreate: true
|
||||
});
|
||||
const [connection2] = await createTestingConnections({
|
||||
name: "another",
|
||||
enabledDrivers: ["postgres"],
|
||||
entities: [CommentV2, GuestV2],
|
||||
schemaName: "another-schema",
|
||||
dropSchemaOnConnection: false,
|
||||
schemaCreate: true
|
||||
});
|
||||
connections = [connection1, connection2];
|
||||
});
|
||||
});
|
||||
|
||||
describe("Can change postgres default schema name", () => {
|
||||
let connections: Connection[];
|
||||
beforeEach(async () => {
|
||||
connections = await createTestingConnections({
|
||||
const [connection1] = await createTestingConnections({
|
||||
name: "test",
|
||||
enabledDrivers: ["postgres"],
|
||||
entities: [Post],
|
||||
entities: [CommentV1, GuestV1],
|
||||
schemaName: "test-schema",
|
||||
dropSchemaOnConnection: true,
|
||||
});
|
||||
const [connection2] = await createTestingConnections({
|
||||
name: "another",
|
||||
enabledDrivers: ["postgres"],
|
||||
entities: [CommentV1, GuestV1],
|
||||
schemaName: "another-schema",
|
||||
dropSchemaOnConnection: true
|
||||
});
|
||||
connections = [connection1, connection2];
|
||||
});
|
||||
afterEach(() => closeTestingConnections(connections));
|
||||
|
||||
it("schema name can be set", () => {
|
||||
return Promise.all(connections.map(async connection => {
|
||||
await connection.syncSchema(true);
|
||||
const schemaName = (connection.driver as PostgresDriver).schemaName;
|
||||
const comment = new CommentV1();
|
||||
comment.title = "Change SchemaName";
|
||||
comment.context = `To ${schemaName}`;
|
||||
|
||||
const post = new Post();
|
||||
post.title = "ChangeSchemaName";
|
||||
|
||||
const PostRepo = connection.getRepository(Post);
|
||||
await PostRepo.persist(post);
|
||||
const commentRepo = connection.getRepository(CommentV1);
|
||||
await commentRepo.persist(comment);
|
||||
|
||||
const query = await connection.driver.createQueryRunner();
|
||||
const rows = await query.query(`select * from "test-schema"."post" where id = $1`, [post.id]);
|
||||
expect(rows[0]["title"]).to.be.eq(post.title);
|
||||
const rows = await query.query(`select * from "${schemaName}"."comment" where id = $1`, [comment.id]);
|
||||
expect(rows[0]["context"]).to.be.eq(comment.context);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
32
test/functional/connection/entity/v1/Comment.ts
Normal file
32
test/functional/connection/entity/v1/Comment.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import {Entity} from "../../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {ManyToOne} from "../../../../../src/decorator/relations/ManyToOne";
|
||||
import {OneToMany} from "../../../../../src/decorator/relations/OneToMany";
|
||||
// import {JoinColumn} from "../../../../../src/decorator/relations/JoinColumn";
|
||||
import {Index} from "../../../../../src/decorator/Index";
|
||||
import {Guest} from "./Guest";
|
||||
|
||||
@Entity()
|
||||
@Index("author_and_title_unique", ["author", "title"], { unique: true })
|
||||
export class Comment {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
@Index()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
context: string;
|
||||
|
||||
@OneToMany(type => Comment, comment => comment.relay)
|
||||
reference?: Comment;
|
||||
|
||||
@ManyToOne(type => Comment, comment => comment.reference)
|
||||
relay?: Comment;
|
||||
|
||||
@ManyToOne(type => Guest, guest => guest.comments)
|
||||
author: Guest;
|
||||
}
|
||||
18
test/functional/connection/entity/v1/Guest.ts
Normal file
18
test/functional/connection/entity/v1/Guest.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {Entity} from "../../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {OneToMany} from "../../../../../src/decorator/relations/OneToMany";
|
||||
import {Comment} from "./Comment";
|
||||
|
||||
@Entity()
|
||||
export class Guest {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
username: string;
|
||||
|
||||
@OneToMany(type => Comment, comment => comment.author)
|
||||
comments: Comment[];
|
||||
}
|
||||
24
test/functional/connection/entity/v2/Comment.ts
Normal file
24
test/functional/connection/entity/v2/Comment.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {Entity} from "../../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {ManyToOne} from "../../../../../src/decorator/relations/ManyToOne";
|
||||
import {Index} from "../../../../../src/decorator/Index";
|
||||
import {Guest} from "./Guest";
|
||||
|
||||
@Entity()
|
||||
@Index("author_and_title_unique_rename", ["author", "title", "context"], { unique: true })
|
||||
export class Comment {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
@Index()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
context: string;
|
||||
|
||||
@ManyToOne(type => Guest, guest => guest.comments)
|
||||
author: Guest;
|
||||
}
|
||||
18
test/functional/connection/entity/v2/Guest.ts
Normal file
18
test/functional/connection/entity/v2/Guest.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {Entity} from "../../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {OneToMany} from "../../../../../src/decorator/relations/OneToMany";
|
||||
import {Comment} from "./Comment";
|
||||
|
||||
@Entity()
|
||||
export class Guest {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
username: string;
|
||||
|
||||
@OneToMany(type => Comment, comment => comment.author)
|
||||
comments: Comment[];
|
||||
}
|
||||
19
test/github-issues/341/entity/Category.ts
Normal file
19
test/github-issues/341/entity/Category.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Entity} from "../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
import {Post} from "./Post";
|
||||
import {OneToOne} from "../../../../src/decorator/relations/OneToOne";
|
||||
|
||||
@Entity()
|
||||
export class Category {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ unique: true })
|
||||
name: string;
|
||||
|
||||
@OneToOne(type => Post, post => post.category)
|
||||
post: Post;
|
||||
|
||||
}
|
||||
24
test/github-issues/341/entity/Post.ts
Normal file
24
test/github-issues/341/entity/Post.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {Entity} from "../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
import {Category} from "./Category";
|
||||
import {OneToOne} from "../../../../src/decorator/relations/OneToOne";
|
||||
import {JoinColumn} from "../../../../src/decorator/relations/JoinColumn";
|
||||
|
||||
@Entity()
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
categoryName: string;
|
||||
|
||||
@OneToOne(type => Category, category => category.post)
|
||||
@JoinColumn({ name: "categoryName", referencedColumnName: "name" })
|
||||
category: Category;
|
||||
|
||||
}
|
||||
41
test/github-issues/341/issue-341.ts
Normal file
41
test/github-issues/341/issue-341.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import "reflect-metadata";
|
||||
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
import {Post} from "./entity/Post";
|
||||
import {Category} from "./entity/Category";
|
||||
import {expect} from "chai";
|
||||
|
||||
describe("github issues > OneToOne relation with referencedColumnName does not work", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("custom join column name and referencedColumnName", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const category = new Category();
|
||||
category.name = "category #1";
|
||||
await connection.entityManager.persist(category);
|
||||
|
||||
const post = new Post();
|
||||
post.title = "post #1";
|
||||
post.category = category;
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
const loadedPost = await connection
|
||||
.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.category", "category")
|
||||
.getOne();
|
||||
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
expect(loadedPost!.category).not.to.be.empty;
|
||||
|
||||
})));
|
||||
|
||||
});
|
||||
21
test/github-issues/345/entity/Category.ts
Normal file
21
test/github-issues/345/entity/Category.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import {Entity} from "../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
import {Post} from "./Post";
|
||||
import {ManyToMany} from "../../../../src/decorator/relations/ManyToMany";
|
||||
import {JoinTable} from "../../../../src/decorator/relations/JoinTable";
|
||||
|
||||
@Entity()
|
||||
export class Category {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
category_id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToMany(() => Post, post => post.categories)
|
||||
@JoinTable()
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
21
test/github-issues/345/entity/Post.ts
Normal file
21
test/github-issues/345/entity/Post.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import {Entity} from "../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
import {Category} from "./Category";
|
||||
import {ManyToMany} from "../../../../src/decorator/relations/ManyToMany";
|
||||
|
||||
@Entity()
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@ManyToMany(() => Category, category => category.posts, {
|
||||
cascadeInsert: true
|
||||
})
|
||||
categories: Category[];
|
||||
|
||||
}
|
||||
45
test/github-issues/345/issue-345.ts
Normal file
45
test/github-issues/345/issue-345.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import "reflect-metadata";
|
||||
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
import {Post} from "./entity/Post";
|
||||
import {Category} from "./entity/Category";
|
||||
import {expect} from "chai";
|
||||
|
||||
describe("github issues > Join query on ManyToMany relations not working", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("embedded with custom column name should persist and load without errors", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const category = new Category();
|
||||
category.name = "Category #" + i;
|
||||
await connection.entityManager.persist(category);
|
||||
}
|
||||
|
||||
const post = new Post();
|
||||
post.title = "SuperRace";
|
||||
post.categories = [new Category()];
|
||||
post.categories[0].name = "SuperCategory";
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
const loadedPost = await connection
|
||||
.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.categories", "category")
|
||||
.where("category.category_id IN (:ids)", { ids: [21] })
|
||||
.getOne();
|
||||
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
expect(loadedPost!.categories).not.to.be.empty;
|
||||
|
||||
})));
|
||||
|
||||
});
|
||||
12
test/github-issues/363/entity/Car.ts
Normal file
12
test/github-issues/363/entity/Car.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity";
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import { Column } from "../../../../src/decorator/columns/Column";
|
||||
|
||||
@Entity()
|
||||
export class Car {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
}
|
||||
12
test/github-issues/363/entity/Fruit.ts
Normal file
12
test/github-issues/363/entity/Fruit.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity";
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import { Column } from "../../../../src/decorator/columns/Column";
|
||||
|
||||
@Entity()
|
||||
export class Fruit {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
}
|
||||
76
test/github-issues/363/issue-363.ts
Normal file
76
test/github-issues/363/issue-363.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import "reflect-metadata";
|
||||
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
import {expect} from "chai";
|
||||
import {Car} from "./entity/Car";
|
||||
import {Fruit} from "./entity/Fruit";
|
||||
|
||||
describe("github issues > #363 Can't save 2 unrelated entity types in a single persist call", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("entityManager should allow you to save unrelated entities with one persist call", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const car = new Car();
|
||||
car.name = "Ferrari";
|
||||
|
||||
const fruit = new Fruit();
|
||||
fruit.name = "Banana";
|
||||
|
||||
const [savedCar, savedFruit] = await connection.entityManager.persist([car, fruit]);
|
||||
|
||||
expect(savedFruit).to.have.property("name", "Banana");
|
||||
expect(savedFruit).to.be.instanceof(Fruit);
|
||||
|
||||
expect(savedCar).to.have.property("name", "Ferrari");
|
||||
expect(savedCar).to.be.instanceof(Car);
|
||||
|
||||
const cars = await connection.entityManager.find(Car);
|
||||
|
||||
// before the changes in this PR, all the tests before this one actually passed
|
||||
expect(cars).to.length(1);
|
||||
expect(cars[0]).to.have.property("name", "Ferrari");
|
||||
|
||||
const fruits = await connection.entityManager.find(Fruit);
|
||||
|
||||
expect(fruits).to.length(1);
|
||||
expect(fruits[0]).to.have.property("name", "Banana");
|
||||
|
||||
})));
|
||||
|
||||
it("entityManager should allow you to delete unrelated entities with one remove call", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const fruit = new Fruit();
|
||||
fruit.name = "Banana";
|
||||
|
||||
const fruit2 = new Fruit();
|
||||
fruit2.name = "Apple";
|
||||
|
||||
const [savedFruit] = await connection.entityManager.persist([fruit, fruit2]);
|
||||
|
||||
const car = new Car();
|
||||
car.name = "Ferrari";
|
||||
|
||||
const savedCar = await connection.entityManager.persist(car);
|
||||
|
||||
await connection.entityManager.remove([savedCar, savedFruit]);
|
||||
|
||||
const cars = await connection.entityManager.find(Car);
|
||||
|
||||
expect(cars).to.length(0);
|
||||
|
||||
const fruits = await connection.entityManager.find(Fruit);
|
||||
|
||||
expect(fruits).to.length(1);
|
||||
expect(fruits[0]).to.have.property("name", "Apple");
|
||||
|
||||
})));
|
||||
|
||||
});
|
||||
39
test/other-issues/column-getters/column-getters.ts
Normal file
39
test/other-issues/column-getters/column-getters.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import "reflect-metadata";
|
||||
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
import {Post} from "./entity/Post";
|
||||
import {expect} from "chai";
|
||||
|
||||
describe("other issues > column with getter / setter should work", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("getters and setters should work correctly", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const post = new Post();
|
||||
post.title = "Super title";
|
||||
post.text = "About this post";
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
const loadedPost = await connection
|
||||
.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne();
|
||||
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
expect(loadedPost!.title).not.to.be.empty;
|
||||
expect(loadedPost!.text).not.to.be.empty;
|
||||
loadedPost!.title.should.be.equal("Super title");
|
||||
loadedPost!.text.should.be.equal("About this post");
|
||||
|
||||
})));
|
||||
|
||||
});
|
||||
25
test/other-issues/column-getters/entity/Post.ts
Normal file
25
test/other-issues/column-getters/entity/Post.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import {Entity} from "../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
|
||||
@Entity()
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: "title" })
|
||||
private _title: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
set title(title: string) {
|
||||
this._title = title;
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user