UNPKG

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
"use strict"; 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