Typeorm은 Nodejs, 브라우저, 코르도바, 전화기, 이온, 반응 네이티브, 네이티브 스크립트, 엑스포 및 전자 플랫폼에서 실행할 수있는 ORM이며 TypeScript 및 JavaScript (ES2021)와 함께 사용할 수 있습니다. 목표는 항상 최신 JavaScript 기능을 지원하고 몇 개의 테이블이있는 작은 응용 프로그램에서 여러 데이터베이스가있는 대규모 엔터프라이즈 애플리케이션에 이르기까지 데이터베이스를 사용하는 모든 종류의 애플리케이션을 개발하는 데 도움이되는 추가 기능을 제공하는 것입니다.
Typeorm은 현재 존재하는 다른 모든 JavaScript Orm과 달리 활성 레코드 및 데이터 맵퍼 패턴을 모두 지원하므로 가장 생산적인 방식으로 고품질, 느슨하게 결합, 확장 가능하며 유지 관리 가능한 응용 프로그램을 작성할 수 있습니다.
Typeorm은 최대 절전 모드, 교리 및 단체 프레임 워크와 같은 다른 ORM의 영향을 많이받습니다.
발표 : Typeorm의 미래
우리는 장기적으로 안정적이고 강력하며 지속 가능한 기초를 구축하는 데 중점을 둔 전략 인 활성화 된 Typeorm에 대한 비전을 공유하게되어 기쁩니다. 우리가 유지 보수를 구조화하고 전용 자원을 모아서 Offeorm이 몇 년 동안 번성 할 수 있도록하는 방법을 알아보십시오.
전체 공지 사항을 읽으십시오
그리고 더 ...
Typeorm을 사용하면 모델이 다음과 같습니다.
import { Entity , PrimaryGeneratedColumn , Column } from "typeorm"
@ Entity ( )
export class User {
@ PrimaryGeneratedColumn ( )
id : number
@ Column ( )
firstName : string
@ Column ( )
lastName : string
@ Column ( )
age : number
}
도메인 로직은 다음과 같습니다.
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 )
또는 ActiveRecord
구현을 선호하는 경우 다음을 사용할 수 있습니다.
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
}
그리고 도메인 논리는 다음과 같이 보입니다.
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 ( )
NPM 패키지 설치 :
npm install typeorm --save
reflect-metadata
Shim을 설치해야합니다.
npm install reflect-metadata --save
앱의 글로벌 장소 어딘가에서 가져 오십시오 (예 : app.ts
) :
import "reflect-metadata"
노드 타이핑을 설치해야 할 수도 있습니다.
npm install @types/node --save-dev
데이터베이스 드라이버 설치 :
MySQL 또는 Mariadb 의 경우
npm install mysql --save
(대신 mysql2
설치할 수 있음)
postgresql 또는 cockroachdb 용
npm install pg --save
sqlite 의 경우
npm install sqlite3 --save
Microsoft SQL Server 용
npm install mssql --save
sql.js 의 경우
npm install sql.js --save
오라클을 위해
npm install oracledb --save
Oracle 드라이버를 작동 시키려면 해당 사이트의 설치 지침을 따라야합니다.
SAP HANA 의 경우
npm install @sap/hana-client
npm install hdb-pool
SAP HANA 지원은 Neptune 소프트웨어의 후원으로 가능합니다.
Google Cloud Spanner 의 경우
npm install @google-cloud/spanner --save
환경 변수 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.
에뮬레이터와 함께 Spanner를 사용하려면 SPANNER_EMULATOR_HOST
환경 변수를 설정해야합니다.
# Linux/macOS
export SPANNER_EMULATOR_HOST=localhost:9010
# Windows
set SPANNER_EMULATOR_HOST=localhost:9010
mongodb 용 (실험)
npm install mongodb@^5.2.0 --save
원주민 스크립트 의 경우 반응 및 코르도바
지원되는 플랫폼의 문서를 확인하십시오
사용하는 데이터베이스에 따라 그 중 하나만 설치하십시오.
또한 TypeScript 버전 4.5 이상을 사용하고 있는지 확인하고 tsconfig.json
에서 다음 설정을 활성화했습니다.
"emitDecoratorMetadata" : true ,
"experimentalDecorators" : true ,
컴파일러 옵션의 lib
섹션에서 es6
활성화하거나 @types
에서 es6-shim
설치해야 할 수도 있습니다.
TypeOrm으로 시작하는 가장 빠른 방법은 CLI 명령을 사용하여 스타터 프로젝트를 생성하는 것입니다. 빠른 시작은 NodeJS 응용 프로그램에서 TypeOrm을 사용하는 경우에만 작동합니다. 다른 플랫폼을 사용하는 경우 단계별 안내서를 진행하십시오.
CLI를 사용하여 새 프로젝트를 만들려면 다음 명령을 실행하십시오.
npx typeorm init --name MyProject --database postgres
name
이 프로젝트 이름이고 database
사용할 데이터베이스입니다. 데이터베이스는 mysql
, mariadb
, postgres
, cockroachdb
, sqlite
, mssql
, sap
, spanner
, oracle
, mongodb
, cordova
, react-native
, expo
, nativescript
의 다음 값 중 하나 일 수 있습니다.
이 명령은 MyProject
디렉토리에서 다음 파일로 새 프로젝트를 생성합니다.
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
기존 노드 프로젝트에서
typeorm init
실행할 수도 있지만 조심하십시오. 이미 가지고있는 일부 파일을 무시할 수 있습니다.
다음 단계는 새로운 프로젝트 종속성을 설치하는 것입니다.
cd MyProject
npm install
모든 종속성이 설치된 후 data-source.ts
파일을 편집하고 자체 데이터베이스 연결 구성 옵션을 거기에 넣습니다.
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 : [ ] ,
} )
특히 대부분의 경우 host
, username
, password
, database
및 port
옵션 만 구성하면됩니다.
구성으로 완료되고 모든 노드 모듈이 설치되면 응용 프로그램을 실행할 수 있습니다.
npm start
바로 응용 프로그램이 새로운 사용자를 데이터베이스에 성공적으로 실행하고 삽입해야합니다. 이 프로젝트를 계속 작업하고 필요한 다른 모듈을 통합하고 더 많은 엔티티를 만들 수 있습니다.
npx typeorm init --name MyProject --database postgres --module esm
명령을 실행하여 ESM 프로젝트를 생성 할 수 있습니다.
npx typeorm init --name MyProject --database mysql --express
명령을 실행하여 Express를 설치하여 더욱 고급 프로젝트를 생성 할 수 있습니다.
npx typeorm init --name MyProject --database postgres --docker
명령을 실행하여 docker-compose 파일을 생성 할 수 있습니다.
ORM에서 무엇을 기대하고 있습니까? 우선, 당신은 당신이 당신을위한 데이터베이스 테이블을 생성하고 거의 유지 관리 할 수없는 SQL 쿼리를 많이 쓰지 않아도 데이터를 찾아 / 삽입 / 업데이트 / 삭제할 것으로 기대하고 있습니다. 이 안내서는 TypeOrm을 처음부터 설정하고 ORM에서 기대하는 것을 수행하는 방법을 보여줍니다.
데이터베이스로 작업하는 것은 테이블 작성으로 시작됩니다. typeorm에 데이터베이스 테이블을 만드는 방법은 무엇입니까? 대답은 모델을 통한 것입니다. 앱의 모델은 데이터베이스 테이블입니다.
예를 들어 Photo
모델이 있습니다.
export class Photo {
id : number
name : string
description : string
filename : string
views : number
isPublished : boolean
}
데이터베이스에 사진을 저장하고 싶습니다. 데이터베이스에 물건을 저장하려면 먼저 데이터베이스 테이블이 필요하며 데이터베이스 테이블이 모델에서 생성됩니다. 모든 모델이 아니라 엔티티 로 정의하는 모델 만.
엔티티는 @Entity
데코레이터로 장식 된 모델입니다. 이러한 모델에 대한 데이터베이스 테이블이 작성됩니다. 당신은 Typeorm의 모든 곳에서 엔티티와 함께 일합니다. 다른 작업을로드/삽입/업데이트/제거하고 수행 할 수 있습니다.
Photo
모델을 엔티티로 만들어 봅시다.
import { Entity } from "typeorm"
@ Entity ( )
export class Photo {
id : number
name : string
description : string
filename : string
views : number
isPublished : boolean
}
이제 Photo
엔티티를 위해 데이터베이스 테이블이 만들어지며 앱의 어느 곳에서나 작업 할 수 있습니다. 데이터베이스 테이블을 만들었지 만 열이 없으면 어떤 테이블이 존재할 수 있습니까? 데이터베이스 테이블에 몇 개의 열을 만들어 봅시다.
데이터베이스 열을 추가하려면 @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
}
이제 id
, name
, description
, filename
, views
및 isPublished
열이 photo
테이블에 추가됩니다. 데이터베이스의 열 유형은 사용한 속성 유형에서 추론되며, 예를 들어 number
integer
로 변환, varchar
로 string
, bool
으로 boolean
등으로 변환됩니다. 그러나 열 유형을 @Column
데코레이터.
열이있는 데이터베이스 테이블을 생성했지만 한 가지 남은 것이 있습니다. 각 데이터베이스 테이블에는 기본 키가있는 열이 있어야합니다.
각 엔티티에는 하나 이상의 기본 키 열이 있어야 합니다 . 이것은 요구 사항이며 피할 수 없습니다. 열을 기본 키로 만들려면 @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
}
이제 ID 열이 자동으로 생성되기를 원한다고 가정 해 봅시다 (이는 자동 점수 / 시퀀스 / 시리얼 / 생성 ID 열로 알려져 있음). 그렇게하려면 @PrimaryColumn
데코레이터를 @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
}
다음으로 데이터 유형을 수정하겠습니다. 기본적으로 문자열은 Varchar (255)와 같은 유형 (데이터베이스 유형에 따라 다름)에 매핑됩니다. 숫자는 정수와 같은 유형 (데이터베이스 유형에 따라 다름)에 매핑됩니다. 우리는 모든 열이 제한된 바르 차르 또는 정수가되기를 원하지 않습니다. 올바른 데이터 유형을 설정하겠습니다.
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
}
열 유형은 데이터베이스 별입니다. 데이터베이스가 지원하는 모든 열 유형을 설정할 수 있습니다. 지원되는 열 유형에 대한 자세한 내용은 여기를 참조하십시오.
DataSource
생성 이제 엔티티가 만들어지면 index.ts
파일을 만들고 DataSource
설정하겠습니다.
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 ) )
이 예에서는 Postgres를 사용하고 있지만 다른 지원되는 데이터베이스를 사용할 수 있습니다. 다른 데이터베이스를 사용하려면 mysql
, mariadb
, postgres
, cockroachdb
, sqlite
, mssql
, oracle
type
SAP, Spanner, cordova
, sap
nativescript
spanner
react-native
, expo
또는 mongodb
. 또한 자신의 호스트, 포트, 사용자 이름, 비밀번호 및 데이터베이스 설정을 사용해야합니다.
이 데이터 소스의 엔티티 목록에 사진 엔티티를 추가했습니다. 연결 중에 사용중인 각 엔티티는 거기에 나열되어야합니다.
synchronize
설정은 응용 프로그램을 실행할 때마다 엔티티가 데이터베이스와 동기화되도록합니다.
이제 index.ts
실행하면 데이터베이스와의 연결이 초기화되고 사진의 데이터베이스 테이블이 작성됩니다.
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(100) | |
| description | text | |
| filename | varchar(255) | |
| views | int(11) | |
| isPublished | boolean | |
+-------------+--------------+----------------------------+
이제 데이터베이스에 저장할 새 사진을 만들어 봅시다.
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 )
엔티티가 저장되면 새로 생성 된 ID가 표시됩니다. 메소드 save
전달한 객체의 인스턴스를 반환합니다. 객체의 새로운 사본이 아니며 "ID"를 수정하고 반환합니다.
방금 새 사진을 만들어 데이터베이스에 저장했습니다. EntityManager
사용하여 저장했습니다. 엔티티 관리자를 사용하면 앱의 엔티티를 조작 할 수 있습니다. 예를 들어 저장된 엔티티를로드하겠습니다.
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
데이터베이스에서로드 된 데이터가있는 다양한 사진 객체입니다.
EntityManager에 대해 자세히 알아보십시오.
이제 코드를 리팩토링하고 EntityManager
대신 Repository
사용하겠습니다. 각 엔티티에는 모든 작업을 엔티티와 함께 처리하는 자체 저장소가 있습니다. 엔티티를 많이 다룰 때 리포지토리는 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 )
저장소에 대해 자세히 알아보십시오.
저장소를 사용하여 더 많은로드 작업을 시도해 봅시다.
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 )
이제 데이터베이스에서 단일 사진을로드하여 업데이트하고 저장하겠습니다.
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 )
이제 id = 1
인 사진은 데이터베이스에서 업데이트됩니다.
이제 데이터베이스에서 사진을 제거하겠습니다.
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 )
이제 id = 1
인 사진은 데이터베이스에서 제거됩니다.
다른 클래스와 일대일 관계를 만들어 봅시다. PhotoMetadata.ts
에서 새로운 클래스를 만들어 봅시다. 이 Photometadata 클래스는 사진의 추가 메타 정보를 포함해야합니다.
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
}
여기에서 우리는 @OneToOne
이라는 새로운 데코레이터를 사용하고 있습니다. 이를 통해 두 엔티티간에 일대일 관계를 만들 수 있습니다. type => Photo
우리가 관계를 맺고 자하는 엔티티의 클래스를 반환하는 함수입니다. 언어 세부 사항 때문에 클래스를 직접 사용하는 대신 클래스를 반환하는 함수를 사용해야합니다. 또한 () => Photo
로 쓸 수 있지만 CODE 가독성을 높이기 위해 type => Photo
컨벤션으로 사용합니다. 유형 변수 자체에는 아무것도 포함되어 있지 않습니다.
우리는 또한 @JoinColumn
데코레이터를 추가하는데, 이는 관계 의이 측면이 관계를 소유 할 것임을 나타냅니다. 관계는 단방향 또는 양방향 일 수 있습니다. 관계형의 한 측면 만 소유 할 수 있습니다. 관계의 소유자 측면에서 @JoinColumn
데코레이터를 사용해야합니다.
앱을 실행하면 새로 생성 된 테이블이 표시되며 사진 관계에 대한 외국 키가있는 열이 포함됩니다.
+-------------+--------------+----------------------------+
| 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 |
+-------------+--------------+----------------------------+
이제 사진과 메타 데이터를 저장하고 서로 첨부하겠습니다.
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" ,
)
관계는 단방향 또는 양방향 일 수 있습니다. 현재 Photometadata와 Photo 사이의 관계는 단방향입니다. 관계의 소유자는 Photometadata이며 사진은 Photometadata에 대해 아무것도 모릅니다. 이로 인해 사진 측면에서 Photometadata에 액세스하는 것이 복잡해집니다. 이 문제를 해결하기 위해서는 역 관계를 추가하고 광 라타 다타와 사진 양방향과 관계를 맺어야합니다. 엔티티를 수정합시다.
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
는 관계의 반대편 이름을 반환하는 함수입니다. 여기서 우리는 사진 클래스의 메타 데이터 속성이 사진 클래스에 Photometadata를 저장하는 곳임을 보여줍니다. 사진의 속성을 반환하는 함수를 전달하는 대신 "metadata"
와 같은 @OneToOne
데코레이터에 문자열을 전달할 수 있습니다. 그러나 우리는이 기능성 접근 방식을 사용하여 리팩토링을보다 쉽게 만들었습니다.
우리는 관계의 한쪽에만 @JoinColumn
데코레이터를 사용해야합니다. 이 데코레이터를 착용 한 쪽이 관계의 소유 측면이 될 것입니다. 관계의 소유 측면에는 데이터베이스에 외국 키가있는 열이 포함되어 있습니다.
TypeScript 프로젝트에서 ESM을 사용하는 경우 관계 속성의 Relation
래퍼 유형을 사용하여 원형 의존성 문제를 피해야합니다. 엔티티를 수정합시다.
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 >
}
이제 사진과 사진 메타 데이터를 단일 쿼리로로드하겠습니다. find*
메소드를 사용하거나 QueryBuilder
기능을 사용하는 두 가지 방법이 있습니다. 먼저 find*
메소드를 사용해 봅시다. find*
메소드를 사용하면 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 ,
} ,
} )
여기에는 사진이 데이터베이스의 사진 배열이 포함되어 있으며 각 사진에는 사진 메타 데이터가 포함됩니다. 이 문서에서 옵션 찾기에 대해 자세히 알아보십시오.
찾기 옵션을 사용하는 것은 좋고 죽은 것이 단순하지만 더 복잡한 쿼리가 필요한 경우 대신 QueryBuilder
사용해야합니다. QueryBuilder
더 복잡한 쿼리를 우아한 방식으로 사용할 수 있습니다.
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
거의 모든 복잡성의 SQL 쿼리를 생성하고 실행할 수 있습니다. QueryBuilder
와 함께 일할 때 SQL 쿼리를 작성하는 것처럼 생각하십시오. 이 예에서 "사진"및 "메타 데이터"는 선택한 사진에 적용되는 별칭입니다. 별명을 사용하여 선택한 데이터의 열 및 속성에 액세스합니다.
우리는 다른 객체가 저장 될 때마다 관련 객체를 저장하기를 원하는 경우 관계에서 캐스케이드 옵션을 설정할 수 있습니다. 사진의 @OneToOne
데코레이터를 조금 바르자 :
export class Photo {
// ... other columns
@ OneToOne ( ( ) => PhotoMetadata , ( metadata ) => metadata . photo , {
cascade : true ,
} )
metadata : PhotoMetadata
}
cascade
사용하면 사진을 별도로 저장하지 않고 메타 데이터 객체를 별도로 저장할 수 있습니다. 이제 간단히 사진 객체를 저장할 수 있으며 캐스케이드 옵션으로 인해 메타 데이터 객체가 자동으로 저장됩니다.
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." )
이제 이전과 같이 메타 데이터의 photo
속성 대신 사진의 metadata
속성을 설정합니다. cascade
기능은 사진을 사진 측면에서 메타 데이터에 연결하는 경우에만 작동합니다. 메타 데이터 쪽을 설정하면 메타 데이터가 자동으로 저장되지 않습니다.
다중/일대일 관계를 만들어 봅시다. 사진에 한 명의 저자가 있고 각 저자가 많은 사진을 가질 수 있다고 가정 해 봅시다. 먼저, 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
관계의 반대편을 포함합니다. OneToMany
항상 관계의 반대 측면이며 관계의 반대편에 ManyToOne
없으면 존재할 수 없습니다.
이제 관계의 소유자 측면을 사진 엔티티에 추가하겠습니다.
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
}
다중 / 일대일 관계에서 소유자 측은 항상 다릅니다. @ManyToOne
사용하는 클래스는 관련 객체의 ID를 저장한다는 것을 의미합니다.
응용 프로그램을 실행하면 ORM은 author
테이블을 만듭니다.
+-------------+--------------+----------------------------+
| author |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+
또한 photo
테이블을 수정하고 새 author
열을 추가하고 이에 대한 외국 키를 만듭니다.
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
| description | varchar(255) | |
| filename | varchar(255) | |
| isPublished | boolean | |
| authorId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
다수의 관계를 만들어 봅시다. 사진이 많은 앨범에있을 수 있으며 각 앨범에 많은 사진이 포함될 수 있다고 가정 해 봅시다. 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
이것이 관계의 소유자 측면임을 지정해야합니다.
이제 Photo
클래스와의 관계의 역을 추가하겠습니다.
export class Photo {
// ... other columns
@ ManyToMany ( ( ) => Album , ( album ) => album . photos )
albums : Album [ ]
}
응용 프로그램을 실행하면 ORM은 앨범 을 만듭니다.
+-------------+--------------+----------------------------+
| album_photos_photo_albums |
+-------------+--------------+----------------------------+
| album_id | int(11) | PRIMARY KEY FOREIGN KEY |
| photo_id | int(11) | PRIMARY KEY FOREIGN KEY |
+-------------+--------------+----------------------------+
ORM에서 연결하여 Album
클래스를 등록하는 것을 잊지 마십시오.
const options : DataSourceOptions = {
// ... other options
entities : [ Photo , PhotoMetadata , Author , Album ] ,
}
이제 앨범과 사진을 데이터베이스에 삽입하겠습니다.
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
다음과 같습니다.
{
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"
} ]
}
QueryBuilder를 사용하여 거의 모든 복잡성의 SQL 쿼리를 구축 할 수 있습니다. 예를 들어 다음을 수행 할 수 있습니다.
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 ( )
이 쿼리는 "My"또는 "Mishka"이름으로 게시 된 모든 사진을 선택합니다. 위치 5 (Pagination Offset)에서 결과를 선택하고 10 개의 결과 (Pagination Limit) 만 선택합니다. 선택 결과는 내림차순으로 ID로 주문됩니다. 사진 앨범이 결합되고 메타 데이터가 내부에 결합됩니다.
응용 프로그램에서 쿼리 빌더를 많이 사용합니다. QueryBuilder에 대해 자세히 알아보십시오.
사용의 예를 위해 샘플의 샘플을 살펴보십시오.
복제하고 시작할 수있는 몇 가지 저장소가 있습니다.
Typeorm으로 작업하고 다른 모듈과 통합하는 몇 가지 확장 기능이 있습니다.
data-source.ts
자동으로 업데이트합니다relations
대상의 쉬운 조작 - 형태 - 선형relations
자동으로 생성합니다-TypeOrm-Relations-GraphQL 여기에서 기여도와 개발 환경을 설정하는 방법에 대해 알아보십시오.
이 프로젝트는 다음과 같이 기부 한 모든 사람들 덕분에 있습니다.
오픈 소스는 어렵고 시간이 많이 걸립니다. Typeorm의 미래에 투자하려면 스폰서가되어 핵심 팀이 Typeorm의 개선 및 새로운 기능에 더 많은 시간을 할애 할 수 있습니다. 스폰서가 되십시오
골드 스폰서가되어 핵심 기고자로부터 프리미엄 기술 지원을 받으십시오. 금 스폰서가 되십시오