| name | bx-orm-bif-development |
| description | Use when creating ORM built-in functions (BIFs): extending BaseORMBIF, @BoxBIF annotation, invoke() method, argument patterns (entityName, idOrFilter, options), entity name resolution, ORMService access, ORMContext lookup, session handling, HQL execution, entity CRUD operations (EntityLoad, EntitySave, EntityDelete, EntityNew, EntityReload), and ORM utility BIFs (ORMEvictEntity, ORMFlush, ORMClearSession, ORMGetSession). |
| version | 1.0.0 |
| domain | bx-orm |
| triggers | ORM BIF, entityLoad, entitySave, entityDelete, entityNew, entityReload, entityMerge, entityToQuery, ormFlush, ormClearSession, ormCloseSession, ormGetSession, ormExecuteQuery, BaseORMBIF, orm built-in function |
| role | expert |
| scope | bx-orm |
| related-skills | boxlang-core-dev-bif-development, bx-orm-session-management, bx-orm-hibernate-bridge |
BoxLang ORM — BIF Development
Overview
ORM BIFs (Built-In Functions) provide the public API for BoxLang developers to interact with the ORM. They wrap Hibernate session operations — entity CRUD, session management, HQL queries — in BoxLang-callable functions that resolve the correct ORM context and datasource transparently.
BIF Inventory
| Category | BIFs |
|---|
| Entity CRUD | EntityNew, EntityLoad, EntityLoadByPK, EntityLoadByExample, EntitySave, EntityDelete, EntityMerge, EntityReload, EntityNameArray, EntityToQuery |
| Session Management | ORMGetSession, ORMGetSessionFactory, ORMCloseSession, ORMCloseAllSessions, ORMClearSession, ORMFlush, ORMFlushAll, ORMReload |
| Cache & Eviction | ORMEvictEntity, ORMEvictCollection, ORMEvictQueries |
| HQL Queries | ORMExecuteQuery |
| Metadata | ORMGetHibernateVersion |
BaseORMBIF — The Parent Class
All ORM BIFs extend BaseORMBIF, which extends the core BIF class and provides shared access to the ORM service:
public abstract class BaseORMBIF extends BIF {
protected ORMService ormService = (ORMService) runtime
.getGlobalService( ORMKeys.ORMService );
protected String getEntityName( IClassRunnable entity ) {
return ORMService.getEntityName( entity );
}
protected String getClassNameFromFQN( String fqn ) {
return ORMService.getClassNameFromFQN( fqn );
}
}
BIF Structure Pattern
Every ORM BIF follows this pattern:
@BoxBIF
public class EntityLoad extends BaseORMBIF {
public EntityLoad() {
super();
declaredArguments = new Argument[] {
new Argument( true, "string", ORMKeys.entityName ),
new Argument( false, "any", ORMKeys.idOrFilter ),
new Argument( false, "any", ORMKeys.uniqueOrOrder ),
new Argument( false, "struct", ORMKeys.options )
};
}
@Override
public Object invoke( IBoxContext context, ArgumentsScope arguments ) {
ORMApp ormApp = ormService.getORMAppByContext( context );
if ( ormApp == null ) {
throw new BoxRuntimeException( "No ORM application configured" );
}
IJDBCCapableContext jdbcContext = context
.getParentOfType( IJDBCCapableContext.class );
ORMContext ormContext = ORMContext.getForContext( jdbcContext );
String entityName = arguments.getAsString( ORMKeys.entityName );
Key datasource = ormApp.getEntityDatasource( entityName );
Session session = ormContext.getSession( datasource );
return result;
}
}
Entity CRUD BIF — Detailed Reference
EntityNew
Creates a new entity instance without persisting:
@BoxBIF
public class EntityNew extends BaseORMBIF {
}
var vehicle = entityNew( "Vehicle", {
make : "Toyota",
model : "Camry"
} )
EntityLoad
Loads entities with optional filtering, sorting, and pagination:
@BoxBIF
public class EntityLoad extends BaseORMBIF {
}
Options struct:
var options = {
unique : false,
ignorecase : false,
offset : 0,
maxresults : null,
cacheable : false,
cachename : null,
timeout : null
}
EntityLoadByPK
Loads a single entity by its primary key:
@BoxBIF
public class EntityLoadByPK extends BaseORMBIF {
}
EntitySave
Persists a new or updated entity:
@BoxBIF
public class EntitySave extends BaseORMBIF {
}
EntityDelete
Removes an entity from the database:
@BoxBIF
public class EntityDelete extends BaseORMBIF {
}
EntityReload
Refreshes an entity from the database, discarding in-memory changes:
@BoxBIF
public class EntityReload extends BaseORMBIF {
}
EntityMerge
Merges a detached entity state:
@BoxBIF
public class EntityMerge extends BaseORMBIF {
}
Session Management BIFs
ORMFlush / ORMFlushAll
@BoxBIF
public class ORMFlush extends BaseORMBIF {
}
@BoxBIF
public class ORMFlushAll extends BaseORMBIF {
}
ORMClearSession
@BoxBIF
public class ORMClearSession extends BaseORMBIF {
}
ORMCloseSession / ORMCloseAllSessions
@BoxBIF
public class ORMCloseSession extends BaseORMBIF {
}
@BoxBIF
public class ORMCloseAllSessions extends BaseORMBIF {
}
ORMEvictEntity / ORMEvictCollection / ORMEvictQueries
Evict entities or query results from the session and/or second-level cache:
@BoxBIF
public class ORMEvictEntity extends BaseORMBIF {
}
HQL BIF
ORMExecuteQuery
Executes a raw HQL query:
@BoxBIF
public class ORMExecuteQuery extends BaseORMBIF {
}
var results = ormExecuteQuery(
"FROM Vehicle WHERE make = ? AND year > ?",
[ "Toyota", 2020 ]
)
var results = ormExecuteQuery(
"FROM Vehicle WHERE make = :make AND year > :year",
{ make : "Toyota", year : 2020 }
)
var vehicle = ormExecuteQuery(
"FROM Vehicle WHERE id = :id",
{ id : 42 },
true
)
Argument Patterns
Required Pattern: entityName as first argument
Almost every entity BIF takes entityName as its first required argument:
new Argument( true, "string", ORMKeys.entityName )
Common Pattern: options as last argument
Optional behavior is grouped into an options struct:
new Argument( false, "struct", ORMKeys.options )
Overloaded Pattern: idOrFilter
EntityLoad accepts either an ID (string/number) or a filter struct as the second argument:
new Argument( false, "any", ORMKeys.idOrFilter )
Creating a New ORM BIF
package ortus.boxlang.modules.orm.bifs;
import ortus.boxlang.modules.orm.config.ORMKeys;
import ortus.boxlang.runtime.bifs.BoxBIF;
import ortus.boxlang.runtime.context.IBoxContext;
import ortus.boxlang.runtime.scopes.ArgumentsScope;
import ortus.boxlang.runtime.types.Argument;
@BoxBIF
public class EntityCount extends BaseORMBIF {
public EntityCount() {
super();
declaredArguments = new Argument[] {
new Argument( true, "string", ORMKeys.entityName ),
new Argument( false, "struct", ORMKeys.filter )
};
}
@Override
public Object invoke( IBoxContext context, ArgumentsScope arguments ) {
String entityName = arguments.getAsString( ORMKeys.entityName );
ORMApp ormApp = ormService.getORMAppByContext( context );
if ( ormApp == null ) {
throw new BoxRuntimeException( "No ORM application configured" );
}
IJDBCCapableContext jdbcContext = context
.getParentOfType( IJDBCCapableContext.class );
ORMContext ormContext = ORMContext.getForContext( jdbcContext );
Key datasource = ormApp.getEntityDatasource( Key.of( entityName ) );
Session session = ormContext.getSession( datasource );
Criteria criteria = session.createCriteria(
ormApp.getEntityClass( entityName )
);
if ( arguments.containsKey( ORMKeys.filter ) ) {
IStruct filter = arguments.getAsStruct( ORMKeys.filter );
filter.forEach( ( key, value ) -> {
criteria.add( Restrictions.eq( key.getName(), value ) );
} );
}
Long count = (Long) criteria
.setProjection( Projections.rowCount() )
.uniqueResult();
return count;
}
}
File Locations
src/main/java/ortus/boxlang/modules/orm/bifs/
├── BaseORMBIF.java # Parent class for all ORM BIFs
├── EntityNew.java
├── EntityLoad.java
├── EntityLoadByPK.java
├── EntityLoadByExample.java
├── EntitySave.java
├── EntityDelete.java
├── EntityMerge.java
├── EntityReload.java
├── EntityNameArray.java
├── EntityToQuery.java
├── ORMExecuteQuery.java
├── ORMGetSession.java
├── ORMGetSessionFactory.java
├── ORMFlush.java
├── ORMFlushAll.java
├── ORMClearSession.java
├── ORMCloseSession.java
├── ORMCloseAllSessions.java
├── ORMEvictEntity.java
├── ORMEvictCollection.java
├── ORMEvictQueries.java
├── ORMReload.java
└── ORMGetHibernateVersion.java
Registration
ORM BIFs are auto-discovered by BoxLang's module system via the META-INF/services file:
src/main/resources/META-INF/services/ortus.boxlang.runtime.bifs.BIF
Each line in this file is the fully-qualified class name of a BIF:
ortus.boxlang.modules.orm.bifs.EntityNew
ortus.boxlang.modules.orm.bifs.EntityLoad
ortus.boxlang.modules.orm.bifs.EntitySave
...
Best Practices
- Always extend
BaseORMBIF — never extend BIF directly for ORM functions; BaseORMBIF provides shared ORM service access and entity name resolution.
- Resolve ORM context explicitly — use
context.getParentOfType( IJDBCCapableContext.class ) to find the JDBC-capable context, then get the ORM context from it.
- Guard against null
ORMApp — throw a descriptive BoxRuntimeException if no ORM application is configured, rather than letting a NullPointerException surface.
- Use
ORMKeys for argument names — all argument names should reference ORMKeys constants.
- Support both positional and named HQL parameters — in
ORMExecuteQuery and similar BIFs, detect whether params is an Array (positional) or IStruct (named).
- Return BoxLang-native types — return
IClassRunnable, Array, IStruct, Boolean, or Number; never raw Hibernate objects.