TypeORM Entity Inheritance ManyToOne Relationship - node.js

I'm using TypeORM in Node.JS and would like to use the entity inheritance to implement a BaseRecord:
export abstract class BaseRecord {
#CreateDateColumn({type: 'timestamp'})
public created_at: Date;
#UpdateDateColumn({type: 'timestamp'})
public updated_at: Date;
#ManyToOne(type => User, user => user.records_created)
public created_by: User
#ManyToOne(type => User, user => user.records_updated)
public updated_by: User
}
Which I would like to extend other entities by. This works as expected when removing the #ManyToOne relationship:
#Entity()
export class Address extends BaseRecord {
#PrimaryGeneratedColumn()
public id: number;
#Column({ nullable: true, type: "text" })
public alias: string;
#Column({ type: "text" })
public street_1: string;
#Column({ nullable: true, type: "text" })
public street_2: string;
#Column({ type: "text" })
public city: string;
#Column({ type: "text" })
public state: string;
#Column({ type: "text" })
public zip_code: string;
#Column(type => GeoLocation)
public geo_location: GeoLocation
}
Has anyone run into this or a method to inherit entity and have ManyToOne relationships?

I suggest using composition over inheritance with an Embedded Entity
An embedded column is a column which accepts a class with its own columns and merges those columns into the current entity's database table.
You can use as many columns (or relations) in embedded classes as you need. You even can have nested embedded columns inside embedded classes.
import {Column} from "typeorm";
export class Assigned {
#ManyToOne(type => User, user => user.records_created)
public created_by: User
#ManyToOne(type => User, user => user.records_updated)
public updated_by: User
}
export class Dated {
#CreateDateColumn({type: 'timestamp'})
public created_at: Date;
#UpdateDateColumn({type: 'timestamp'})
public updated_at: Date;
}
then use it
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
import {Assigned} from "./Assigned";
import {Dated} from "./Dated";
#Entity()
export class Address extends BaseRecord {
// ...Other columns
#Column(type => Assigned)
assigned: Assigned;
#Column(type => Dated)
dated: Dated;
}
You can use as many columns (or relations) in embedded classes as you need.
You even can have nested embedded columns inside embedded classes.

Related

Is there a way to get all Entity records through a custom pivot table

I am trying to create a tweet bookmarking feature in my project. Where a user can save tweets to view them later. I am able to hit an endpoint and save a bookmark table record given a userId and tweetId. I'm having trouble figuring out how to return all bookmarked tweets using typeorm. One user can have many bookmarks.
I have the three following entities in a mysql database
tweet.entity.ts
#Entity()
export class Tweet {
#PrimaryGeneratedColumn()
public id?: number;
#Column('text')
public text: string;
#ManyToOne(() => User, (user: User) => user.tweets)
public user: User;
#OneToMany(() => Comment, (comment: Comment) => comment.tweet)
public comments: Comment[];
}
user.entity.ts
#Entity()
class User {
#PrimaryGeneratedColumn()
public id?: number;
#Column({ unique: true })
public email: string;
#OneToMany(() => Tweet, (tweet: Tweet) => tweet.user)
public tweets: Tweet[];
}
bookmark.entity.ts
#Entity()
export class Bookmark {
#PrimaryGeneratedColumn()
public id?: number;
#Column()
public userId: number;
#Column()
public tweetId: number;
}
One solution using query builder:
const items = await dataSource
.createQueryBuilder(tweet, "tweet")
.innerJoin("bookmark", "bookmark", "bookmark.tweetId = tweet.id")
.where("bookmark.userId = :userId", { userId: userId })
.getMany();
You can also declare a many-to-many relation between Tweet and User with Bookmark as pivot table:
user.entity.ts
#ManyToMany(type => Tweet)
#JoinTable({
name: "bookmarks", // pivot table name
// Custom column name
// joinColumn: {
// name: "userId",
// referencedColumnName: "id"
// },
// inverseJoinColumn: {
// name: "tweetId",
// referencedColumnName: "id"
// }
})
bookmarks: Tweet[];
Usage:
userRepository.find({
relations: ["bookmarks"],
})
More info: https://github.com/typeorm/typeorm/blob/master/docs/relations.md#jointable-options

Finding All Child Entities using Query to Parent Entity in TypeORM

Consider a base entity as below:
export abstract class Notification {
#PrimaryGeneratedColumn()
id: number;
#Column({type: "date",nullable: false})
seenAt: Date;
#Column({ type: "integer", nullable: false })
priority: number;
}
and two child entities as below:
#Entity()
export class NotificationType1 extends Notification {}
and
#Entity()
export class NotificationType2 extends Notification {}
Is there a way to find all rows in NotificationType1 and NotificationType2 using a query to the parent class like this?
SELECT * FROM NOTIFICATION;
This query return 0 rows, although there are records in NotificationType1 and NotificationType2 tables.
You should be able to Select from the superclass and retrieve all the records with something like this:
import {getConnection} from "typeorm";
const user = await getConnection().createQueryBuilder()
.select("notification")
.from(Notification, "notification");
You also need to change your abstract class to #TableInheritance to leverage Single Table Inheritance.
This Code:
export abstract class Notification {
#PrimaryGeneratedColumn()
id: number;
#Column({type: "date",nullable: false})
seenAt: Date;
#Column({ type: "integer", nullable: false })
priority: number;
}
Would become:
#Entity()
#TableInheritance({ column: { type: "varchar", name: "type" } })
export class Notification {
#PrimaryGeneratedColumn()
id: number;
#Column({type: "date",nullable: false})
seenAt: Date;
#Column({ type: "integer", nullable: false })
priority: number;
}
And the Child Entity:
#ChildEntity()
export class NotificationType1 extends Notification {}
The docs have on single table inheritance.

