Typeorm adalah ORM yang dapat berjalan di NodeJs, Browser, Cordova, PhoneGap, Ionic, React Native, NativeScript, Expo, dan Platform elektron dan dapat digunakan dengan TypeScript dan JavaScript (ES2021). Tujuannya adalah untuk selalu mendukung fitur JavaScript terbaru dan menyediakan fitur tambahan yang membantu Anda mengembangkan segala jenis aplikasi yang menggunakan basis data - dari aplikasi kecil dengan beberapa tabel hingga aplikasi perusahaan skala besar dengan banyak database.
Typeorm mendukung pola catatan aktif dan data mapper, tidak seperti semua orms javascript lainnya saat ini ada, yang berarti Anda dapat menulis aplikasi berkualitas tinggi, digabungkan secara longgar, dapat diskalakan, dan dapat dipelihara dengan cara yang paling produktif.
Typeorm sangat dipengaruhi oleh ORM lain, seperti kerangka kerja hibernasi, doktrin dan entitas.
Pengumuman: Masa Depan Typeorm
Kami senang berbagi visi kami untuk Typeorm yang direvitalisasi - strategi yang berfokus pada membangun fondasi yang stabil, kuat, dan berkelanjutan untuk jangka panjang. Pelajari bagaimana kami menyusun pemeliharaan dan menyatukan sumber daya yang berdedikasi untuk memastikan Typeorm berkembang pesat di tahun -tahun mendatang.
Baca pengumuman lengkapnya
Dan lebih banyak ...
Dengan Typeorm model Anda terlihat seperti ini:
import { Entity , PrimaryGeneratedColumn , Column } from "typeorm"
@ Entity ( )
export class User {
@ PrimaryGeneratedColumn ( )
id : number
@ Column ( )
firstName : string
@ Column ( )
lastName : string
@ Column ( )
age : number
}
Dan logika domain Anda terlihat seperti ini:
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 )
Atau, jika Anda lebih suka menggunakan implementasi ActiveRecord
, Anda dapat menggunakannya juga:
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
}
Dan logika domain Anda akan terlihat seperti ini:
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 ( )
Instal Paket NPM:
npm install typeorm --save
Anda perlu menginstal reflect-metadata
shim:
npm install reflect-metadata --save
dan impor di suatu tempat di tempat global aplikasi Anda (misalnya dalam app.ts
):
import "reflect-metadata"
Anda mungkin perlu menginstal Typings Node:
npm install @types/node --save-dev
Instal driver database:
untuk mysql atau mariadb
npm install mysql --save
(Anda dapat menginstal mysql2
juga)
untuk postgresql atau cockroachdb
npm install pg --save
untuk sqlite
npm install sqlite3 --save
untuk Microsoft SQL Server
npm install mssql --save
untuk sql.js
npm install sql.js --save
untuk Oracle
npm install oracledb --save
Untuk membuat driver Oracle berfungsi, Anda harus mengikuti instruksi instalasi dari situs mereka.
untuk SAP HANA
npm install @sap/hana-client
npm install hdb-pool
Dukungan SAP HANA dimungkinkan oleh sponsor perangkat lunak Neptunus.
Untuk Google Cloud Spanner
npm install @google-cloud/spanner --save
Berikan kredensial otentikasi ke kode aplikasi Anda dengan mengatur variabel lingkungan 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.
Untuk menggunakan Spanner dengan emulator Anda harus mengatur variabel lingkungan SPANNER_EMULATOR_HOST
:
# Linux/macOS
export SPANNER_EMULATOR_HOST=localhost:9010
# Windows
set SPANNER_EMULATOR_HOST=localhost:9010
Untuk MongoDB (Eksperimental)
npm install mongodb@^5.2.0 --save
untuk nativeScript , reaksi-asli dan cordova
Periksa dokumentasi platform yang didukung
Instal hanya satu dari mereka, tergantung pada basis data yang Anda gunakan.
Juga, pastikan Anda menggunakan TypeScript versi 4.5 atau lebih tinggi, dan Anda telah mengaktifkan pengaturan berikut di tsconfig.json
:
"emitDecoratorMetadata" : true ,
"experimentalDecorators" : true ,
Anda mungkin juga perlu mengaktifkan es6
di bagian lib
opsi kompiler, atau menginstal es6-shim
dari @types
.
Cara tercepat untuk memulai dengan Typeorm adalah dengan menggunakan perintah CLI -nya untuk menghasilkan proyek starter. Mulai cepat hanya berfungsi jika Anda menggunakan Typeorm dalam aplikasi NodeJS. Jika Anda menggunakan platform lain, lanjutkan ke panduan langkah demi langkah.
Untuk membuat proyek baru menggunakan CLI, jalankan perintah berikut:
npx typeorm init --name MyProject --database postgres
Di mana name
adalah nama proyek dan database
Anda adalah database yang akan Anda gunakan. Basis data dapat menjadi salah satu nilai berikut: mysql
, mariadb
, postgres
, cockroachdb
, sqlite
, mssql
, sap
, spanner
, oracle
, mongodb
, cordova
, react-native
, expo
, nativescript
.
Perintah ini akan menghasilkan proyek baru di Direktori MyProject
dengan file -file berikut:
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
Anda juga dapat menjalankan
typeorm init
pada proyek node yang ada, tetapi berhati -hatilah - mungkin mengesampingkan beberapa file yang sudah Anda miliki.
Langkah selanjutnya adalah menginstal dependensi proyek baru:
cd MyProject
npm install
Setelah semua dependensi diinstal, edit file data-source.ts
dan letakkan opsi konfigurasi koneksi database Anda sendiri di sana:
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 : [ ] ,
} )
Khususnya, sebagian besar waktu Anda hanya perlu mengonfigurasi host
, username
, password
, database
, dan mungkin opsi port
.
Setelah Anda selesai dengan konfigurasi dan semua modul node diinstal, Anda dapat menjalankan aplikasi Anda:
npm start
Itu saja, aplikasi Anda harus berhasil berjalan dan memasukkan pengguna baru ke dalam database. Anda dapat terus bekerja dengan proyek ini dan mengintegrasikan modul lain yang Anda butuhkan dan mulai membuat lebih banyak entitas.
Anda dapat menghasilkan proyek ESM dengan menjalankan
npx typeorm init --name MyProject --database postgres --module esm
modul.
Anda dapat menghasilkan proyek yang bahkan lebih maju dengan ekspres diinstal dengan menjalankan
npx typeorm init --name MyProject --database mysql --express
express.
Anda dapat menghasilkan file komposisi Docker dengan menjalankan
npx typeorm init --name MyProject --database postgres --docker
command.
Apa yang Anda harapkan dari ORM? Pertama -tama, Anda mengharapkannya akan membuat tabel basis data untuk Anda dan menemukan / memasukkan / memperbarui / menghapus data Anda tanpa rasa sakit karena harus menulis banyak kueri SQL yang hampir tidak dapat dipelihara. Panduan ini akan menunjukkan kepada Anda cara mengatur Typeorm dari awal dan membuatnya melakukan apa yang Anda harapkan dari ORM.
Bekerja dengan database dimulai dengan membuat tabel. Bagaimana Anda memberi tahu Typeorm untuk membuat tabel basis data? Jawabannya adalah - melalui model. Model Anda di aplikasi Anda adalah tabel basis data Anda.
Misalnya, Anda memiliki model Photo
:
export class Photo {
id : number
name : string
description : string
filename : string
views : number
isPublished : boolean
}
Dan Anda ingin menyimpan foto di database Anda. Untuk menyimpan barang -barang dalam database, pertama, Anda memerlukan tabel basis data, dan tabel basis data dibuat dari model Anda. Tidak semua model, tetapi hanya mereka yang Anda definisikan sebagai entitas .
Entitas adalah model Anda yang dihiasi oleh dekorator @Entity
. Tabel basis data akan dibuat untuk model tersebut. Anda bekerja dengan entitas di mana -mana di Typeorm. Anda dapat memuat/memasukkan/memperbarui/menghapus dan melakukan operasi lain dengannya.
Mari Jadikan Model Photo
Kita Entitas:
import { Entity } from "typeorm"
@ Entity ( )
export class Photo {
id : number
name : string
description : string
filename : string
views : number
isPublished : boolean
}
Sekarang, tabel basis data akan dibuat untuk entitas Photo
dan kami akan dapat bekerja dengannya di mana saja di aplikasi kami. Kami telah membuat tabel basis data, tabel apa yang bisa ada tanpa kolom? Mari kita buat beberapa kolom di tabel database kami.
Untuk menambahkan kolom basis data, Anda hanya perlu menghiasi properti entitas yang ingin Anda buat menjadi kolom dengan dekorator @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
}
Sekarang id
, name
, description
, filename
, views
, dan kolom isPublished
akan ditambahkan ke tabel photo
. Jenis kolom dalam database disimpulkan dari jenis properti yang Anda gunakan, misalnya number
akan dikonversi menjadi integer
, string
menjadi varchar
, boolean
menjadi bool
, dll. Tetapi Anda dapat menggunakan jenis kolom apa pun dukungan database Anda dengan secara eksplisit menentukan jenis kolom ke dalam @Column
Decorator.
Kami menghasilkan tabel basis data dengan kolom, tetapi ada satu hal yang tersisa. Setiap tabel database harus memiliki kolom dengan kunci utama.
Setiap entitas harus memiliki setidaknya satu kolom kunci utama. Ini adalah persyaratan dan Anda tidak dapat menghindarinya. Untuk menjadikan kolom sebagai kunci utama, Anda perlu menggunakan dekorator @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
}
Sekarang, katakanlah Anda ingin kolom ID Anda dihasilkan secara otomatis (ini dikenal sebagai Increment Auto / Sequence / Serial / yang dihasilkan kolom identitas). Untuk melakukan itu, Anda perlu mengubah dekorator @PrimaryColumn
menjadi @PrimaryGeneratedColumn
Decorator:
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
}
Selanjutnya, mari kita perbaiki tipe data kita. Secara default, string dipetakan ke tipe varchar (255) (tergantung pada jenis database). Angka ini dipetakan ke tipe seperti bilangan bulat (tergantung pada jenis database). Kami tidak ingin semua kolom kami menjadi varchar atau bilangan bulat terbatas. Mari Siapkan Jenis Data yang Benar:
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
}
Jenis kolom khusus database. Anda dapat mengatur ketik kolom apa saja dukungan database Anda. Informasi lebih lanjut tentang jenis kolom yang didukung dapat ditemukan di sini.
DataSource
baru Sekarang, ketika entitas kita dibuat, mari kita buat file index.ts
dan mengatur DataSource
kita di sana:
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 ) )
Kami menggunakan Postgres dalam contoh ini, tetapi Anda dapat menggunakan database yang didukung lainnya. Untuk menggunakan database lain, cukup ubah type
opsi ke jenis database yang Anda gunakan: mysql
, mariadb
, postgres
, cockroachdb
, sqlite
, mssql
, oracle
, sap
, spanner
, cordova
, nativescript
, react-native
, expo
, atau mongodb
. Pastikan juga untuk menggunakan host, port, nama pengguna, kata sandi, dan pengaturan basis data Anda sendiri.
Kami menambahkan entitas foto kami ke daftar entitas untuk sumber data ini. Setiap entitas yang Anda gunakan dalam koneksi Anda harus terdaftar di sana.
Pengaturan synchronize
Pastikan entitas Anda akan disinkronkan dengan database, setiap kali Anda menjalankan aplikasi.
Sekarang jika Anda menjalankan index.ts
Anda, koneksi dengan database akan diinisialisasi dan tabel database untuk foto Anda akan dibuat.
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(100) | |
| description | text | |
| filename | varchar(255) | |
| views | int(11) | |
| isPublished | boolean | |
+-------------+--------------+----------------------------+
Sekarang mari kita buat foto baru untuk menyimpannya di database:
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 )
Setelah entitas Anda disimpan, itu akan mendapatkan ID yang baru dihasilkan. save
metode mengembalikan instance dari objek yang sama yang Anda berikan padanya. Ini bukan salinan baru dari objek, itu memodifikasi "id" dan mengembalikannya.
Kami baru saja membuat foto baru dan menyimpannya di database. Kami menggunakan EntityManager
untuk menyimpannya. Menggunakan Entity Manager Anda dapat memanipulasi entitas apa pun di aplikasi Anda. Misalnya, mari kita muat entitas yang kami simpan:
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
akan menjadi array objek foto dengan data yang dimuat dari database.
Pelajari lebih lanjut tentang EntityManager di sini.
Sekarang mari kita refactor kode kita dan gunakan Repository
alih -alih EntityManager
. Setiap entitas memiliki repositori sendiri yang menangani semua operasi dengan entitasnya. Ketika Anda banyak berurusan dengan entitas, repositori lebih nyaman untuk digunakan daripada 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 )
Pelajari lebih lanjut tentang repositori di sini.
Mari kita coba lebih banyak operasi muat menggunakan repositori:
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 )
Sekarang mari kita muat satu foto dari database, perbarui dan simpan:
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 )
Sekarang foto dengan id = 1
akan diperbarui dalam database.
Sekarang mari kita hapus foto kita dari database:
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 )
Sekarang foto dengan id = 1
akan dihapus dari database.
Mari kita buat hubungan satu-ke-satu dengan kelas lain. Mari kita buat kelas baru di PhotoMetadata.ts
. Kelas photometadata ini seharusnya berisi meta-informasi tambahan foto kami:
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
}
Di sini, kami menggunakan dekorator baru yang disebut @OneToOne
. Ini memungkinkan kita untuk menciptakan hubungan satu-ke-satu antara dua entitas. type => Photo
adalah fungsi yang mengembalikan kelas entitas yang ingin kita buat hubungan kita. Kami terpaksa menggunakan fungsi yang mengembalikan kelas, alih -alih menggunakan kelas secara langsung, karena spesifik bahasa. Kami juga dapat menulisnya sebagai () => Photo
, tetapi kami menggunakan type => Photo
sebagai konvensi untuk meningkatkan keterbacaan kode. Variabel tipe itu sendiri tidak mengandung apa pun.
Kami juga menambahkan dekorator @JoinColumn
, yang menunjukkan bahwa sisi hubungan ini akan memiliki hubungan. Hubungan bisa searah atau dua arah. Hanya satu sisi relasional yang bisa dimiliki. Menggunakan @JoinColumn
Decorator diperlukan di sisi pemilik hubungan.
Jika Anda menjalankan aplikasi, Anda akan melihat tabel yang baru dihasilkan, dan itu akan berisi kolom dengan kunci asing untuk hubungan foto:
+-------------+--------------+----------------------------+
| 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 |
+-------------+--------------+----------------------------+
Sekarang mari kita simpan foto, dan metadatanya dan lampirkan satu sama lain.
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" ,
)
Hubungan bisa searah atau dua arah. Saat ini, hubungan kami antara photometadata dan foto secara searah. Pemilik relasi adalah photometadata, dan foto tidak tahu apa -apa tentang photometadata. Ini membuatnya rumit untuk mengakses photometadata dari sisi foto. Untuk memperbaiki masalah ini kita harus menambahkan hubungan terbalik, dan membuat hubungan antara photometadata dan foto dua arah. Mari kita ubah entitas kita:
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
adalah fungsi yang mengembalikan nama sisi terbalik dari relasi. Di sini kami menunjukkan bahwa properti metadata dari kelas foto adalah tempat kami menyimpan photometadata di kelas foto. Alih -alih melewati fungsi yang mengembalikan properti foto, Anda dapat dengan mudah meneruskan string ke dekorator @OneToOne
, seperti "metadata"
. Tetapi kami menggunakan pendekatan yang diketik fungsi ini untuk membuat refactoring kami lebih mudah.
Perhatikan bahwa kita harus menggunakan dekorator @JoinColumn
hanya di satu sisi hubungan. Sisi mana pun yang Anda pakai dekorator ini akan menjadi sisi kepemilikan hubungan. Sisi kepemilikan suatu hubungan berisi kolom dengan kunci asing dalam database.
Jika Anda menggunakan ESM dalam proyek TypeScript Anda, Anda harus menggunakan tipe pembungkus Relation
dalam sifat hubungan untuk menghindari masalah ketergantungan melingkar. Mari kita ubah entitas kita:
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 >
}
Sekarang mari kita muat foto kita dan metadata fotonya dalam satu kueri. Ada dua cara untuk melakukannya - menggunakan metode find*
atau menggunakan fungsionalitas QueryBuilder
. Mari kita gunakan find*
Method dulu. find*
Metode memungkinkan Anda untuk menentukan objek dengan antarmuka 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 ,
} ,
} )
Di sini, foto akan berisi serangkaian foto dari database, dan setiap foto akan berisi metadata fotonya. Pelajari lebih lanjut tentang menemukan opsi dalam dokumentasi ini.
Menggunakan opsi temukan itu baik dan mati sederhana, tetapi jika Anda memerlukan kueri yang lebih kompleks, Anda harus menggunakan QueryBuilder
sebagai gantinya. QueryBuilder
memungkinkan kueri yang lebih kompleks untuk digunakan dengan cara yang elegan:
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
memungkinkan penciptaan dan pelaksanaan pertanyaan SQL dari hampir semua kompleksitas. Saat Anda bekerja dengan QueryBuilder
, pikirkan seperti Anda membuat kueri SQL. Dalam contoh ini, "foto" dan "metadata" adalah alias yang diterapkan pada foto yang dipilih. Anda menggunakan alias untuk mengakses kolom dan properti dari data yang dipilih.
Kami dapat mengatur opsi kaskade dalam hubungan kami, dalam kasus ketika kami ingin objek terkait kami disimpan setiap kali objek lain disimpan. Ayo ubah sedikit dekorator @OneToOne
foto kami:
export class Photo {
// ... other columns
@ OneToOne ( ( ) => PhotoMetadata , ( metadata ) => metadata . photo , {
cascade : true ,
} )
metadata : PhotoMetadata
}
Menggunakan cascade
memungkinkan kita untuk tidak menyimpan foto secara terpisah dan secara terpisah menyimpan objek metadata sekarang. Sekarang kita dapat menyimpan objek foto, dan objek metadata akan disimpan secara otomatis karena opsi kaskade.
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." )
Perhatikan bahwa kami sekarang mengatur properti metadata
foto, alih -alih properti photo
metadata seperti sebelumnya. Fitur cascade
hanya berfungsi jika Anda menghubungkan foto ke metadata -nya dari sisi foto. Jika Anda mengatur sisi metadata, metadata tidak akan disimpan secara otomatis.
Mari kita buat hubungan banyak-ke-satu/satu-ke-banyak. Katakanlah foto memiliki satu penulis, dan setiap penulis dapat memiliki banyak foto. Pertama, mari kita buat kelas 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
berisi sisi terbalik dari suatu relasi. OneToMany
selalu merupakan sisi terbalik dari hubungan, dan itu tidak bisa ada tanpa ManyToOne
ke sisi lain dari hubungan.
Sekarang mari kita tambahkan sisi pemilik hubungan ke entitas foto:
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
}
Dalam banyak hubungan satu-ke-satu / satu-ke-banyak, sisi pemilik selalu banyak. Ini berarti bahwa kelas yang menggunakan @ManyToOne
akan menyimpan ID objek terkait.
Setelah Anda menjalankan aplikasi, ORM akan membuat tabel author
:
+-------------+--------------+----------------------------+
| author |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+
Ini juga akan memodifikasi tabel photo
, menambahkan kolom author
baru dan membuat kunci asing untuk itu:
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
| description | varchar(255) | |
| filename | varchar(255) | |
| isPublished | boolean | |
| authorId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
Mari kita buat hubungan banyak-ke-banyak. Katakanlah foto bisa ada di banyak album, dan setiap album dapat berisi banyak foto. Ayo buat kelas 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
diperlukan untuk menentukan bahwa ini adalah sisi pemilik hubungan.
Sekarang mari kita tambahkan sisi terbalik dari hubungan kita dengan kelas Photo
:
export class Photo {
// ... other columns
@ ManyToMany ( ( ) => Album , ( album ) => album . photos )
albums : Album [ ]
}
Setelah Anda menjalankan aplikasi, ORM akan membuat tabel persimpangan album_photos_photo_albums :
+-------------+--------------+----------------------------+
| album_photos_photo_albums |
+-------------+--------------+----------------------------+
| album_id | int(11) | PRIMARY KEY FOREIGN KEY |
| photo_id | int(11) | PRIMARY KEY FOREIGN KEY |
+-------------+--------------+----------------------------+
Jangan lupa untuk mendaftarkan kelas Album
dengan koneksi Anda di ORM:
const options : DataSourceOptions = {
// ... other options
entities : [ Photo , PhotoMetadata , Author , Album ] ,
}
Sekarang mari kita masukkan album dan foto ke dalam database kami:
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
akan sama dengan:
{
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"
} ]
}
Anda dapat menggunakan QueryBuilder untuk membangun kueri SQL dari hampir semua kompleksitas. Misalnya, Anda dapat melakukan ini:
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 ( )
Kueri ini memilih semua foto yang diterbitkan dengan nama "My" atau "Mishka". Ini akan memilih hasil dari posisi 5 (pagination offset) dan hanya akan memilih 10 hasil (batas pagination). Hasil seleksi akan dipesan oleh ID dalam urutan menurun. Album foto akan dibiarkan bergabung dan metadata mereka akan bergabung.
Anda akan sering menggunakan pembangun kueri di aplikasi Anda. Pelajari lebih lanjut tentang querybuilder di sini.
Lihatlah sampel dalam sampel untuk contoh penggunaan.
Ada beberapa repositori yang dapat Anda klon dan mulai dengan:
Ada beberapa ekstensi yang menyederhanakan bekerja dengan Typeorm dan mengintegrasikannya dengan modul lain:
data-source.ts
setelah menghasilkan migrasi/entitas-Typeorm-CodeBase-Syncrelations
Mudah - Typeorm -Relationsrelations
berdasarkan kueri GraphQL-Typeorm-Relations-Graphql Pelajari tentang kontribusi di sini dan cara mengatur lingkungan pengembangan Anda di sini.
Proyek ini ada berkat semua orang yang berkontribusi:
Sumber terbuka sulit dan memakan waktu. Jika Anda ingin berinvestasi di masa depan Typeorm, Anda dapat menjadi sponsor dan memungkinkan tim inti kami untuk menghabiskan lebih banyak waktu untuk perbaikan Typeorm dan fitur baru. Menjadi sponsor
Menjadi sponsor emas dan dapatkan dukungan teknis premium dari kontributor inti kami. Menjadi Sponsor Emas