2025SELab2-project-Dwengo/backend/src/sqlite-autoincrement-workaround.ts
2025-04-18 23:36:22 +00:00

41 lines
2.3 KiB
TypeScript

import { EntityProperty, EventArgs, EventSubscriber } from '@mikro-orm/core';
/**
* The tests are ran on an in-memory SQLite database. However, SQLite does not allow fields which are part of composite
* primary keys to be autoincremented (while PostgreSQL, which we use in production, does). This Subscriber works around
* the issue by remembering the highest values for every autoincremented part of a primary key and assigning them when
* creating a new entity.
*
* However, it is important to note the following limitations:
* - this class can only be used for in-memory SQLite databases since the information on what the highest sequence
* number for each of the properties is, is only saved transiently.
* - automatically setting the generated "autoincremented" value for properties only works when the entity is created
* via an entityManager.create(...) or repo.create(...) method. Otherwise, onInit will not be called and therefore,
* the sequence number will not be filled in.
*/
export class SqliteAutoincrementSubscriber implements EventSubscriber {
private sequenceNumbersForEntityType = new Map<string, number>();
/**
* When an entity with an autoincremented property which is part of the composite private key is created,
* automatically fill this property so we won't face not-null-constraint exceptions when persisting it.
*/
onInit<T extends object>(args: EventArgs<T>): void {
if (!args.meta.compositePK) {
return; // If there is not a composite primary key, autoincrement works fine with SQLite anyway.
}
for (const prop of Object.values(args.meta.properties)) {
const property = prop as EntityProperty<T>;
if (property.primary && property.autoincrement && (args.entity as Record<string, unknown>)[property.name] === undefined) {
// Obtain and increment sequence number of this entity.
const propertyKey = args.meta.class.name + '.' + property.name;
const nextSeqNumber = this.sequenceNumbersForEntityType.get(propertyKey) || 0;
this.sequenceNumbersForEntityType.set(propertyKey, nextSeqNumber + 1);
// Set the property accordingly.
(args.entity as Record<string, unknown>)[property.name] = nextSeqNumber + 1;
}
}
}
}