artmapper
Version:
Spring Boot clone for Node.js with TypeScript/JavaScript - JPA-like ORM, Lombok decorators, dependency injection, and MySQL support
155 lines • 8.16 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RelationshipManager = void 0;
require("reflect-metadata");
const entity_1 = require("../decorators/entity");
const mapping_1 = require("../decorators/mapping");
class RelationshipManager {
constructor(pool) {
this.pool = pool;
}
/**
* Load a ManyToOne relationship
*/
async loadManyToOne(entity, propertyKey, relationMetadata, targetEntityClass) {
const joinColumnMeta = Reflect.getMetadata(mapping_1.JOIN_COLUMN_METADATA_KEY, entity.constructor) || {};
const joinColumn = joinColumnMeta[propertyKey];
let foreignKeyColumn;
let foreignKeyValue;
if (joinColumn) {
foreignKeyColumn = joinColumn.name;
foreignKeyValue = entity[foreignKeyColumn] || entity[propertyKey];
}
else if (relationMetadata.joinColumn) {
foreignKeyColumn = typeof relationMetadata.joinColumn === 'string'
? relationMetadata.joinColumn
: relationMetadata.joinColumn.name || `${propertyKey}_id`;
foreignKeyValue = entity[foreignKeyColumn] || entity[propertyKey];
}
else {
foreignKeyColumn = `${propertyKey}_id`;
foreignKeyValue = entity[foreignKeyColumn] || entity[propertyKey];
}
if (!foreignKeyValue) {
return null;
}
const targetTableMeta = Reflect.getMetadata(entity_1.TABLE_METADATA_KEY, targetEntityClass);
const targetTable = targetTableMeta?.name || targetEntityClass.name.toLowerCase();
const targetIdMeta = Reflect.getMetadata(entity_1.ID_METADATA_KEY, targetEntityClass);
const targetColumnMeta = Reflect.getMetadata(entity_1.COLUMN_METADATA_KEY, targetEntityClass) || {};
const targetIdColumn = targetIdMeta ? (targetColumnMeta[targetIdMeta]?.name || targetIdMeta) : 'id';
const referencedColumn = joinColumn?.referencedColumnName || 'id';
const sql = `SELECT * FROM \`${targetTable}\` WHERE \`${referencedColumn}\` = ? LIMIT 1`;
const [rows] = await this.pool.execute(sql, [foreignKeyValue]);
if (rows.length === 0) {
return null;
}
return this.mapRowToEntity(targetEntityClass, rows[0], targetColumnMeta);
}
/**
* Load a OneToMany relationship
*/
async loadOneToMany(entity, propertyKey, relationMetadata, targetEntityClass) {
const mappedBy = relationMetadata.mappedBy || propertyKey;
const targetTableMeta = Reflect.getMetadata(entity_1.TABLE_METADATA_KEY, targetEntityClass);
const targetTable = targetTableMeta?.name || targetEntityClass.name.toLowerCase();
const targetColumnMeta = Reflect.getMetadata(entity_1.COLUMN_METADATA_KEY, targetEntityClass) || {};
// Get the ID of the current entity
const sourceIdMeta = Reflect.getMetadata(entity_1.ID_METADATA_KEY, entity.constructor);
const sourceColumnMeta = Reflect.getMetadata(entity_1.COLUMN_METADATA_KEY, entity.constructor) || {};
const sourceIdColumn = sourceIdMeta ? (sourceColumnMeta[sourceIdMeta]?.name || sourceIdMeta) : 'id';
const sourceIdValue = entity[sourceIdMeta] || entity.id;
if (!sourceIdValue) {
return [];
}
// Find the foreign key column in the target entity
const foreignKeyColumn = targetColumnMeta[mappedBy]?.name || `${entity.constructor.name.toLowerCase()}_id` || mappedBy;
const sql = `SELECT * FROM \`${targetTable}\` WHERE \`${foreignKeyColumn}\` = ?`;
const [rows] = await this.pool.execute(sql, [sourceIdValue]);
return rows.map(row => this.mapRowToEntity(targetEntityClass, row, targetColumnMeta));
}
/**
* Load a ManyToMany relationship
*/
async loadManyToMany(entity, propertyKey, relationMetadata, targetEntityClass) {
const joinTableMeta = Reflect.getMetadata(mapping_1.JOIN_TABLE_METADATA_KEY, entity.constructor) || {};
const joinTable = joinTableMeta[propertyKey];
let joinTableName;
let joinColumn;
let inverseJoinColumn;
if (joinTable) {
joinTableName = joinTable.name || `${entity.constructor.name.toLowerCase()}_${propertyKey}`;
joinColumn = joinTable.joinColumns?.[0]?.name || `${entity.constructor.name.toLowerCase()}_id`;
inverseJoinColumn = joinTable.inverseJoinColumns?.[0]?.name || `${targetEntityClass.name.toLowerCase()}_id`;
}
else if (relationMetadata.joinTable) {
if (typeof relationMetadata.joinTable === 'string') {
joinTableName = relationMetadata.joinTable;
joinColumn = `${entity.constructor.name.toLowerCase()}_id`;
inverseJoinColumn = `${targetEntityClass.name.toLowerCase()}_id`;
}
else {
joinTableName = relationMetadata.joinTable.name || `${entity.constructor.name.toLowerCase()}_${propertyKey}`;
joinColumn = relationMetadata.joinTable.joinColumns?.[0]?.name || `${entity.constructor.name.toLowerCase()}_id`;
inverseJoinColumn = relationMetadata.joinTable.inverseJoinColumns?.[0]?.name || `${targetEntityClass.name.toLowerCase()}_id`;
}
}
else {
joinTableName = `${entity.constructor.name.toLowerCase()}_${propertyKey}`;
joinColumn = `${entity.constructor.name.toLowerCase()}_id`;
inverseJoinColumn = `${targetEntityClass.name.toLowerCase()}_id`;
}
// Get the ID of the current entity
const sourceIdMeta = Reflect.getMetadata(entity_1.ID_METADATA_KEY, entity.constructor);
const sourceIdValue = entity[sourceIdMeta] || entity.id;
if (!sourceIdValue) {
return [];
}
const targetTableMeta = Reflect.getMetadata(entity_1.TABLE_METADATA_KEY, targetEntityClass);
const targetTable = targetTableMeta?.name || targetEntityClass.name.toLowerCase();
const targetColumnMeta = Reflect.getMetadata(entity_1.COLUMN_METADATA_KEY, targetEntityClass) || {};
const targetIdMeta = Reflect.getMetadata(entity_1.ID_METADATA_KEY, targetEntityClass);
const targetIdColumn = targetIdMeta ? (targetColumnMeta[targetIdMeta]?.name || targetIdMeta) : 'id';
const sql = `
SELECT t.* FROM \`${targetTable}\` t
INNER JOIN \`${joinTableName}\` jt ON t.\`${targetIdColumn}\` = jt.\`${inverseJoinColumn}\`
WHERE jt.\`${joinColumn}\` = ?
`;
const [rows] = await this.pool.execute(sql, [sourceIdValue]);
return rows.map(row => this.mapRowToEntity(targetEntityClass, row, targetColumnMeta));
}
/**
* Load a OneToOne relationship
*/
async loadOneToOne(entity, propertyKey, relationMetadata, targetEntityClass) {
// OneToOne can be bidirectional, check if mappedBy is set
if (relationMetadata.mappedBy) {
// This is the inverse side, load from the owning side
return this.loadOneToMany(entity, propertyKey, relationMetadata, targetEntityClass).then(results => results.length > 0 ? results[0] : null);
}
else {
// This is the owning side, use join column
return this.loadManyToOne(entity, propertyKey, relationMetadata, targetEntityClass);
}
}
/**
* Map database row to entity instance
*/
mapRowToEntity(entityClass, row, columnMetadata) {
const entity = new entityClass();
const reverseColumnMap = {};
// Create reverse mapping: column name -> property name
Object.keys(columnMetadata).forEach(prop => {
const colName = columnMetadata[prop].name || prop;
reverseColumnMap[colName] = prop;
});
// Map row data to entity properties
Object.keys(row).forEach(colName => {
const propName = reverseColumnMap[colName] || colName;
entity[propName] = row[colName];
});
return entity;
}
}
exports.RelationshipManager = RelationshipManager;
//# sourceMappingURL=RelationshipManager.js.map