0
# Configuration
1
2
Database configuration, schema updates, and utility functions for entity management and persistence. HibernateTS provides comprehensive configuration options for database schema management, automatic change tracking, and entity lifecycle management.
3
4
## Capabilities
5
6
### Database Schema Updates
7
8
Automatic database schema synchronization based on entity model definitions.
9
10
```typescript { .api }
11
/**
12
* Updates database schema to match model definitions
13
* @param modelRootPath - Path to directory containing model files
14
* @param opts - Update options including database configuration
15
* @returns Promise that resolves when schema update is complete
16
*/
17
function updateDatabase(modelRootPath: string, opts?: UpdateOpts): Promise<void>;
18
19
interface UpdateOpts {
20
/** Database adapter class (constructor) for creating connections */
21
dbPoolGEnerator?: DataBaseBaseStatic;
22
/** Database name to update schema for */
23
modelDb?: string;
24
}
25
26
interface DataBaseBaseStatic {
27
new(...args: any[]): DataBaseBase;
28
}
29
```
30
31
**Usage Examples:**
32
33
```typescript
34
import { updateDatabase, MariaDbBase } from "hibernatets";
35
import path from "path";
36
37
// Basic schema update
38
await updateDatabase(path.join(__dirname, "models"));
39
40
// Schema update with custom database adapter
41
await updateDatabase(path.join(__dirname, "models"), {
42
dbPoolGEnerator: MariaDbBase
43
});
44
45
// Schema update with specific database name
46
await updateDatabase(path.join(__dirname, "models"), {
47
modelDb: "my_custom_database"
48
});
49
50
// Automated schema update in application startup
51
async function initializeApplication() {
52
try {
53
console.log("Updating database schema...");
54
await updateDatabase("./src/entities");
55
console.log("Database schema updated successfully");
56
} catch (error) {
57
console.error("Schema update failed:", error);
58
process.exit(1);
59
}
60
}
61
```
62
63
### Change Tracking and Interception
64
65
Automatic property change tracking and database synchronization.
66
67
```typescript { .api }
68
/**
69
* Intercepts property setters to automatically trigger database updates
70
* @param object - Entity instance to intercept
71
* @param opts - Interception configuration options
72
*/
73
function intercept<T>(object: T, opts?: InterceptParams): void;
74
75
interface InterceptParams {
76
/** Enable interception of array methods like push/filter */
77
interceptArrayFunctions?: boolean;
78
/** Database connection to use for updates */
79
db?: DataBaseBase;
80
}
81
```
82
83
**Usage Examples:**
84
85
```typescript
86
import { intercept, load, queries } from "hibernatets";
87
88
// Basic property interception
89
const user = await load(User, 1);
90
intercept(user);
91
92
// Changes are automatically tracked
93
user.name = "Updated Name";
94
user.email = "new@example.com";
95
96
// Execute all pending updates
97
await queries(user);
98
99
// Interception with array function tracking
100
const userWithPosts = await load(User, 1, [], { deep: true });
101
intercept(userWithPosts, {
102
interceptArrayFunctions: true
103
});
104
105
// Array operations are tracked
106
userWithPosts.posts.push(new Post());
107
userWithPosts.posts[0].title = "Updated Title";
108
109
// All changes are persisted
110
await queries(userWithPosts);
111
```
112
113
### Entity Utility Functions
114
115
Core utility functions for working with entities and their database representations.
116
117
```typescript { .api }
118
/**
119
* Gets the primary key value of an entity
120
* @param object - Entity instance
121
* @returns Primary key value
122
*/
123
function getId(object: any): number;
124
125
/**
126
* Sets the primary key value of an entity
127
* @param object - Entity instance
128
* @param id - New primary key value
129
*/
130
function setId(object: any, id: number): void;
131
132
/**
133
* Checks if an entity is persisted to database
134
* @param object - Entity instance
135
* @returns True if entity exists in database
136
*/
137
function isPersisted(object: any): boolean;
138
139
/**
140
* Gets database configuration for an entity or constructor
141
* @param obj - Entity instance or constructor
142
* @returns Database configuration object
143
*/
144
function getDBConfig<TableClass = any>(obj: ISaveAbleObject | ConstructorClass<TableClass> | TableClass): DataBaseConfig<TableClass>;
145
146
/**
147
* Gets database representation of an entity for storage
148
* @param object - Entity instance
149
* @returns Object with database column mappings
150
*/
151
function getRepresentation(object: any): { [key: string]: unknown };
152
```
153
154
**Usage Examples:**
155
156
```typescript
157
import {
158
getId, setId, isPersisted,
159
getDBConfig, getRepresentation,
160
load, save
161
} from "hibernatets";
162
163
// Working with entity IDs
164
const user = new User();
165
user.name = "Alice";
166
167
console.log(getId(user)); // undefined (not persisted yet)
168
console.log(isPersisted(user)); // false
169
170
await save(user);
171
console.log(getId(user)); // 123 (assigned after save)
172
console.log(isPersisted(user)); // true
173
174
// Manual ID assignment
175
const newUser = new User();
176
setId(newUser, 999);
177
newUser.name = "Bob";
178
179
// Get database configuration
180
const config = getDBConfig(User);
181
console.log("Table name:", config.table);
182
console.log("Primary key:", config.modelPrimary);
183
console.log("Columns:", Object.keys(config.columns));
184
185
// Get database representation
186
const dbRepresentation = getRepresentation(user);
187
console.log("DB data:", dbRepresentation);
188
// Output: { id: 123, name: "Alice", email: "alice@example.com", ... }
189
```
190
191
### Update Management
192
193
Advanced update tracking and batch processing for entity changes.
194
195
```typescript { .api }
196
/**
197
* Adds a pending database update to an entity's update queue
198
* @param obj - Entity instance
199
* @param update - Update operation to queue
200
*/
201
function pushUpdate(obj: any, update: any): void;
202
203
/**
204
* Updates a specific property of an entity in the database
205
* @param object - Entity instance
206
* @param key - Property key to update
207
* @param value - New value for the property
208
* @param opts - Update options
209
* @returns Promise resolving to number of affected rows
210
*/
211
function update(object: any, key: string, value: any, opts?: any): Promise<number>;
212
```
213
214
**Usage Examples:**
215
216
```typescript
217
import { pushUpdate, update, queries, load } from "hibernatets";
218
219
// Manual update queuing
220
const user = await load(User, 1);
221
222
// Queue multiple updates
223
pushUpdate(user, { field: "name", value: "New Name" });
224
pushUpdate(user, { field: "email", value: "new@email.com" });
225
226
// Execute all queued updates
227
await queries(user);
228
229
// Direct property update
230
const affectedRows = await update(user, "lastLogin", new Date());
231
console.log(`Updated ${affectedRows} rows`);
232
233
// Batch property updates
234
async function batchUpdateUser(userId: number, updates: Record<string, any>) {
235
const user = await load(User, userId);
236
237
for (const [key, value] of Object.entries(updates)) {
238
await update(user, key, value);
239
}
240
241
return user;
242
}
243
244
const updatedUser = await batchUpdateUser(123, {
245
name: "Alice Smith",
246
email: "alice.smith@example.com",
247
lastLogin: new Date()
248
});
249
```
250
251
### Database Configuration Class
252
253
Complete database configuration management for entities.
254
255
```typescript { .api }
256
/**
257
* Configuration class for entity database mapping
258
*/
259
interface DataBaseConfig<T> {
260
/** Primary key field name */
261
modelPrimary: string;
262
/** Database table name */
263
table: string;
264
/** Column definitions mapped by property name */
265
columns: { [key: string]: ColumnDefinition<any> };
266
/** Table-level options */
267
options: any;
268
/** Reference key field name */
269
referenceKey: string;
270
271
/**
272
* Creates new instance of the entity
273
* @returns New entity instance
274
*/
275
createInstance(): T;
276
}
277
278
/**
279
* Individual column configuration
280
*/
281
interface ColumnDefinition<K> {
282
/** Property name in the model class */
283
modelName: string;
284
/** Corresponding column name in database */
285
dbTableName: string;
286
/** Relationship mapping configuration */
287
mapping?: any;
288
/** Inverse relationship definitions */
289
inverseMappingDef?: any;
290
/** Primary key generation strategy */
291
primaryType?: "auto-increment" | "custom";
292
/** Column-specific options */
293
opts?: any;
294
}
295
```
296
297
**Usage Examples:**
298
299
```typescript
300
import { getDBConfig } from "hibernatets";
301
302
@table({ name: "user_accounts" })
303
class User {
304
@primary()
305
id: number;
306
307
@column()
308
name: string;
309
310
@mapping(Mappings.OneToMany, Post, "userId")
311
posts: Post[];
312
}
313
314
// Access complete configuration
315
const userConfig = getDBConfig(User);
316
317
console.log("Configuration details:");
318
console.log("- Table:", userConfig.table); // "user_accounts"
319
console.log("- Primary key:", userConfig.modelPrimary); // "id"
320
console.log("- Reference key:", userConfig.referenceKey); // "id"
321
322
// Inspect column configurations
323
Object.entries(userConfig.columns).forEach(([propName, columnDef]) => {
324
console.log(`Column ${propName}:`, {
325
dbName: columnDef.dbTableName,
326
hasMapping: !!columnDef.mapping,
327
isPrimary: !!columnDef.primaryType
328
});
329
});
330
331
// Create new instance using configuration
332
const newUser = userConfig.createInstance();
333
console.log("New instance:", newUser); // Empty User instance
334
```
335
336
### Type Utilities
337
338
Type-safe utilities for working with entity objects and their properties.
339
340
```typescript { .api }
341
/**
342
* Type-safe Object.values() implementation
343
* @param obj - Object to extract values from
344
* @returns Array of object values with preserved typing
345
*/
346
function objectValues<T>(obj: T): Array<T[keyof T]>;
347
348
/**
349
* Custom utility type for omitting keys from objects
350
*/
351
type CustomOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
352
```
353
354
**Usage Examples:**
355
356
```typescript
357
import { objectValues } from "hibernatets";
358
359
interface UserData {
360
id: number;
361
name: string;
362
email: string;
363
}
364
365
const userData: UserData = {
366
id: 1,
367
name: "Alice",
368
email: "alice@example.com"
369
};
370
371
// Type-safe value extraction
372
const values = objectValues(userData);
373
// Type: (string | number)[]
374
// Value: [1, "Alice", "alice@example.com"]
375
376
// Usage with entity configurations
377
const config = getDBConfig(User);
378
const columnDefs = objectValues(config.columns);
379
// Type: ColumnDefinition<any>[]
380
381
// Iterate with type safety
382
columnDefs.forEach(column => {
383
console.log(`Column: ${column.modelName} -> ${column.dbTableName}`);
384
});
385
```
386
387
### Advanced Configuration Patterns
388
389
Complex configuration scenarios and best practices for application setup.
390
391
```typescript
392
import {
393
updateDatabase, intercept, getDBConfig,
394
MariaDbBase, withMariaDbPool, setMariaDbPoolDefaults
395
} from "hibernatets";
396
import path from "path";
397
398
// Application configuration manager
399
class HibernateTSConfig {
400
private static initialized = false;
401
private static modelPaths: string[] = [];
402
403
static async initialize(options: {
404
modelPaths: string[];
405
autoSchema?: boolean;
406
dbPoolGenerator?: DataBaseBaseStatic;
407
interceptByDefault?: boolean;
408
}) {
409
if (this.initialized) return;
410
411
this.modelPaths = options.modelPaths;
412
413
// Set up database defaults
414
if (options.dbPoolGenerator) {
415
setMariaDbPoolDefaults({
416
connectionLimit: 20,
417
minimumIdle: 5,
418
idleTimeout: 300000
419
});
420
}
421
422
// Update database schema if requested
423
if (options.autoSchema !== false) {
424
for (const modelPath of options.modelPaths) {
425
await updateDatabase(modelPath, {
426
dbPoolGEnerator: options.dbPoolGenerator
427
});
428
}
429
}
430
431
this.initialized = true;
432
console.log("HibernateTS configuration initialized");
433
}
434
435
static createEntityWithDefaults<T>(
436
EntityClass: new () => T,
437
data: Partial<T>,
438
options: { enableInterception?: boolean } = {}
439
): T {
440
const entity = Object.assign(new EntityClass(), data);
441
442
if (options.enableInterception !== false) {
443
intercept(entity, {
444
interceptArrayFunctions: true
445
});
446
}
447
448
return entity;
449
}
450
451
static async validateEntityConfiguration<T>(
452
EntityClass: new () => T
453
): Promise<boolean> {
454
try {
455
const config = getDBConfig(EntityClass);
456
457
// Validate basic configuration
458
if (!config.table || !config.modelPrimary) {
459
throw new Error("Missing table or primary key configuration");
460
}
461
462
// Validate columns
463
if (Object.keys(config.columns).length === 0) {
464
throw new Error("No columns configured");
465
}
466
467
// Test instance creation
468
const instance = config.createInstance();
469
if (!instance) {
470
throw new Error("Cannot create entity instance");
471
}
472
473
return true;
474
} catch (error) {
475
console.error(`Entity validation failed:`, error);
476
return false;
477
}
478
}
479
480
static getModelSummary() {
481
// This would typically iterate through loaded models
482
return {
483
initialized: this.initialized,
484
modelPaths: this.modelPaths,
485
loadedModels: [] // Would be populated with actual model information
486
};
487
}
488
}
489
490
// Usage example
491
async function setupApplication() {
492
await HibernateTSConfig.initialize({
493
modelPaths: [
494
path.join(__dirname, "entities"),
495
path.join(__dirname, "models")
496
],
497
autoSchema: true,
498
dbPoolGenerator: MariaDbBase,
499
interceptByDefault: true
500
});
501
502
// Validate entity configurations
503
const isUserValid = await HibernateTSConfig.validateEntityConfiguration(User);
504
const isPostValid = await HibernateTSConfig.validateEntityConfiguration(Post);
505
506
if (!isUserValid || !isPostValid) {
507
throw new Error("Entity configuration validation failed");
508
}
509
510
console.log("Application setup complete");
511
}
512
```
513
514
### Error Handling
515
516
Enhanced error handling and debugging utilities for database operations.
517
518
```typescript { .api }
519
/**
520
* Enhanced Error class with cause chain support
521
*/
522
class Exception extends Error {
523
/**
524
* Creates exception with nested error information
525
* @param message - Error message
526
* @param error - Underlying error that caused this exception
527
*/
528
constructor(message: string, error: Error);
529
}
530
```
531
532
**Usage Examples:**
533
534
```typescript
535
import { Exception, load, save } from "hibernatets";
536
537
// Error handling with context
538
async function safeDatabaseOperation<T>(
539
operation: () => Promise<T>,
540
context: string
541
): Promise<T> {
542
try {
543
return await operation();
544
} catch (error) {
545
throw new Exception(
546
`Database operation failed in ${context}`,
547
error as Error
548
);
549
}
550
}
551
552
// Usage with comprehensive error context
553
try {
554
const user = await safeDatabaseOperation(
555
() => load(User, 999),
556
"user loading"
557
);
558
559
await safeDatabaseOperation(
560
() => save(user),
561
"user saving"
562
);
563
} catch (error) {
564
if (error instanceof Exception) {
565
console.error("Operation failed:", error.message);
566
console.error("Root cause:", error.cause);
567
}
568
}
569
```
570
571
### Extended Map Utilities
572
573
Type-safe extended map implementations for managing key-value pairs with automatic JSON serialization.
574
575
```typescript { .api }
576
/**
577
* Extended Map implementation with type-safe value access and JSON serialization
578
* @template T - ExtendedMapItem type defining key and value structure
579
* @template ValueMap - Mapped type for value types by key
580
*/
581
class ExtendedMap<T extends ExtendedMapItem<string, any>, ValueMap extends { [key in T["key"]]: ReturnType<T["parsed"]> } = {
582
[key in T["key"]]: any;
583
}> extends Map<T["key"], ExtendedMapItem<T["key"], ValueMap[T["key"]]>> {
584
/**
585
* Creates new ExtendedMap instance
586
* @param itemClass - Constructor for map items
587
* @param parentArray - Optional array to sync with
588
*/
589
constructor(itemClass: new () => T, parentArray?: Array<T>);
590
591
/**
592
* Gets parsed value for a key with fallback support
593
* @param key - Map key
594
* @param fallback - Optional fallback value
595
* @returns Parsed value of type ValueMap[K]
596
*/
597
getValue<K extends T["key"]>(key: K, fallback?: ValueMap[K]): ValueMap[K];
598
599
/**
600
* Sets parsed value for a key
601
* @param key - Map key
602
* @param val - Value to set
603
*/
604
setValue<K extends T["key"]>(key: K, val: ValueMap[K]): void;
605
606
/**
607
* Iterate over parsed values
608
* @returns Iterator of parsed values
609
*/
610
entryValues(): IterableIterator<ReturnType<T["parsed"]>>;
611
612
/**
613
* Iterate over each value with callback
614
* @param callbackfn - Function called for each key-value pair
615
*/
616
forEachValue(callbackfn: <K extends T["key"]>(value: ValueMap[K], key: K, map: ExtendedMap<T>) => void): void;
617
}
618
619
/**
620
* Extended Map Item for storing key-value pairs with JSON serialization
621
* @template K - Key type (string-based)
622
* @template T - Value type
623
*/
624
@table()
625
class ExtendedMapItem<K extends string = string, T = any> {
626
/** Primary key for database storage */
627
@primary()
628
id: number;
629
630
/** JSON-serialized value */
631
@column()
632
value: string;
633
634
/** Map key */
635
@column()
636
key: K;
637
638
/**
639
* Parse stored JSON value
640
* @returns Parsed value of type T
641
*/
642
parsed(): T;
643
644
/**
645
* Set value with JSON serialization
646
* @param value - Value to serialize and store
647
*/
648
setStringified(value: T): void;
649
}
650
```
651
652
**Usage Examples:**
653
654
```typescript
655
import { ExtendedMap, ExtendedMapItem } from "hibernatets";
656
657
// Define custom map item type
658
interface UserSettings {
659
theme: "dark" | "light";
660
notifications: boolean;
661
language: "en" | "es" | "fr";
662
}
663
664
interface SettingsMap {
665
theme: "dark" | "light";
666
notifications: boolean;
667
language: "en" | "es" | "fr";
668
}
669
670
// Create extended map for user settings
671
const settingsMap = new ExtendedMap<ExtendedMapItem<keyof SettingsMap, any>, SettingsMap>(
672
ExtendedMapItem,
673
[]
674
);
675
676
// Set values with automatic JSON serialization
677
settingsMap.setValue("theme", "dark");
678
settingsMap.setValue("notifications", true);
679
settingsMap.setValue("language", "en");
680
681
// Get values with type safety and fallbacks
682
const theme = settingsMap.getValue("theme", "light"); // "dark"
683
const notifications = settingsMap.getValue("notifications", false); // true
684
const language = settingsMap.getValue("language", "en"); // "en"
685
686
// Iterate over all values
687
settingsMap.forEachValue((value, key) => {
688
console.log(\`Setting \${key}: \${value}\`);
689
});
690
691
// Working with extended map items directly
692
const themeItem = new ExtendedMapItem<"theme", "dark" | "light">();
693
themeItem.key = "theme";
694
themeItem.setStringified("dark");
695
696
console.log(themeItem.parsed()); // "dark"
697
console.log(themeItem.value); // "\"dark\""
698
```