entity-mapper-builder.vala 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. using Invercargill.DataStructures;
  2. using Invercargill.Mapping;
  3. namespace InvercargillSql.Orm {
  4. /**
  5. * Fluent builder for creating EntityMapper instances.
  6. *
  7. * This builder provides a type-safe, fluent API for configuring entity mappings.
  8. * It wraps a PropertyMapperBuilder<T> and adds ORM-specific metadata like
  9. * column definitions.
  10. *
  11. * Schema metadata (primary key, auto-increment, etc.) is discovered through
  12. * database introspection rather than being configured explicitly.
  13. *
  14. * Example usage:
  15. * {{{
  16. * var mapper = EntityMapper.build_for<User>(b => b
  17. * .table("users")
  18. * .column<int64>("id", u => u.id, (u, v) => u.id = v)
  19. * .column<string>("name", u => u.name, (u, v) => u.name = v)
  20. * .column<string>("email", u => u.email, (u, v) => u.email = v));
  21. * }}}
  22. */
  23. public class EntityMapperBuilder<T> : Object {
  24. private string? _table_name;
  25. private Vector<ColumnDefinition> _columns;
  26. private PropertyMapperBuilder<T> _mapper_builder;
  27. /**
  28. * Creates a new EntityMapperBuilder instance.
  29. */
  30. public EntityMapperBuilder() {
  31. _columns = new Vector<ColumnDefinition>();
  32. _mapper_builder = new PropertyMapperBuilder<T>();
  33. }
  34. /**
  35. * Sets the database table name for this entity.
  36. *
  37. * If not called, the entity type name will be used as the table name.
  38. *
  39. * @param name The table name in the database
  40. * @return This builder for method chaining
  41. */
  42. public EntityMapperBuilder<T> table(string name) {
  43. _table_name = name;
  44. return this;
  45. }
  46. /**
  47. * Adds a column mapping for a property.
  48. *
  49. * This method configures both the ORM column definition and the underlying
  50. * PropertyMapper mapping. It returns this EntityMapperBuilder for natural
  51. * method chaining.
  52. *
  53. * @param name The column name in the database
  54. * @param getter A function to get the property value from an entity
  55. * @param setter A function to set the property value on an entity
  56. * @return This builder for method chaining
  57. *
  58. * Example:
  59. * {{{
  60. * .column<int64>("id", u => u.id, (u, v) => u.id = v)
  61. * .column<string>("name", u => u.name, (u, v) => u.name = v)
  62. * }}}
  63. */
  64. public EntityMapperBuilder<T> column<TProp>(string name, owned PropertyGetter<T, TProp> getter, owned PropertySetter<T, TProp> setter) {
  65. var col_def = new ColumnDefinition() {
  66. name = name,
  67. column_type = ColumnType.from_gtype(typeof(TProp)) ?? ColumnType.TEXT
  68. };
  69. _columns.add(col_def);
  70. // Add to underlying PropertyMapper - transfer ownership of delegates
  71. var mapping_builder = _mapper_builder.map<TProp>(name, (owned)getter, (owned)setter);
  72. // Configure when_null handler to skip setting (leave property as default)
  73. // This allows the PropertyMapper to handle null values from the database gracefully
  74. mapping_builder.when_null((o) => { /* skip - leave property as default */ });
  75. // Configure when_undefined handler to skip setting (leave property as default)
  76. // This allows the PropertyMapper to handle partial property sets (e.g., back-populating only the PK)
  77. mapping_builder.when_undefined((o) => { /* skip - leave property as default */ });
  78. return this;
  79. }
  80. /**
  81. * Builds and returns the configured EntityMapper.
  82. *
  83. * @return A new EntityMapper instance with all configured mappings
  84. */
  85. public EntityMapper<T> build() {
  86. var mapper = new EntityMapper<T>();
  87. mapper.table_name = _table_name ?? typeof(T).name();
  88. mapper.property_mapper = _mapper_builder.build();
  89. mapper.columns = _columns;
  90. return mapper;
  91. }
  92. /**
  93. * Builds the EntityMapper with schema metadata from introspection.
  94. *
  95. * This internal method is used by OrmSession.register_with_schema<T>()
  96. * to apply discovered schema metadata to the mapper.
  97. *
  98. * @param schema The introspected table schema
  99. * @return A new EntityMapper instance with schema metadata applied
  100. */
  101. public EntityMapper<T> build_with_schema(TableSchema schema) {
  102. var mapper = build();
  103. mapper.table_schema = schema;
  104. mapper.primary_key_column = schema.primary_key_column;
  105. return mapper;
  106. }
  107. public string peek_table() {
  108. return _table_name;
  109. }
  110. }
  111. }