-
-
Notifications
You must be signed in to change notification settings - Fork 34
Description
Describe the bug
TLDR
- Given we are using Entity Schema definitions
- And we have a
Userentity class with its schema - And we have an
Employeeentity class that shares some fields with theUser(and resides in the same table) - And we have an
EmployeeDetailclass and entity with a One-To-One reference toEmployee - And we persist a new user
- When we try to create and persist a new
EmployeeDetailentity, passing theemployeeas an attribute - Then we got an error:
insert into "user" ("id") values (default) returning "id" - null value in column "email" violates not-null constraint
Consider the following entity relationships:
---
title: Read-Only association example
---
erDiagram
company ||--o{ user : "employs"
user ||--o{ address : "resides_at"
user ||--o{ employee_detail : "has"
company {
int id
string name
}
user {
int id
string email
string first_name
string last_name
int company_id
date created_at
date updated_at
}
address {
int id
int user_id
string street
}
employee_detail {
int id
int user_id
float salary
}
Also, consider that we have an Employee entity that is not described in a table above. That happens because an Employee is a "sub-type" of User, with the association with a company:
export class Employee {
public readonly id: number;
public readonly email: string;
public readonly company: Reference<Company>;
constructor(props) {
Object.assign(this, props);
}
}EmployeeDetail in the database, pointing to a reference of Employee, Mikro-ORM throws an error: insert into "user" ("id") values (default) returning "id" - null value in column "email" violates not-null constraint
Stack trace
src/mikro-orm/mikro-orm-internal.module.spec.ts > MikroOrmInternalModule > creates a new employee detail
NotNullConstraintViolationException: insert into "user" ("id") values (default) returning "id" - null value in column "email" violates not-null constraint
❯ PostgreSqlExceptionConverter.convertException node_modules/@mikro-orm/postgresql/PostgreSqlExceptionConverter.js:24:24
❯ PostgreSqlDriver.convertException node_modules/@mikro-orm/core/drivers/DatabaseDriver.js:197:54
❯ node_modules/@mikro-orm/core/drivers/DatabaseDriver.js:201:24
❯ PostgreSqlDriver.nativeInsertMany node_modules/@mikro-orm/knex/AbstractSqlDriver.js:303:21
❯ ChangeSetPersister.persistNewEntity node_modules/@mikro-orm/core/unit-of-work/ChangeSetPersister.js:85:21
❯ ChangeSetPersister.executeInserts node_modules/@mikro-orm/core/unit-of-work/ChangeSetPersister.js:29:13
❯ ChangeSetPersister.runForEachSchema node_modules/@mikro-orm/core/unit-of-work/ChangeSetPersister.js:68:13
❯ UnitOfWork.commitCreateChangeSets node_modules/@mikro-orm/core/unit-of-work/UnitOfWork.js:739:9
❯ UnitOfWork.persistToDatabase node_modules/@mikro-orm/core/unit-of-work/UnitOfWork.js:703:13
❯ Parser.parseErrorMessage node_modules/pg-protocol/src/parser.ts:369:69
❯ Parser.handlePacket node_modules/pg-protocol/src/parser.ts:188:21
❯ Parser.parse node_modules/pg-protocol/src/parser.ts:103:30
❯ Socket.<anonymous> node_modules/pg-protocol/src/index.ts:7:48
❯ Socket.emit node:events:513:28
To Reproduce
Steps to reproduce the behavior:
- Clone the minimum reproducible code repository: https://github.com/thiagomini/nest-mikro-orm-example/tree/bug/read-only-association
- run
yarn - run
docker compose up -d - run
yarn test
Expected behavior
A new employee_detail record should be created, given that we have the existing user in the database already.
Additional context
You can look at the schemas in the repo linked, but here's both the employee and employee_detail schemas:
Employee Schema
import { BigIntType, EntitySchema } from '@mikro-orm/core';
import { Company } from './company.entity';
import { Employee } from './employee.entity';
import { EmployeeDetail } from './employee-detail.entity';
export const employeeSchema = new EntitySchema<Employee>({
class: Employee,
tableName: 'user',
forceConstructor: true,
properties: {
id: {
type: BigIntType,
primary: true,
autoincrement: true,
},
email: {
type: String,
persist: false
},
company: {
entity: () => Company,
reference: 'm:1',
ref: true,
persist: false
},
detail: {
entity: () => EmployeeDetail,
reference: '1:1',
mappedBy: 'employee',
}
},
});EmployeeDetail Schema:
import { BigIntType, EntitySchema } from '@mikro-orm/core';
import { Company } from './company.entity';
import { Employee } from './employee.entity';
import { EmployeeDetail } from './employee-detail.entity';
export const employeeDetailSchema = new EntitySchema<EmployeeDetail>({
class: EmployeeDetail,
tableName: 'employee_detail',
forceConstructor: true,
properties: {
id: {
type: BigIntType,
primary: true,
autoincrement: true,
},
employee: {
entity: () => Employee,
reference: '1:1',
ref: true,
inversedBy: 'detail'
},
salary: {
type: Number
}
},
});Versions
| Dependency | Version |
|---|---|
| node | 18.12.1 |
| typescript | 5.1.3 |
| mikro-orm | next |
| pg | 8.11.2 |