A TypeScript ORM library inspired by Hibernate for working with MySQL, MariaDB, and PostgreSQL databases using decorators and async/await patterns
Core CRUD operations for loading, saving, and deleting entities with support for relationships, bulk operations, and advanced querying capabilities. HibernateTS provides a comprehensive set of functions for database interaction with full type safety.
Loads entities from the database with support for deep loading, filtering, and flexible query patterns.
/**
* Loads entities from the database with support for deep loading and filtering
* @param findClass - Constructor class of the entity to load
* @param primaryKeyOrFilter - Primary key, SQL condition, function filter, or LoadParams object
* @param parameters - Array of SQL parameters or string parameter
* @param options - LoadOptions for controlling loading behavior
* @returns Promise resolving to single entity or array of entities
*/
function load<T>(findClass: ConstructorClass<T>, primaryKeyOrFilter: LoadParams<T, Filter<T>, { first: true } & CustomOmit<LoadOptions<T>, "first">>): Promise<T>;
function load<T>(findClass: ConstructorClass<T>, primaryKeyOrFilter: number | LoadParams<T, number>, parameters?: any[], options?: LoadOptions<T>): Promise<T>;
function load<T>(findClass: ConstructorClass<T>, primaryKeyOrFilter: string | SqlCondition | LoadParams<T, string>, parameters?: any[], options?: LoadOptions<T>): Promise<Array<T>>;
function load<T>(findClass: ConstructorClass<T>, primaryKeyOrFilter: ((obj: T) => any) | LoadParams<T, (obj: T) => any>, parameters?: any[], options?: LoadOptions<T>): Promise<Array<T>>;
interface LoadOptions<T> {
/** Enable deep loading of relationships */
deep?: boolean | Array<string | SqlCondition> | { [key: string]: string | SqlCondition | { filter: string | SqlCondition, depths: number } };
/** Array of field names to skip during loading */
skipFields?: Array<string>;
/** Return single result instead of array */
first?: boolean;
/** Include IDs for non-deep one-to-one relationships */
idOnNonDeepOneToOne?: boolean;
/** Include shallow reference data */
withShallowReferences?: boolean;
/** Database connection to use */
db?: DataBaseBase;
/** Skip automatic setter interception */
dontInterceptSetters?: true;
/** Enable array function interception */
interceptArrayFunctions?: boolean;
}
interface LoadParams<T, F = Filter<T>, O = LoadOptions<T>> {
/** Filter condition */
filter?: F;
/** SQL parameters */
params?: Array<string | number> | string;
/** Load options */
options?: O;
}
interface ConstructorClass<T> {
new (...args: any[]): T;
}
interface DataBaseBase {
sqlquery<T>(cfg: any, queryString: string, params?: any[]): Promise<any>;
selectQuery<T>(queryString: string, params?: any[]): Promise<Array<T>>;
end(): Promise<void>;
}
type Filter<T> = SqlCondition | string | number | ((obj: T) => any);Usage Examples:
import { load, SqlCondition } from "hibernatets";
// Load by primary key
const user = await load(User, 1);
// Load by primary key with relationships
const userWithPosts = await load(User, 1, [], { deep: true });
// Load multiple users with SQL condition
const activeUsers = await load(User,
new SqlCondition().column("active").equals(true)
);
// Load with specific deep loading
const userWithFilteredPosts = await load(User, 1, [], {
deep: {
posts: new SqlCondition().column("published").equals(true)
}
});
// Load with function filter
const youngUsers = await load(User, (user: User) => user.age < 30);
// Load with LoadParams object
const result = await load(User, {
filter: new SqlCondition().column("name").equals("Alice"),
params: [],
options: { deep: true, first: true }
});
// Load with skip fields
const usersWithoutSensitiveData = await load(User,
SqlCondition.ALL,
[],
{ skipFields: ["password", "email"] }
);Saves entities to the database with support for relationships, duplicate key handling, and bulk operations.
/**
* Saves entities to the database with support for relationships and duplicate key handling
* @param saveObjects - Single entity or array of entities to save
* @param options - SaveOptions including updateOnDuplicate and db connection
* @returns Promise resolving to array of affected row counts
*/
function save<T>(saveObjects: T | T[], options?: SaveOptions<T>): Promise<Array<number>>;
interface SaveOptions<T> {
/** Handle duplicate keys during save operation */
updateOnDuplicate?: boolean | { skip?: Array<keyof T & string> };
/** Database connection to use */
db?: DataBaseBase;
}Usage Examples:
import { save } from "hibernatets";
// Save single entity
const user = new User();
user.name = "Alice";
user.email = "alice@example.com";
const [insertId] = await save(user);
// Save multiple entities
const users = [
Object.assign(new User(), { name: "Bob", email: "bob@example.com" }),
Object.assign(new User(), { name: "Charlie", email: "charlie@example.com" })
];
const insertIds = await save(users);
// Save with duplicate key handling
await save(user, {
updateOnDuplicate: true
});
// Save with selective duplicate key handling
await save(user, {
updateOnDuplicate: { skip: ["createdAt", "id"] }
});
// Save with relationships
const userWithPosts = new User();
userWithPosts.name = "David";
userWithPosts.posts = [
Object.assign(new Post(), { title: "First Post" }),
Object.assign(new Post(), { title: "Second Post" })
];
await save(userWithPosts);Deletes entities from the database with support for cascade deletion and bulk operations.
/**
* Deletes entities from the database with support for cascade deletion
* @param object - Entity instance to delete
* @param opts - DeleteOptions including cascade deletion settings
* @returns Promise resolving to number of affected rows
*/
function remove<T>(object: any, opts?: DeleteOptions): Promise<number>;
/**
* Deletes entities by primary key with support for bulk deletion
* @param descriptor - Constructor class of the entity
* @param primaryId - Primary key value or array of values
* @param opts - DeleteOptions including cascade deletion settings
* @returns Promise resolving to number of affected rows
*/
function remove<T>(descriptor: ConstructorClass<T>, primaryId: number | Array<number>, opts?: DeleteOptions): Promise<number>;
interface DeleteOptions {
/** Enable cascade deletion of related entities */
deep?: boolean | Array<string>;
/** Database connection to use */
db?: DataBaseBase;
}Usage Examples:
import { remove, load } from "hibernatets";
// Delete single entity
const user = await load(User, 1);
const deletedRows = await remove(user);
// Delete by primary key
const deletedCount = await remove(User, 1);
// Delete multiple entities by IDs
const deletedCount = await remove(User, [1, 2, 3]);
// Cascade deletion of related entities
await remove(user, { deep: true });
// Selective cascade deletion
await remove(user, {
deep: ["posts", "profile"]
});Functions for managing array relationships and bulk operations.
/**
* Adds items to an array property with automatic relationship mapping
* @param parent - Parent entity containing the array
* @param key - Key of the array property
* @param opts - Array of items to add or AddArrayOpts object
*/
function addArrayItem<T extends ISaveAbleObject, K extends ArrayKeys<T>>(
parent: T,
key: K,
opts: Array<ArrayType<T, K> & ISaveAbleObject> | AddArrayOpts<ArrayType<T, K> & ISaveAbleObject>
): void;
interface AddArrayOpts<T> {
/** Database connection to use */
db: DataBaseBase;
/** Items to add to the array */
items: Array<T>;
}
interface ISaveAbleObject {
[key: string]: any;
}
type ArrayKeys<T> = {
[K in keyof T]: T[K] extends Array<infer U> ? K : never;
}[keyof T];
type ArrayType<T, K extends keyof T> = T[K] extends Array<infer U> ? U : never;Usage Examples:
import { addArrayItem, load, save } from "hibernatets";
// Load user with posts
const user = await load(User, 1, [], { deep: true });
// Add new posts to user
const newPosts = [
Object.assign(new Post(), { title: "New Post 1" }),
Object.assign(new Post(), { title: "New Post 2" })
];
addArrayItem(user, "posts", newPosts);
// Save to persist changes
await save(user);Execute pending database updates and retrieve results.
/**
* Executes all pending database updates for an object and returns results
* @param object - Entity with pending updates
* @returns Promise resolving to array of result counts
*/
function queries(object: any): Promise<Array<number>>;Usage Examples:
import { queries, intercept } from "hibernatets";
// Enable automatic change tracking
const user = await load(User, 1);
intercept(user);
// Make changes
user.name = "Updated Name";
user.email = "updated@example.com";
// Execute all pending queries
const results = await queries(user);
console.log(`Updated ${results.length} database records`);Complex loading scenarios with conditional relationships and custom filters.
import { load, SqlCondition, Mappings } from "hibernatets";
// Conditional deep loading based on user permissions
async function loadUserData(userId: number, includePrivateData: boolean) {
const loadOptions = {
deep: includePrivateData ? true : ["posts"],
skipFields: includePrivateData ? [] : ["email", "phone"]
};
return await load(User, userId, [], loadOptions);
}
// Load with complex relationship filters
const usersWithRecentPosts = await load(User, SqlCondition.ALL, [], {
deep: {
posts: new SqlCondition()
.column("createdAt")
.greater()
.param(new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)) // Last 30 days
}
});
// Pagination with relationships
async function getPaginatedUsers(page: number, pageSize: number) {
const offset = page * pageSize;
return await load(User,
new SqlCondition()
.column("active")
.equals(true)
.and(c => c.column("id").greater().param(offset)),
[],
{
deep: ["profile"],
first: false
}
);
}Efficient patterns for handling large datasets and bulk operations.
import { save, load, remove } from "hibernatets";
// Bulk insert with batch processing
async function bulkInsertUsers(userData: any[]) {
const batchSize = 100;
const results = [];
for (let i = 0; i < userData.length; i += batchSize) {
const batch = userData.slice(i, i + batchSize);
const users = batch.map(data => Object.assign(new User(), data));
const batchResults = await save(users);
results.push(...batchResults);
}
return results;
}
// Bulk update with selective fields
async function bulkUpdateUserStatus(userIds: number[], newStatus: boolean) {
const users = await load(User,
new SqlCondition().column("id").equals(userIds)
);
users.forEach(user => {
user.active = newStatus;
user.lastModified = new Date();
});
return await save(users, {
updateOnDuplicate: { skip: ["createdAt"] }
});
}
// Bulk delete with conditions
async function cleanupInactiveUsers(daysInactive: number) {
const cutoffDate = new Date(Date.now() - daysInactive * 24 * 60 * 60 * 1000);
const inactiveUsers = await load(User,
new SqlCondition()
.column("lastLoginAt")
.smaller()
.param(cutoffDate)
.and(c => c.column("active").equals(false))
);
const deletePromises = inactiveUsers.map(user =>
remove(user, { deep: ["posts", "profile"] })
);
return await Promise.all(deletePromises);
}Install with Tessl CLI
npx tessl i tessl/npm-hibernatets