O Typeorm é um ORM que pode ser executado em plataformas NodeJs, Browser, Cordova, PhoneGap, Ionic, React Native, Nativescript, Expo e Electron e pode ser usado com TypeScript e JavaScript (ES2021). Seu objetivo é sempre suportar os recursos JavaScript mais recente e fornecer recursos adicionais que ajudem a desenvolver qualquer tipo de aplicativo que use bancos de dados - de pequenos aplicativos com algumas tabelas a aplicativos corporativos em larga escala com vários bancos de dados.
O Typeorm suporta padrões de registro ativo e mapeador de dados, ao contrário de todos os outros ORMs JavaScript atualmente existentes, o que significa que você pode escrever aplicativos de alta qualidade, pouco acoplados, escaláveis e sustentáveis da maneira mais produtiva.
O Typeorm é altamente influenciado por outros ORMs, como Hibernate, Doutrine e Entity Framework.
Anúncio: O Futuro do Typeorm
Estamos entusiasmados em compartilhar nossa visão para uma forma de tipo revitalizada - uma estratégia focada na construção de uma base estável, robusta e sustentável a longo prazo. Saiba como estamos estruturando a manutenção e reunindo recursos dedicados para garantir que o Typeorm prospere nos próximos anos.
Leia o anúncio completo
E mais ...
Com o Typeorm, seus modelos se parecem com o seguinte:
import { Entity , PrimaryGeneratedColumn , Column } from "typeorm"
@ Entity ( )
export class User {
@ PrimaryGeneratedColumn ( )
id : number
@ Column ( )
firstName : string
@ Column ( )
lastName : string
@ Column ( )
age : number
}
E sua lógica de domínio se parece com a seguinte:
const userRepository = MyDataSource . getRepository ( User )
const user = new User ( )
user . firstName = "Timber"
user . lastName = "Saw"
user . age = 25
await userRepository . save ( user )
const allUsers = await userRepository . find ( )
const firstUser = await userRepository . findOneBy ( {
id : 1 ,
} ) // find by id
const timber = await userRepository . findOneBy ( {
firstName : "Timber" ,
lastName : "Saw" ,
} ) // find by firstName and lastName
await userRepository . remove ( timber )
Como alternativa, se você preferir usar a implementação ActiveRecord
, também poderá usá -lo:
import { Entity , PrimaryGeneratedColumn , Column , BaseEntity } from "typeorm"
@ Entity ( )
export class User extends BaseEntity {
@ PrimaryGeneratedColumn ( )
id : number
@ Column ( )
firstName : string
@ Column ( )
lastName : string
@ Column ( )
age : number
}
E sua lógica de domínio parecerá desta maneira:
const user = new User ( )
user . firstName = "Timber"
user . lastName = "Saw"
user . age = 25
await user . save ( )
const allUsers = await User . find ( )
const firstUser = await User . findOneBy ( {
id : 1 ,
} )
const timber = await User . findOneBy ( {
firstName : "Timber" ,
lastName : "Saw"
} )
await timber . remove ( )
Instale o pacote NPM:
npm install typeorm --save
Você precisa instalar reflect-metadata
shim:
npm install reflect-metadata --save
e importá -lo em algum lugar no local global do seu aplicativo (por exemplo, no app.ts
):
import "reflect-metadata"
Pode ser necessário instalar as tímadas de nós:
npm install @types/node --save-dev
Instale um driver de banco de dados:
para mysql ou mariadb
npm install mysql --save
(você também pode instalar mysql2
)
Para PostgreSql ou Cockroachdb
npm install pg --save
para sqlite
npm install sqlite3 --save
para o servidor SQL Microsoft
npm install mssql --save
para sql.js
npm install sql.js --save
para Oracle
npm install oracledb --save
Para fazer o Oracle Driver funcionar, você precisa seguir as instruções de instalação do site deles.
para SAP HANA
npm install @sap/hana-client
npm install hdb-pool
O suporte da SAP HANA possibilitou o patrocínio do software Neptune.
Para o Google Cloud Spanner
npm install @google-cloud/spanner --save
Forneça credenciais de autenticação ao seu código de aplicativo, configurando a variável de ambiente GOOGLE_APPLICATION_CREDENTIALS
:
# Linux/macOS
export GOOGLE_APPLICATION_CREDENTIALS= " KEY_PATH "
# Windows
set GOOGLE_APPLICATION_CREDENTIALS=KEY_PATH
# Replace KEY_PATH with the path of the JSON file that contains your service account key.
Para usar o Spanner com o emulador, você deve definir SPANNER_EMULATOR_HOST
de ambiente variável:
# Linux/macOS
export SPANNER_EMULATOR_HOST=localhost:9010
# Windows
set SPANNER_EMULATOR_HOST=localhost:9010
Para MongoDB (experimental)
npm install mongodb@^5.2.0 --save
para Nativescript , React-native e Cordova
Verifique a documentação das plataformas suportadas
Instale apenas um deles, dependendo do banco de dados que você usa.
Além disso, verifique se você está usando o TypeScript versão 4.5 ou superior e você ativou as seguintes configurações em tsconfig.json
:
"emitDecoratorMetadata" : true ,
"experimentalDecorators" : true ,
Você também pode precisar ativar es6
na seção lib
das opções do compilador ou instalar es6-shim
a partir do @types
.
A maneira mais rápida de começar o Typeorm é usar seus comandos da CLI para gerar um projeto inicial. O Start Quick Start funciona apenas se você estiver usando o Typeorm em um aplicativo NodeJS. Se você estiver usando outras plataformas, prossiga para o guia passo a passo.
Para criar um novo projeto usando a CLI, execute o seguinte comando:
npx typeorm init --name MyProject --database postgres
Onde name
é o nome do seu projeto e database
é o banco de dados que você usará. O banco de dados pode ser um dos seguintes valores: mysql
, mariadb
, postgres
, cockroachdb
sqlite
nativescript
, MSSQL, oracle
, spanner
, sap
, mssql
, cordova
, react-native
, expo
, mongodb
.
Este comando gerará um novo projeto no diretório MyProject
com os seguintes arquivos:
MyProject
├── src // place of your TypeScript code
│ ├── entity // place where your entities (database models) are stored
│ │ └── User.ts // sample entity
│ ├── migration // place where your migrations are stored
│ ├── data-source.ts // data source and all connection configuration
│ └── index.ts // start point of your application
├── .gitignore // standard gitignore file
├── package.json // node module dependencies
├── README.md // simple readme file
└── tsconfig.json // TypeScript compiler options
Você também pode executar
typeorm init
em um projeto de nó existente, mas tenha cuidado - ele pode substituir alguns arquivos que você já possui.
O próximo passo é instalar novas dependências do projeto:
cd MyProject
npm install
Depois de instalar todas as dependências, edite o arquivo data-source.ts
e coloque suas próprias opções de configuração de conexão com o banco de dados:
export const AppDataSource = new DataSource ( {
type : "postgres" ,
host : "localhost" ,
port : 5432 ,
username : "test" ,
password : "test" ,
database : "test" ,
synchronize : true ,
logging : true ,
entities : [ Post , Category ] ,
subscribers : [ ] ,
migrations : [ ] ,
} )
Particularmente, na maioria das vezes, você só precisará configurar host
, username
, password
, database
e talvez opções port
.
Depois de terminar com a configuração e todos os módulos de nós estão instalados, você pode executar seu aplicativo:
npm start
É isso, seu aplicativo deve executar e inserir com sucesso um novo usuário no banco de dados. Você pode continuar a trabalhar com este projeto e integrar outros módulos necessários e começar a criar mais entidades.
Você pode gerar um projeto ESM executando
npx typeorm init --name MyProject --database postgres --module esm
Command.
Você pode gerar um projeto ainda mais avançado com o Express instalado executando
npx typeorm init --name MyProject --database mysql --express
.
Você pode gerar um arquivo do Docker-Compose, executando
npx typeorm init --name MyProject --database postgres --docker
Command.
O que você está esperando do ORM? Primeiro de tudo, você espera que ele crie tabelas de banco de dados para você e encontre / inserir / atualizar / excluir seus dados sem a dor de ter que escrever muitas consultas SQL dificilmente sustentáveis. Este guia mostrará como configurar o TypeRort do zero e fazê -lo fazer o que você espera de um ORM.
Trabalhar com um banco de dados começa com a criação de tabelas. Como você diz ao TypeRorm para criar uma tabela de banco de dados? A resposta é - através dos modelos. Seus modelos em seu aplicativo são suas tabelas de banco de dados.
Por exemplo, você tem um modelo Photo
:
export class Photo {
id : number
name : string
description : string
filename : string
views : number
isPublished : boolean
}
E você deseja armazenar fotos em seu banco de dados. Para armazenar as coisas no banco de dados, primeiro, você precisa de uma tabela de banco de dados e as tabelas de banco de dados são criadas a partir de seus modelos. Nem todos os modelos, mas apenas aqueles que você define como entidades .
A entidade é o seu modelo decorado por um decorador @Entity
. Uma tabela de banco de dados será criada para esses modelos. Você trabalha com entidades em todos os lugares no Typeorm. Você pode carregar/inserir/atualizar/remover e executar outras operações com eles.
Vamos tornar nosso modelo Photo
uma entidade:
import { Entity } from "typeorm"
@ Entity ( )
export class Photo {
id : number
name : string
description : string
filename : string
views : number
isPublished : boolean
}
Agora, uma tabela de banco de dados será criada para a entidade Photo
e poderemos trabalhar com ela em qualquer lugar do nosso aplicativo. Criamos uma tabela de banco de dados, no entanto, que tabela pode existir sem colunas? Vamos criar algumas colunas em nossa tabela de banco de dados.
Para adicionar colunas de banco de dados, basta decorar as propriedades de uma entidade que deseja transformar em uma coluna com um decorador @Column
.
import { Entity , Column } from "typeorm"
@ Entity ( )
export class Photo {
@ Column ( )
id : number
@ Column ( )
name : string
@ Column ( )
description : string
@ Column ( )
filename : string
@ Column ( )
views : number
@ Column ( )
isPublished : boolean
}
Agora id
, name
, description
, filename
, views
e colunas isPublished
serão adicionadas à tabela photo
. Os tipos de coluna no banco de dados são inferidos a partir dos tipos de propriedades que você usou, por exemplo, number
será convertido em integer
, string
em varchar
, boolean
em bool
etc., mas você pode usar qualquer tipo de coluna que seu banco de dados suporta, especificando explicitamente um tipo de coluna no tipo de coluna no @Column
Decorator.
Geramos uma tabela de banco de dados com colunas, mas resta uma coisa. Cada tabela de banco de dados deve ter uma coluna com uma chave primária.
Cada entidade deve ter pelo menos uma coluna de chave primária. Este é um requisito e você não pode evitá -lo. Para tornar uma coluna uma chave primária, você precisa usar o decorador @PrimaryColumn
.
import { Entity , Column , PrimaryColumn } from "typeorm"
@ Entity ( )
export class Photo {
@ PrimaryColumn ( )
id : number
@ Column ( )
name : string
@ Column ( )
description : string
@ Column ( )
filename : string
@ Column ( )
views : number
@ Column ( )
isPublished : boolean
}
Agora, digamos que você queira que sua coluna de identificação seja gerada automaticamente (isso é conhecido como coluna de identidade de incremento / sequência / sequência / serial / geração). Para fazer isso, você precisa alterar o Decorador @PrimaryColumn
para um Decorador @PrimaryGeneratedColumn
:
import { Entity , Column , PrimaryGeneratedColumn } from "typeorm"
@ Entity ( )
export class Photo {
@ PrimaryGeneratedColumn ( )
id : number
@ Column ( )
name : string
@ Column ( )
description : string
@ Column ( )
filename : string
@ Column ( )
views : number
@ Column ( )
isPublished : boolean
}
Em seguida, vamos corrigir nossos tipos de dados. Por padrão, a string é mapeada para um tipo Varchar (255) (dependendo do tipo de banco de dados). O número é mapeado para um tipo de número inteiro (dependendo do tipo de banco de dados). Não queremos que todas as nossas colunas sejam varchars ou inteiros limitados. Vamos configurar os tipos de dados corretos:
import { Entity , Column , PrimaryGeneratedColumn } from "typeorm"
@ Entity ( )
export class Photo {
@ PrimaryGeneratedColumn ( )
id : number
@ Column ( {
length : 100 ,
} )
name : string
@ Column ( "text" )
description : string
@ Column ( )
filename : string
@ Column ( "double" )
views : number
@ Column ( )
isPublished : boolean
}
Os tipos de colunas são específicos do banco de dados. Você pode definir qualquer tipo de coluna que seu banco de dados suporta. Mais informações sobre tipos de coluna suportadas podem ser encontradas aqui.
DataSource
Agora, quando nossa entidade é criada, vamos criar arquivo index.ts
e configurar nosso DataSource
lá:
import "reflect-metadata"
import { DataSource } from "typeorm"
import { Photo } from "./entity/Photo"
const AppDataSource = new DataSource ( {
type : "postgres" ,
host : "localhost" ,
port : 5432 ,
username : "root" ,
password : "admin" ,
database : "test" ,
entities : [ Photo ] ,
synchronize : true ,
logging : false ,
} )
// to initialize the initial connection with the database, register all entities
// and "synchronize" database schema, call "initialize()" method of a newly created database
// once in your application bootstrap
AppDataSource . initialize ( )
. then ( ( ) => {
// here you can start to work with your database
} )
. catch ( ( error ) => console . log ( error ) )
Estamos usando o PostGres neste exemplo, mas você pode usar qualquer outro banco de dados suportado. react-native
usar sap
oracle
de dados, basta alterar o type
cordova
nativescript
expo
o tipo de banco de dados mssql
spanner
está usando: mysql
, mariadb
, postgres
mongodb
sqlite
cockroachdb
. Também use seu próprio host, porta, nome de usuário, senha e configurações de banco de dados.
Adicionamos nossa entidade fotográfica à lista de entidades para essa fonte de dados. Cada entidade que você está usando em sua conexão deve estar listada lá.
A configuração synchronize
garante que suas entidades sejam sincronizadas com o banco de dados, sempre que você executar o aplicativo.
Agora, se você executar seu index.ts
, uma conexão com o banco de dados será inicializada e uma tabela de banco de dados para suas fotos será criada.
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(100) | |
| description | text | |
| filename | varchar(255) | |
| views | int(11) | |
| isPublished | boolean | |
+-------------+--------------+----------------------------+
Agora vamos criar uma nova foto para salvá -la no banco de dados:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const photo = new Photo ( )
photo . name = "Me and Bears"
photo . description = "I am near polar bears"
photo . filename = "photo-with-bears.jpg"
photo . views = 1
photo . isPublished = true
await AppDataSource . manager . save ( photo )
console . log ( "Photo has been saved. Photo id is" , photo . id )
Depois que sua entidade for salva, ela receberá um ID recém -gerado. save
o método retorna uma instância do mesmo objeto que você passa para ele. Não é uma nova cópia do objeto, ele modifica seu "id" e o retorna.
Acabamos de criar uma nova foto e a salvamos no banco de dados. Usamos EntityManager
para salvá -lo. Usando o Entity Manager, você pode manipular qualquer entidade em seu aplicativo. Por exemplo, vamos carregar nossa entidade salva:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const savedPhotos = await AppDataSource . manager . find ( Photo )
console . log ( "All photos from the db: " , savedPhotos )
savedPhotos
será uma variedade de objetos de foto com os dados carregados no banco de dados.
Saiba mais sobre EntityManager aqui.
Agora, vamos refatorar nosso código e usar Repository
em vez de EntityManager
. Cada entidade possui seu próprio repositório que lida com todas as operações com sua entidade. Quando você lida muito com as entidades, os repositórios são mais convenientes de usar do que o EntityManagers:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const photo = new Photo ( )
photo . name = "Me and Bears"
photo . description = "I am near polar bears"
photo . filename = "photo-with-bears.jpg"
photo . views = 1
photo . isPublished = true
const photoRepository = AppDataSource . getRepository ( Photo )
await photoRepository . save ( photo )
console . log ( "Photo has been saved" )
const savedPhotos = await photoRepository . find ( )
console . log ( "All photos from the db: " , savedPhotos )
Saiba mais sobre o repositório aqui.
Vamos tentar mais operações de carga usando o repositório:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const photoRepository = AppDataSource . getRepository ( Photo )
const allPhotos = await photoRepository . find ( )
console . log ( "All photos from the db: " , allPhotos )
const firstPhoto = await photoRepository . findOneBy ( {
id : 1 ,
} )
console . log ( "First photo from the db: " , firstPhoto )
const meAndBearsPhoto = await photoRepository . findOneBy ( {
name : "Me and Bears" ,
} )
console . log ( "Me and Bears photo from the db: " , meAndBearsPhoto )
const allViewedPhotos = await photoRepository . findBy ( { views : 1 } )
console . log ( "All viewed photos: " , allViewedPhotos )
const allPublishedPhotos = await photoRepository . findBy ( { isPublished : true } )
console . log ( "All published photos: " , allPublishedPhotos )
const [ photos , photosCount ] = await photoRepository . findAndCount ( )
console . log ( "All photos: " , photos )
console . log ( "Photos count: " , photosCount )
Agora vamos carregar uma única foto do banco de dados, atualizá -la e salvá -la:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const photoRepository = AppDataSource . getRepository ( Photo )
const photoToUpdate = await photoRepository . findOneBy ( {
id : 1 ,
} )
photoToUpdate . name = "Me, my friends and polar bears"
await photoRepository . save ( photoToUpdate )
Agora a foto com id = 1
será atualizada no banco de dados.
Agora vamos remover nossa foto do banco de dados:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const photoRepository = AppDataSource . getRepository ( Photo )
const photoToRemove = await photoRepository . findOneBy ( {
id : 1 ,
} )
await photoRepository . remove ( photoToRemove )
Agora a foto com id = 1
será removida do banco de dados.
Vamos criar um relacionamento individual com outra classe. Vamos criar uma nova classe em PhotoMetadata.ts
. Esta aula de Photometadata deve conter a meta-informação adicional de nossa foto:
import {
Entity ,
Column ,
PrimaryGeneratedColumn ,
OneToOne ,
JoinColumn ,
} from "typeorm"
import { Photo } from "./Photo"
@ Entity ( )
export class PhotoMetadata {
@ PrimaryGeneratedColumn ( )
id : number
@ Column ( "int" )
height : number
@ Column ( "int" )
width : number
@ Column ( )
orientation : string
@ Column ( )
compressed : boolean
@ Column ( )
comment : string
@ OneToOne ( ( ) => Photo )
@ JoinColumn ( )
photo : Photo
}
Aqui, estamos usando um novo decorador chamado @OneToOne
. Ele nos permite criar um relacionamento individual entre duas entidades. type => Photo
é uma função que retorna a classe da entidade com a qual queremos fazer nosso relacionamento. Somos forçados a usar uma função que retorna uma classe, em vez de usar a classe diretamente, devido às especificidades do idioma. Também podemos escrever como () => Photo
, mas usamos type => Photo
como uma convenção para aumentar a legibilidade do código. A variável de tipo em si não contém nada.
Também adicionamos um decorador @JoinColumn
, que indica que este lado do relacionamento será o dono do relacionamento. As relações podem ser unidirecionais ou bidirecionais. Apenas um lado do relacional pode ser dono. O uso do decorador @JoinColumn
é necessário no lado do proprietário do relacionamento.
Se você executar o aplicativo, verá uma tabela recém -gerada e conterá uma coluna com uma chave estrangeira para a relação fotográfica:
+-------------+--------------+----------------------------+
| photo_metadata |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| height | int(11) | |
| width | int(11) | |
| comment | varchar(255) | |
| compressed | boolean | |
| orientation | varchar(255) | |
| photoId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
Agora vamos salvar uma foto, e seus metadados e anexá -los um ao outro.
import { Photo } from "./entity/Photo"
import { PhotoMetadata } from "./entity/PhotoMetadata"
// create a photo
const photo = new Photo ( )
photo . name = "Me and Bears"
photo . description = "I am near polar bears"
photo . filename = "photo-with-bears.jpg"
photo . views = 1
photo . isPublished = true
// create a photo metadata
const metadata = new PhotoMetadata ( )
metadata . height = 640
metadata . width = 480
metadata . compressed = true
metadata . comment = "cybershoot"
metadata . orientation = "portrait"
metadata . photo = photo // this way we connect them
// get entity repositories
const photoRepository = AppDataSource . getRepository ( Photo )
const metadataRepository = AppDataSource . getRepository ( PhotoMetadata )
// first we should save a photo
await photoRepository . save ( photo )
// photo is saved. Now we need to save a photo metadata
await metadataRepository . save ( metadata )
// done
console . log (
"Metadata is saved, and the relation between metadata and photo is created in the database too" ,
)
As relações podem ser unidirecionais ou bidirecionais. Atualmente, nossa relação entre fotometadata e foto é unidirecional. O proprietário da relação é Photometadata, e a foto não sabe nada sobre Photometadata. Isso torna complicado acessar o Photometadata do lado da foto. Para corrigir esse problema, devemos adicionar uma relação inversa e fazer relações entre Photometadata e Bidirectional de foto. Vamos modificar nossas entidades:
import {
Entity ,
Column ,
PrimaryGeneratedColumn ,
OneToOne ,
JoinColumn ,
} from "typeorm"
import { Photo } from "./Photo"
@ Entity ( )
export class PhotoMetadata {
/* ... other columns */
@ OneToOne ( ( ) => Photo , ( photo ) => photo . metadata )
@ JoinColumn ( )
photo : Photo
}
import { Entity , Column , PrimaryGeneratedColumn , OneToOne } from "typeorm"
import { PhotoMetadata } from "./PhotoMetadata"
@ Entity ( )
export class Photo {
/* ... other columns */
@ OneToOne ( ( ) => PhotoMetadata , ( photoMetadata ) => photoMetadata . photo )
metadata : PhotoMetadata
}
photo => photo.metadata
é uma função que retorna o nome do lado inverso da relação. Aqui mostramos que a propriedade de metadados da aula de fotos é onde armazenamos Photometadata na aula de fotos. Em vez de passar uma função que retorna uma propriedade da foto, você pode simplesmente passar uma corda para o decorador @OneToOne
, como "metadata"
. Mas usamos essa abordagem do tipo função para facilitar nossa refatoração.
Observe que devemos usar o Decorador @JoinColumn
apenas em um lado de uma relação. Qualquer que seja o lado em que você colocar este decorador será o lado próprio do relacionamento. O lado próprio de um relacionamento contém uma coluna com uma chave estrangeira no banco de dados.
Se você usar o ESM em seu projeto TypeScript, use o tipo de wrapper de Relation
em propriedades para evitar problemas de dependência circular. Vamos modificar nossas entidades:
import {
Entity ,
Column ,
PrimaryGeneratedColumn ,
OneToOne ,
JoinColumn ,
Relation ,
} from "typeorm"
import { Photo } from "./Photo"
@ Entity ( )
export class PhotoMetadata {
/* ... other columns */
@ OneToOne ( ( ) => Photo , ( photo ) => photo . metadata )
@ JoinColumn ( )
photo : Relation < Photo >
}
import {
Entity ,
Column ,
PrimaryGeneratedColumn ,
OneToOne ,
Relation ,
} from "typeorm"
import { PhotoMetadata } from "./PhotoMetadata"
@ Entity ( )
export class Photo {
/* ... other columns */
@ OneToOne ( ( ) => PhotoMetadata , ( photoMetadata ) => photoMetadata . photo )
metadata : Relation < PhotoMetadata >
}
Agora vamos carregar nossa foto e seus metadados fotográficos em uma única consulta. Existem duas maneiras de fazer isso - usando os métodos find*
ou usando a funcionalidade QueryBuilder
. Vamos usar o método find*
primeiro. Os métodos find*
permitem especificar um objeto com a interface FindOneOptions
/ FindManyOptions
.
import { Photo } from "./entity/Photo"
import { PhotoMetadata } from "./entity/PhotoMetadata"
import { AppDataSource } from "./index"
const photoRepository = AppDataSource . getRepository ( Photo )
const photos = await photoRepository . find ( {
relations : {
metadata : true ,
} ,
} )
Aqui, as fotos conterão uma variedade de fotos do banco de dados e cada foto conterá seus metadados fotográficos. Saiba mais sobre as opções de localização nesta documentação.
O uso de opções do Find é bom e morto simples, mas se você precisar de uma consulta mais complexa, use QueryBuilder
. QueryBuilder
permite que consultas mais complexas sejam usadas de maneira elegante:
import { Photo } from "./entity/Photo"
import { PhotoMetadata } from "./entity/PhotoMetadata"
import { AppDataSource } from "./index"
const photos = await AppDataSource . getRepository ( Photo )
. createQueryBuilder ( "photo" )
. innerJoinAndSelect ( "photo.metadata" , "metadata" )
. getMany ( )
QueryBuilder
permite a criação e execução de consultas SQL de quase qualquer complexidade. Quando você trabalha com QueryBuilder
, pense como se estivesse criando uma consulta SQL. Neste exemplo, "foto" e "metadados" são aliases aplicados a fotos selecionadas. Você usa aliases para acessar colunas e propriedades dos dados selecionados.
Podemos configurar opções em cascata em nossas relações, nos casos em que queremos que nosso objeto relacionado seja salvo sempre que o outro objeto for salvo. Vamos mudar um pouco o decorador @OneToOne
da nossa foto:
export class Photo {
// ... other columns
@ OneToOne ( ( ) => PhotoMetadata , ( metadata ) => metadata . photo , {
cascade : true ,
} )
metadata : PhotoMetadata
}
O uso cascade
nos permite não salvar separadamente fotos e salvar separadamente os objetos de metadados agora. Agora, podemos simplesmente salvar um objeto fotográfico, e o objeto de metadados será salvo automaticamente devido às opções de cascata.
import { AppDataSource } from "./index"
// create photo object
const 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
const metadata = new PhotoMetadata ( )
metadata . height = 640
metadata . width = 480
metadata . compressed = true
metadata . comment = "cybershoot"
metadata . orientation = "portrait"
photo . metadata = metadata // this way we connect them
// get repository
const photoRepository = AppDataSource . getRepository ( Photo )
// saving a photo also save the metadata
await photoRepository . save ( photo )
console . log ( "Photo is saved, photo metadata is saved too." )
Observe que agora definimos a propriedade metadata
da foto, em vez da propriedade photo
dos metadados como antes. O recurso cascade
funciona apenas se você conectar a foto aos seus metadados do lado da foto. Se você definir o lado dos metadados, os metadados não serão salvos automaticamente.
Vamos criar uma relação muitos para um/um para muitos. Digamos que uma foto tenha um autor e cada autor pode ter muitas fotos. Primeiro, vamos criar uma classe Author
:
import {
Entity ,
Column ,
PrimaryGeneratedColumn ,
OneToMany ,
JoinColumn ,
} from "typeorm"
import { Photo } from "./Photo"
@ Entity ( )
export class Author {
@ PrimaryGeneratedColumn ( )
id : number
@ Column ( )
name : string
@ OneToMany ( ( ) => Photo , ( photo ) => photo . author ) // note: we will create author property in the Photo class below
photos : Photo [ ]
}
Author
contém um lado inverso de uma relação. OneToMany
é sempre um lado inverso da relação, e não pode existir sem ManyToOne
do outro lado da relação.
Agora vamos adicionar o lado do proprietário da relação na entidade fotográfica:
import { Entity , Column , PrimaryGeneratedColumn , ManyToOne } from "typeorm"
import { PhotoMetadata } from "./PhotoMetadata"
import { Author } from "./Author"
@ Entity ( )
export class Photo {
/* ... other columns */
@ ManyToOne ( ( ) => Author , ( author ) => author . photos )
author : Author
}
Nas relações muitas para um / um para muitos, o lado do proprietário é sempre muitos para um. Isso significa que a classe que usa @ManyToOne
armazenará o ID do objeto relacionado.
Depois de executar o aplicativo, o ORM criará a tabela author
:
+-------------+--------------+----------------------------+
| author |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+
Ele também modificará a tabela photo
, adicionando uma nova coluna author
e criando uma chave estrangeira para ela:
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
| description | varchar(255) | |
| filename | varchar(255) | |
| isPublished | boolean | |
| authorId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
Vamos criar uma relação muitos para muitos. Digamos que uma foto possa estar em muitos álbuns, e cada álbum pode conter muitas fotos. Vamos criar uma aula Album
:
import {
Entity ,
PrimaryGeneratedColumn ,
Column ,
ManyToMany ,
JoinTable ,
} from "typeorm"
@ Entity ( )
export class Album {
@ PrimaryGeneratedColumn ( )
id : number
@ Column ( )
name : string
@ ManyToMany ( ( ) => Photo , ( photo ) => photo . albums )
@ JoinTable ( )
photos : Photo [ ]
}
@JoinTable
é necessário para especificar que este é o lado do proprietário do relacionamento.
Agora vamos adicionar o lado inverso de nossa relação com a aula Photo
:
export class Photo {
// ... other columns
@ ManyToMany ( ( ) => Album , ( album ) => album . photos )
albums : Album [ ]
}
Depois de executar o aplicativo, o ORM criará um album_photos_photo_albums tabela de junção :
+-------------+--------------+----------------------------+
| album_photos_photo_albums |
+-------------+--------------+----------------------------+
| album_id | int(11) | PRIMARY KEY FOREIGN KEY |
| photo_id | int(11) | PRIMARY KEY FOREIGN KEY |
+-------------+--------------+----------------------------+
Não se esqueça de registrar a aula Album
com sua conexão no ORM:
const options : DataSourceOptions = {
// ... other options
entities : [ Photo , PhotoMetadata , Author , Album ] ,
}
Agora vamos inserir álbuns e fotos em nosso banco de dados:
import { AppDataSource } from "./index"
// create a few albums
const album1 = new Album ( )
album1 . name = "Bears"
await AppDataSource . manager . save ( album1 )
const album2 = new Album ( )
album2 . name = "Me"
await AppDataSource . manager . save ( album2 )
// create a few photos
const photo = new Photo ( )
photo . name = "Me and Bears"
photo . description = "I am near polar bears"
photo . filename = "photo-with-bears.jpg"
photo . views = 1
photo . isPublished = true
photo . albums = [ album1 , album2 ]
await AppDataSource . manager . save ( photo )
// now our photo is saved and albums are attached to it
// now lets load them:
const loadedPhoto = await AppDataSource . getRepository ( Photo ) . findOne ( {
where : {
id : 1 ,
} ,
relations : {
albums : true ,
} ,
} )
loadedPhoto
será igual a:
{
id : 1 ,
name : "Me and Bears" ,
description : "I am near polar bears" ,
filename : "photo-with-bears.jpg" ,
albums : [ {
id : 1 ,
name : "Bears"
} , {
id : 2 ,
name : "Me"
} ]
}
Você pode usar o QueryBuilder para criar consultas SQL de quase qualquer complexidade. Por exemplo, você pode fazer isso:
const photos = await AppDataSource . getRepository ( Photo )
. createQueryBuilder ( "photo" ) // first argument is an alias. Alias is what you are selecting - photos. You must specify it.
. innerJoinAndSelect ( "photo.metadata" , "metadata" )
. leftJoinAndSelect ( "photo.albums" , "album" )
. where ( "photo.isPublished = true" )
. andWhere ( "(photo.name = :photoName OR photo.name = :bearName)" )
. orderBy ( "photo.id" , "DESC" )
. skip ( 5 )
. take ( 10 )
. setParameters ( { photoName : "My" , bearName : "Mishka" } )
. getMany ( )
Esta consulta seleciona todas as fotos publicadas com nomes "My" ou "Mishka". Ele selecionará os resultados da posição 5 (deslocamento da paginação) e selecionará apenas 10 resultados (limite de paginação). O resultado da seleção será ordenado por ID em ordem decrescente. Os álbuns de fotos serão deixados unidos e seus metadados serão ingressos internos.
Você usará muito o construtor de consultas em seu aplicativo. Saiba mais sobre o QueryBuilder aqui.
Dê uma olhada nas amostras na amostra para exemplos de uso.
Existem alguns repositórios com os quais você pode clonar e começar:
Existem várias extensões que simplificam o trabalho com o Typeorm e integrando -o com outros módulos:
data-source.ts
Após a geração de migrações/entidades-Typeorm-CodeBase-Syncrelations
- Typeorm -Relationsrelations
automaticamente com base em uma consulta grafql-typeorm-relações-graphql Aprenda sobre a contribuição aqui e como configurar seu ambiente de desenvolvimento aqui.
Este projeto existe graças a todas as pessoas que contribuem:
O código aberto é difícil e demorado. Se você deseja investir no futuro do Typeorm, pode se tornar um patrocinador e permitir que nossa equipe principal gaste mais tempo nas melhorias e novos recursos do Typeorm. Torne -se um patrocinador
Torne -se um patrocinador de ouro e obtenha suporte técnico premium de nossos principais colaboradores. Torne -se um patrocinador de ouro