Not loaded nullable ManyToOne relation comes as null and thus gets removed on save
See original GitHub issueIssue Description
When loading an entity via SelectQueryBuilder
without joining a nullable ManyToOne
relation, the returned entity will have the not loaded relation set as null
.
Now saving again this entity results in removing any possibly attached related entity.
Expected Behavior
Loading an entity without loading a certain relation should always result in undefined
(and not null
) for this relation, as undefined
can be interpreted as “was not loaded” while null
should be “was loaded but is NULL
in the database”.
Thus saving the entity directly after loading it should be a NO-OP.
Actual Behavior
The not loaded relation is automatically assigned the value null
on entity retrieval. Thus saving the entity will remove the link between the entity and the relation. Just loading and saving an entity without actively manipulating it changed the state of the database.
Steps to Reproduce
game.entity.ts
with nullable ManyToOne
relation creator
import { Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { User } from './user.entity';
@Entity()
export class Game {
@PrimaryGeneratedColumn()
id!: number;
// some more relations and columns
@ManyToOne(() => User, user => user.createdGames, { nullable: true })
@JoinColumn()
creator!: User | null;
@Column()
finished: boolean;
}
user.entity.ts
import { Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
import { Game } from './game.entity';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id!: number;
@OneToMany(() => Game, game => game.creator)
createdGames!: Game[];
}
game.repository.ts
import { EntityRepository, Repository } from 'typeorm';
import { Game } from './game.entity';
@EntityRepository(Game)
export class GameRepository extends Repository<Game> {}
Retrieval of the entity (just in case it might matter, I added the WHERE
s and as comment some joins):
const game: Game | undefined = await this.gameRepository
.createQueryBuilder('g')
// several innerJoinAndSelect / leftJoinAndSelect here, but none for g.creator
.where('g.id = :gameId', { gameId })
.andWhere('g.finished = FALSE')
.getOne();
If the game with the given id gameId
has a creator set in the database, game.creator
will be null
. (I guess it will always be null
).
Thus running
await this.gameRepository.save(game);
will result in an update of the game in the database, setting the foreign key to its creator to NULL
.
My Environment
Dependency | Version |
---|---|
Operating System | Ubuntu 20.04.3 (inside Windows 10 20H2 WSL) |
Node.js version | 16.13.1 |
Typescript version | 4.4.4 |
TypeORM version | 0.2.38 |
TypeORM version | 0.2.38 |
@nestjs/typeorm version | 8.0.2 |
@nestjs/core and @nestjs/common | 8.1.1 |
npm ls typescript
also shows 4.3.5 as dependency of @nestjs/cli@8.1.4
Relevant Database Driver(s)
DB Type | Reproducible |
---|---|
aurora-data-api |
no |
aurora-data-api-pg |
no |
better-sqlite3 |
no |
cockroachdb |
no |
cordova |
no |
expo |
no |
mongodb |
no |
mysql |
yes |
nativescript |
no |
oracle |
no |
postgres |
no |
react-native |
no |
sap |
no |
sqlite |
no |
sqlite-abstract |
no |
sqljs |
no |
sqlserver |
no |
Are you willing to resolve this issue by submitting a Pull Request?
- ✖️ Yes, I have the time, and I know how to start.
- ✖️ Yes, I have the time, but I don’t know how to start. I would need guidance.
- ✖️ No, I don’t have the time, but I can support (using donations) development.
- ✅ No, I don’t have the time and I’m okay to wait for the community / maintainers to resolve this issue.
Issue Analytics
- State:
- Created 2 years ago
- Comments:9 (4 by maintainers)
@Isli-GD Hmm, I haven’t touched any project using typeorm for at least a year. I’ll see if I can get a dev environment up to take a look. At the time, PR #7147 contained all necessary tests to confirm it fixed issue #7146. But it certainly is possible this new issue is related.
@McP4nth3r not yet. Tests also passed when I tried them, so I think I need to further elaborate on my code and check which additional conditions are necessary to give a MWE that indeed reproduces the behavior I encountered. But I currently have no time, so this might take another month or two, I think.