TypeORM OneToOne Relation Joined by Primary Key – Typeorm

Photo of author
Written By M Ibrahim
node.js one-to-one typeorm

Quick Fix: To establish a one-to-one relationship in TypeORM where the primary key is used to join the entities, set the primary property of the OneToOne annotation to true, as seen in the provided example. This will ensure the relationship is accurately mapped through the primary key.

The Problem:

You’re creating an application using TypeORM and you have the following database schema:

Users:
  id (primary key)
  name

Clients:
  id (primary key, foreign key to Users.id)
  client_specific_column

Sellers:
  id (primary key, foreign key to Users.id)
  seller_specific_column

You need to map this schema in TypeORM, and you want to establish a one-to-one relationship between Users, Clients, and Sellers, where the primary keys of these tables are the same. This means that a user can be either a client or a seller, but not both. So, the id column of both Clients and Sellers is also the primary key of the Users table.

The Solutions:

\n

Solution 1: TypeORM OneToOne Relation Joined by Primary Key

\n

TypeORM offers a convenient way to establish a OneToOne relationship between entities, where the foreign key of the related entities is the primary key of the parent entity. This can be achieved using the primary property of the @OneToOne decorator.

For instance, consider a scenario with three tables: Users, Clients, and Sellers. The Users table contains common information for both Clients and Sellers. To map a OneToOne relationship between Users and Clients (or Sellers), where the primary key of the Clients (or Sellers) table is the same as the primary key of the corresponding User, you can utilize the primary property as follows:

Example:

// In the Seller or Client class:
@OneToOne(() => User, { primary: true, cascade: true })
@JoinColumn({ name: 'USER_ID' })
user: User;

In this example, the primary property is set to true, indicating that the relationship will be established using the primary key. The cascade property ensures that changes made to the parent entity (User) are propagated to the related entity (Client or Seller). Additionally, the @JoinColumn decorator specifies the column name (‘USER_ID’ in this case) that represents the foreign key in the related entity.

By utilizing the primary property, you can effortlessly create a OneToOne relationship between entities, where the foreign key is the primary key of the parent entity. This approach provides a straightforward way to establish interconnected entities in your database.

Solution 2: TypeOrm OneToOne Relation Joined By Primary Key

In TypeOrm, it is not possible to directly map a OneToOne relation where the FK of Clients and Sellers is the own primary key, like @MapsId in JPA Hibernate. However, you can achieve a similar result by following these steps:

  1. Use @PrimaryColumn instead of @PrimaryGeneratedColumn: On the related tables (sellers & clients), use @PrimaryColumn instead of @PrimaryGeneratedColumn. This will allow you to specify the primary key explicitly.
  2. Create a @OneToOne Relation with @JoinColumn: After @PrimaryColumn, add a @OneToOne relation to the main table (users in your case). In the @JoinColumn, specify the column name to be the same as the @PrimaryColumn name. This will create a single column for both the Primary Key and the Foreign Key relation.
  3. Use @BeforeInsert to Copy the ID: Add @BeforeInsert in each of the related tables to copy the id from the main table (users) to the primary/foreign key of the other table (sellers & clients). This will ensure that the related tables have the same id as the main table.
  4. Use { "cascade": true } on @OneToOne: Add { "cascade": true } on the @OneToOne relation. This will allow you to save the related table automatically when you save the main table within the same transaction. The @BeforeInsert will then copy the new key to the related table before it is saved.

Here is an example of how to implement this in TypeOrm:

<pre><code>
import { Entity, PrimaryGeneratedColumn, Column,
OneToOne, JoinColumn, PrimaryColumn, BeforeInsert } from "typeorm";

@Entity()
export class users {
@PrimaryGeneratedColumn()
id: number;

@Column()
username: string;

}

@Entity()
export class clients {
@PrimaryColumn()
clientid: number;
@OneToOne(() => users, { "cascade": true })
@JoinColumn({ name: "clientid" } ) // This matches @PrimaryColumn name
user: users;

@BeforeInsert()
newid() { this.clientid = this.user.id; }

@Column()
clientname: string;

}

@Entity()
export class sellers {
@PrimaryColumn()
sellerid: number;
@OneToOne(() => users, { "cascade": true })
@JoinColumn({ name: "sellerid" } ) // This matches @PrimaryColumn name
user: users;

@BeforeInsert()
newid() { this.sellerid = this.user.id; }

@Column()
sellername: string;

}
</code></pre>

Main code to save User + Seller:

<pre><code>
const connection = await createConnection();
const entityManager = connection.createEntityManager();

const user = new users();
const seller = new sellers();
seller.user = user;

user.username = "Giordano User";
seller.sellername = ‘Giordano Seller’;

await entityManager.save(seller); // "user" saves automatically because of "cascade"
</code></pre>

By following these steps, you can create a OneToOne relation in TypeOrm where the FK of Clients and Sellers is the own primary key, similar to @MapsId in JPA Hibernate.