using Invercargill.DataStructures; using Invercargill.Mapping; namespace InvercargillSql.Orm { /** * EntityMapper wraps PropertyMapper and adds ORM-specific metadata. * * This class is the core of the ORM system, providing bidirectional mapping * between entity objects and database records. It maintains column definitions * and schema metadata needed for SQL generation. * * Schema metadata (primary key, auto-increment, etc.) is discovered through * database introspection when using register_with_schema(). * * Example usage: * {{{ * var mapper = EntityMapper.build_for(b => b * .table("users") * .column("id", u => u.id, (u, v) => u.id = v) * .column("name", u => u.name, (u, v) => u.name = v)); * }}} */ public class EntityMapper : Object { /** * The database table name for this entity. */ public string table_name { get; internal set; } /** * The underlying PropertyMapper used for entity materialization. */ public PropertyMapper property_mapper { get; internal set; } /** * Column definitions for this entity. */ public Vector columns { get; internal set; } /** * The introspected table schema, or null if not using schema introspection. */ public TableSchema? table_schema { get; internal set; } /** * The name of the primary key column, or null if not defined. * * This is set either through schema introspection or defaults to "id". */ public string? primary_key_column { get; internal set; } /** * Creates an EntityMapper using a builder function. * * This is the recommended way to create EntityMapper instances. * * @param func A builder function that configures the mapper * @return A fully configured EntityMapper instance * * Example: * {{{ * var mapper = EntityMapper.build_for(b => b * .table("users") * .column("id", u => u.id, (u, v) => u.id = v) * .column("name", u => u.name, (u, v) => u.name = v)); * }}} */ public static EntityMapper build_for(owned GLib.Func> func) { var builder = new EntityMapperBuilder(); func(builder); return builder.build(); } /** * Materializes an entity from a Properties collection. * * This method is typically called with the results of a database query, * where column names map to property names. * * @param properties The property values from a database row * @return A new entity instance populated with the property values * @throws Error if materialization fails */ public T materialise(Invercargill.Properties properties) throws Error { return property_mapper.materialise(properties); } /** * Maps an entity to a Properties collection. * * This method is used when preparing entity data for database insertion * or update operations. * * @param entity The entity to map * @return A Properties collection containing the entity's values * @throws Error if mapping fails */ public Invercargill.Properties map_from(T entity) throws Error { return property_mapper.map_from(entity); } /** * Checks if a column is auto-incrementing. * * This method uses schema introspection data if available. * * @param column_name The column name to check * @return true if the column is auto-incrementing */ public bool is_auto_increment(string column_name) { if (table_schema != null) { var col = table_schema.get_column(column_name); return col != null && col.auto_increment; } return false; } /** * Gets the effective primary key column name. * * Returns the primary key from schema introspection if available, * otherwise returns the explicitly set primary_key_column, * otherwise defaults to "id". * * @return The primary key column name */ public string get_effective_primary_key() { if (table_schema != null && table_schema.primary_key_column != null) { return table_schema.primary_key_column; } return primary_key_column ?? "id"; } /** * Sets a property value on an existing entity. * * This method is used to back-populate generated values (like auto-increment * primary keys) after an insert operation. * * @param entity The entity to modify * @param column_name The column/property name to set * @param value The value to set (will be converted to appropriate type) * @throws Error if the property cannot be set */ public void set_property_value(T entity, string column_name, int64 value) throws Error { // Create a Properties collection with just this value var props = new PropertyDictionary(); props.set_native(column_name, value); // Use the PropertyMapper's map_into to set the value on the existing entity property_mapper.map_into(entity, props); } } }