We are so excited to bring you yet another minor release for our cborm project to version 2.2. We have fine tuned our module with some cool new features and some major improvements. So let's start with our orm adventure by installing or updating.

Install or Update

To get started with cborm, just install it via CommandBox: box install cborm. To update your current install just run a update cborm and off you go.

New Documentation Book

We have completely overhauled the documentation for this module and converted it to a comprehensive book. The new book contains a refreshed look, better categorization, improved structure and some very nice intros to working with Basic CRUD with services and Basic CRUD with Active Entity and all the way to advanced criteria queries.

 

 

What is cborm

The cborm module is a module that will enhance your experience when working with the ColdFusion ORM powered by Hibernate. It will not only enhance it with dynamic goodness but give you a fluent and human approach to working with Hibernate

  • Service Layers with all the methods you could probably think off to help you get started in any project
  • Virtual service layers so you can create virtual services for any entity in your application
  • ActiveEntity our implementation of Active Record for ORM
  • Fluent queries via Hibernate's criteria and detached criteria queries with some Dynamic CFML goodness
  • Automatic transaction demarcation for save and delete operations
  • Dynamic finders and counters for expressive and fluent shorthand SQL
  • Automatic Java casting
  • Entity population from json, structs, xml, and queryies including building up their relationships
  • Entity validation via cbValidation
  • Includes the Mementifier project to produce memento states from any entity, great for producing JSON
  • Ability for finders and queries to be returned as Java streams using our cbStreams project.

 

# A quick preview of some functionality

var book = new Book().findByTitle( "My Awesome Book" );
var book = new Book().getOrFail( 2 );
new Book().getOrFail( 4 ).delete();
new Book().deleteWhere( isActive:false, isPublished:false );

property name="userService" inject="entityService:User";

return userService.list();
return userService.list( asStream=true );

var count = userService.countWhere( age:20, isActive:true );
var users = userService.findAllByLastLoginBetween( "01/01/2019", "05/01/2019" );

userService
    .newCriteria()
    .eq( "name", "luis" )
    .isTrue( "isActive" )
    .getOrFail();

userService
    .newCriteria()
    .isTrue( "isActive" )
    .joinTo( "role" )
        .eq( "name", "admin" )
    .asStream()
    .list();

userService
    .newCriteria()
    .withProjections( property="id,fname:firstName,lname:lastName,age" )
    .isTrue( "isActive" )
    .joinTo( "role" )
        .eq( "name", "admin" )
    .asStruct()
    .list();

What's New With 2.2.0

This release not only has some bug fixes but several new features that pack a punch.

Major Features

 

Criteria Query Fluent If Statements - when()

How many times have you been dealing with if statements in order to add some restrictions into your criteria object? Many, this was the only way before, not anymore. So instead of doing something like the following:

c = newCriteria();

if( isBoolean( arguments.isPublished ) ){
  
	c.isEq( "isPublished", isPublished );

	// Published eq true evaluate other params
    if( isPublished ){
        c.isLt( "publishedDate", now() )
        .$or( c.restrictions.isNull( "expireDate" ), c.restrictions.isGT( "expireDate", now() ) )
        .isEq( "passwordProtection","" );
    }

}

if( !isNull( arguments.showInSearch ) ){
	c.isEq( "showInSearch", showInSearch );
}


return c.list();

This looks like normal code, but we can do a more functional approach by introducing the when() function:

/**
* @test The boolean evaluation
* @target The closure to execute if test is true
*/
when( boolean test, function target )

This function takes in as the first argument a boolean value, if the value is true, then the target closure will be called for you and the criteria will be passed via the arguments scope:

newCriteria()
    .when( isBoolean( arguments.isPublished ), function( c ){
        // Published bit
        c.isEq( "isPublished", isPublished )
        	.when( isPublished, function( c ){
        		c.isLt( "publishedDate", now() )
	            .$or( c.restrictions.isNull( "expireDate" ), c.restrictions.isGT( "expireDate", now() ) )
	            .isEq( "passwordProtection","" );
        	} )
    } )
  .when( !isNull( arguments.showInSearch ), function( criteria ){	
  	c.isEq( "showInSearch", showInSearch );
   } )
  .list()

This construct will help you create more fluent designs when building criteria queries, enjoy!

 

PeekaBoo! - peek()

We have also enhanced the criteria queries with a peek() function which allows you to peek in the current position of the criteria build up. This allows you to debug or inspect the SQL/HQL inside the criteria at that point in time. You can use it for sending debug data or logging, or auditing.

/**
* @target the closure to execute, the criteria is passed as an argument
*/
peek( target )

Enjoy your peekaboo function!

newCriteria()
    .when( isBoolean( arguments.isPublished ), function( c ){
        // Published bit
        c.isEq( "isPublished", isPublished )
        	.when( isPublished, function( c ){
        		c.isLt( "publishedDate", now() )
	            .$or( c.restrictions.isNull( "expireDate" ), c.restrictions.isGT( "expireDate", now() ) )
	            .isEq( "passwordProtection","" );
        	} )
    } )
  .peek( function( c ){
      systemOutput( "HQL after publish: #criteria.getSql()# ");
  } )
  .when( !isNull( arguments.showInSearch ), function( criteria ){	
  	c.isEq( "showInSearch", showInSearch );
   } )
   .peek( function( c ){
      systemOutput( "HQL before listing: #criteria.getSql()# ");
  } )
  .list()

 

ValidateOrFail

We have added a new function on the ActiveEntity object to assist with validations. The validateOrFail() function will allow you to validate the entity and if it validates it just returns the instance of the entity for a nice fluent design. However, if the validation fails, it throws a ValidationException and the errors are passed to the exception object via the extendedInfo key. You can then deal with the exception as needed.

getInstance( "User" )
    .populate( rc )
    .validateOrFail()
    .save();

 

Release Notes

  • Features: New function for criteria query when( boolean, target ) that you can use to build functional criterias without the use of if statements.
  • Feature: Missing nullValue() is BaseBuilder class
  • Feature: Added new criteria query peek( closure ) function to allow for peeking into the building process. Pass in your closure that receives the criteria and interact with it.
  • Feature: Added a validateOrFail() to the active entity, which if the validation fails it will throw an exception or return back to you the same entity validated now.
  • Improvement: Better documentation for deleteById() since it does bulk deletion, which does not do any type of cascading.
  • Improvement: isValid() in active entity missing includeFields argument
  • Improvement: Timeout hints for criteria builder
  • Improvement: Updated exception type for criteria builder get()
  • Bug: ACF2016 issues with elvis operator.
  • Bug: getOrFail() had an invalid throw statement