TypeORM: Custom Many To Many Relationship

I'm working with Nest.js, TypeORM, and PostgreSQL, I have two entities(product and stone) with a many to many relation, based on my own business project, I have to add one extra column to that many to many table(product_stone), but I have some issue with my solution, at first I try to create a product with a set of stones:
"stones": [
{"id": 1,"count":1},
{"id": 2,"count": 3}
]
and after that, I try to add the count to the product_stone table by updating it, the result will be like this:
product_stone_table
till here everything is Okay, but every time that I restart the server all of the data in that extra column will be set to its default value(null):
product_stone_table
And also I tried to do not set the count to {nullable:true} in product_stone table and add count during the creation of a product, but when I want to restart the server I receive an error kile this:
QueryFailedError: column "count" of relation "product_stone" contains null values
Is there anybody to guide me?
product.entity.ts
#Entity()
export class Product extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#ManyToMany(() => Stone)
#JoinTable({
name: 'product_stone',
joinColumn: {
name: 'productId',
referencedColumnName: 'id',
},
inverseJoinColumn: {
name: 'stoneId',
referencedColumnName: 'id',
},
})
stones: Stone[];
}
stone.entity.ts
#Entity()
export class Stone extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
title: string;
}
product_stone.entity.ts
#Entity('product_stone')
export class ProductStone extends BaseEntity {
#Column({ nullable: true })
count: number;
#Column()
#IsNotEmpty()
#PrimaryColumn()
productId: number;
#Column()
#IsNotEmpty()
#PrimaryColumn()
stoneId: number;
}
I don't think you can define custom attributes on many-to-many table like that.
From documentation:
In case you need to have additional properties in your many-to-many relationship, you have to create a new entity yourself
In your case that would mean you would have to so something like that:
// product_stone.entity.ts
#Entity()
export class ProductToStone {
#PrimaryGeneratedColumn()
public id: number;
#Column()
public productId: number;
#Column()
public stoneId: number;
#Column()
public count: number;
#ManyToOne(() => Product, product => product.productToStone)
public product: Product;
#ManyToOne(() => Stone, stone => stone.productToStone)
public stone: Stone;
}
// product.entity.ts
...
#OneToMany(() => ProductToStone, productToStone => postToCategory.product)
public productToStone!: PostToCategory[];
// stone.entity.ts
...
#OneToMany(() => ProductToStone, postToCategory => postToCategory.stone)
public postToCategories!: PostToCategory[];

typeorm save in joined cloumn

in my project i have two entity, naturalist(user) and comment entity
i create relation between them, how to save reciever_id (joined cloumn) in comment entity
this is my comment entity
#Entity()
export class Comments extends BaseEntity {
#PrimaryGeneratedColumn()
id: string
#ManyToOne(
type => Naturalist,
naturalist => naturalist.comments
)
#JoinColumn({
name: 'naturalist_id',
})
naturalist: Naturalist
#Column({ nullable: true, default: '' })
text?: string
#Column({ nullable: true, default: '' })
sender_id?: string
}
naturalist(my user) entity
#Entity()
export class Naturalist extends BaseEntity {
#PrimaryGeneratedColumn()
id: number
#Column()
user_id: string
#OneToMany(
type => Comments,
comment => comment.naturalist
)
comments: Comments[]
}
DTO contains
{
receiver_id: '2cc2f359-821c-4940-99ae-7386576d861b',
text: 'string',
id: 'e0464049-d1d9-474a-af5b-815805aa1c4b'
}
i want to save receiver_id to my comment entity
if I understand correctly your needs,
I think you need an insert method to do this:
await entityManager.insert(Comments, { naturalist: { id: '2cc2f359-821c-4940-99ae-7386576d861b' } as Naturalist });

Nestjs - define OneToMany relation in entity

I am defining entity fields in my NestJS project. I have ManyToOne realtion that is defined successfully. I have trouble finding the right way to define OneToMany relation to fit the syntax I used for other relation.
import {Entity,Column, PrimaryColumn, ManyToOne,JoinColumn,OneToMany} from "typeorm";
import { Account } from "./account.entity";
import { Balance } from "./balance.entity";
import { BaseEntity } from "./base.entity";
#Entity()
export class MainEntity extends BaseEntity {
#PrimaryColumn({
type: "varchar",
name: "id",
unique: true
})
id: string;
#ManyToOne(() => Account, { nullable: true })
#JoinColumn({
name: "account_id",
referencedColumnName: "id"
})
account: Account;
#OneToMay relation needs to be connected to the Balance entity and mappedBy paymentDevice field in it.
My try:
#OneToMany(() => Balance, ...)
balances: Balance[]
I am in NestJs and typescript so this is challenging for me.
Generally, TypeORM relations used in NestJS are simple and dev-friendly. The code which you've written defines parameters which are already predefined.
For example,
#PrimaryColumn({
type: "varchar",
name: "id",
unique: true
})
This exact parameters is what defined by
#PrimaryColumn() // unique: true
id: string //varchar, name: id
So you can have the code just like below. For Account Entity,
#Entity()
export class Account {
#PrimaryColumn()
id: string;
#ManyToOne(type => Balance, balance => balance.id)
balance: Balance;
}
For Balance Entity
#Entity()
export class Balance {
#PrimaryColumn()
id: string;
#OneToMany(type => Account, account => account.id)
#JoinColumn({name: "account_id"})
// defining this is also optional because by default,
// the referenced foreign key is named as <column_name>_id or account_id
account: Account;
}
This will create a many-to-one relationship on Account and OneToMany on Balance entity. For more such examples, Refer: https://orkhan.gitbook.io/typeorm/docs/many-to-one-one-to-many-relations

Resources