I have a Project
entity with a non-autogenerated id field and a successor field. This successor is the project that follows next. But maybe there is no following project so this might be null.
@Entity()
export class Project extends BaseEntity {
@PrimaryColumn({ unique: true })
public id: string;
@OneToMany(() => Project, project => project.id, { nullable: true })
public successorId?: string;
}
When creating a new project via
public createProject(id: string, successorId?: string): Promise<Project> {
const project: Project = new Project();
project.id = id;
project.successorId = successorId;
return project.save();
}
there are multiple cases I have to take care for.
Passing in an id that already exists:
This will not throw an error. It just overrides the existing entity.
Passing in undefined
for the successorId
:
The code works fine then but it does not create a successorId
column with null
then. The column simply does not exist in the database.
Passing in the same id for id
and successorId
(this should be possible):
TypeORM throws the error
TypeError: Cannot read property 'joinColumns' of undefined
Passing in a successorId
of another existing project:
I'm getting the same error as above
Passing in a successorId
of a project that doesn't exist:
I'm getting the same error as above
So how can I fix that? I think my entity design seems to be wrong. Basically it should be
Would be awesome if someone could help!
Update
I also tried this
@OneToMany(() => Project, project => project.successorId, { nullable: true })
@Column()
public successorId?: string;
but whenever I want to call the createProject
method I'm getting this error
QueryFailedError: null value in column "successorId" violates not-null constraint
and this
@OneToMany(() => Project, project => project.successorId, { nullable: true })
public successorId?: string;
but then I'm getting this error
TypeError: relatedEntities.forEach is not a function
Please try this solution
@Entity()
export class Project extends BaseEntity {
@PrimaryColumn({ unique: true })
public id: string
@Column({ nullable: true })
public successorId?: string
@ManyToOne(() => Project, project => project.id)
@JoinColumn({ name: "successorId" })
public successor?: Project
}
public successor?: Project
- property is used for building relation between entities (same entity in this case). Related entity must be specified as a property type, because TypeORM uses this type to determine target entity and build relation metadata. You can read more about TypeORM relations here
public successorId?: string
- property is just an "extracted" join column. When you use ManyToOne
relation, TypeORM automatically creates a column in the database named propertyName
+ referencedColumnName
(successorId
in this case). But you cannot use this column in your code, because it is defined only in table and not in your class. And if you need this column defined in class (for further saving or displaying) you can create a propery and mark it with a @Column
decorator. Property name must be the same as the join column name in the table. Described in more detail here
Creating an entity with the same id just overrides the existing one
this is an expected behaviour. When you trying to save entity with an existing Id, TypeORM recognizes this as an update, not a create
I tested your solution and I think this seems to work. Would you mind explaining why two fields are required? And it knows how to map that field to a variable via string?
The only case still failing was the first part. Creating an entity with the same id just overrides the existing one ..
yes, I was able to reproduce it with this sample repo :) github.com/mhcomp/…
yep, in TypeORM
@OneToMany
is an inverse side of@ManyToOne
and cannot exist without@ManyToOne
. Also@OneToMany
is not required and can be ommited in this case. For example if you need "projects which follow before successor" or in other words "projects in which this project is specified as successor" then you need to useOneToMany
as inverse side. It will look like@OneToMany(() => Project, project => project.successor) public projects: Project[]
btw, I don't know your requirements about successor. If a one successor can be specified in many projects, then
@ManyToOne
is right approach. Otherwise if one succesor can follow only one project, then you should use@OneToOne
relation instead.