Couple thoughts on BaseORMService

Had a couple thoughts when integrating BaseORMService

1) Can you add dml style deletes to the TODO:s? Right now every call
to delete() executes two queries. One to get the entity and another
to delete it. Need a better way to do bulk deletes.

2) Regarding the hyrule integration, I'd prefer it if on validation
error coldBox would raise an exception rather than returning the array
of errors messages from hyrule.

My reasoning is that I am trying to workout the logic in handling the
return data from the BaseORMService.save function and how to catch
validation errors. Right now it appears to be:

var saveResult = ormService.save( entity = myEntity, validate =
true );

if (isBoolean(saveResult) && saveResult ) {
// set flash msg and next action
} else {
  // set error message and show form
  rc.errorDetail = saveResult;
}

If coldbox threw an exception I think the code to handle this would
read much better:

try {

  ormService.save( entity = myEntity, validate = true );

} catch ( coldbox.validation.hyrule error ) {

  rc.errorDetail = DeserializeJSON(error.extendedInfo);

}

The trick is for ColdBox to serialize the hyrule error data into JSON
and pass that string as the throw() extendedInfo attribute. The error
type can be whatever I generally like hierarchical error types so I
can catch coldbox.validation.hyrule with it with catch
( coldbox.validation.hyrule error ) or catch ( coldbox.validation
error ) or even catch ( coldbox error )

Just a couple ideas, thanks!

.brett

Oh also todo with #2, I see validation errors that prevents an
entities from persisting as critical errors.

Right now the request is not aborted by default and it is up to the
developer to inspect the return data for error. Whereas the throw()
hits the developer in the face if they are not catching it.

I only say this because I spent two hours today troubleshooting a DI
issue only to find out my module's model directory was called "models"
and my property wasn't being injected. Doh!

Hi Brett

Purpose of validation is always to return error detail, so the developer should handle it for further process.

Tough you mention some nice suggestions and we will see what could be done in this area.

Any more suggestion or feedback would be great.

Thanks
Sana

Hi Sana,

Purpose of validation is always to return error detail

This is kinda where my gripe comes from. The save method of
BaseORMService returns a duel purpose result, boolean true if
successful, array of errors if not. It'd be a compromise if it
returned an empty array if there is no error found in the entity and
the object was saved - so then my test for error could be
arrayIsEmpty(). I prefer the the thrown exception as mentioned
earlier so it would bubble up in my app error handlers.

The great thing about ColdBox is I can customize all of this, so I am
just talking out loud :slight_smile:

Thanks!

.brett

Few things, yes the batch dml operations are defintely needed and will start on those soon as they are necessary. If you have any code already or ideas, please submit them so we can add them.

As for the validation, we need to remove this from the core trunk Sana as I think we need to still plan it an evaluate it. Can you please remove from the save() and create a branch for this? I still haven’t even reviewed all of hyrule yet and still have to evaluate it yet.

I also agree with Brett about throwing exceptions from the save perspective. I think that if validation is done it should be outside of the save operation, either via the entity itself (by mixin in hyrule validations) or by creating a new validate() method on the service.

As for debugging DI, are you using WireBox? If you are, I would recommend turning on the debugger via the configuration, it will give you extremely DI debugging information via LogBox.

Ideas, thoughts?

Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Hey Lois,

My 2cents on validation is that it should be a separate method of the
service and the save() method should force validation if the object
had not been previously validated. The save routine then would return
the object after persisting it or thrown an error if the validation
failed or any other exception was raised.

Yes I am using WireBox for DI, I have debugging turned on, where would
I find the DI debugging info? Is there another panel I need to
enable?

thanks,

.brett

Once you enable the debugging level for the beanfactory package:

debug = [“coldbox.system.plugins.BeanFactory”]

Then check your appenders to see where you are sending the debug messages. I usually have the coldbox tracer appender in development as I see it in my browser immediately. This gives you insight of all the objects created by WireBox, their dependencies, discover, wiring, etc. You can’t miss that wealth of debuggin info.

Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

I think their should be a validate() method on the service that does validation. Save should not enforce it at all. So we definitely need to review the validation engine.

Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

I’d be in favour of having a validate method which returned a result object with haserror():boolean and geterrors():array methods.

What would be really cool is if you could choose validation framework, similar to how the ioc coldspring/lightwire config. That way I could use validatethis with the ormservice (i’m currently using the plugin).

  • John

– sent by a little green robot

I'd be happy with this too, but

I am still a fan of throwing exceptions, they are really powerful and
the type attribute allows you to cascade error handling. Meaning, if
you throw(type="webflint.coldbox.form.render"), you can catch that
with catch( webflint.coldbox.form.render error ) and any point of the
dot path so this also catches the error catch( webflint.coldbox
error ). Saves on a lot of logic plumbing.

Of course I could always write a wrapper for any validation service,
we just need the API :slight_smile:

.brett

I started to really like hyrule...it's exactly what I need. Will it be
included in 3.0 gold version? As I understand Luis, hyrule integration
for CB3 will be removed or put in a separate branch.

Daniel

Denial,

hyrule will be part of ColdBox ORM entity validation.
We are just discussing options / suggestions.

coldbox orm base service will have new method validate() for "hyrule"
validation.

Thanks
Sana

Sana, thanks for clarification.
Daniel

Some more thoughts on the ORMService… :slight_smile:

I’ve been reading Barney Boiserts blog posts on transactions and a couple of things caught my attention:

http://www.barneyb.com/barneyblog/2010/04/07/why-orm-transactions-are-crucial/

At the moment, the BaseORMService is creating a transaction in the save method which wraps the EntitySave call. As barney points out in this comment http://www.barneyb.com/barneyblog/2010/04/07/why-orm-transactions-are-crucial/comment-page-1/#comment-213383, this doesn’t always work with lazy loading etc.

This made me think about using the transaction inside the handler so I have something like this in the handler:

var txn = ormGetSession().beginTransaction();
try
{
// get a new basket item
BasketItem = BaseORMService.newBasketItem();
BasketItem.setOption( myOption );
BasketItem.setQuantity( rc.quantity );

EntitySave( BasketItem );

rc.Basket.addBasketItem( BasketItem );
EntitySave( rc.Basket );

txn.commit();
}
catch(e)
{
txn.rollback();
throw e;
}

Then I read this post http://www.barneyb.com/barneyblog/2010/04/06/transaction-demarcation/ about using ColdSpring AOP and I wondered if actually it would be better to have an interceptor for preEvent and postEvent to create the transactions?

Anyone have any thoughts on this?

Thanks,

  • John

Sorry that should have been Barney Boisvert’s - apologies to Barney!

Hi John, here is an undocumented feature for you which should be available once wirebox finalized with AOP. You can declare a handler like this:

function shop(event) transactional=true{

}

Do you see it? The transactional annotation? This proxys the event handler method and surrounds it with the AOP hibernate transaction.

You can use this for any handler or domain object created. This is all done via 1 interceptor, this was or POC for our AOP weaving. You can find it on the SVN or builds as: coldbox.aop.aspects.TransactionAdvise.cfc

You can use this if you like for now, but just remove it from the core and place it as a custom interceptor as most likely things will change, this was our POC. However, once we finalize it, the annotation will stay the same, so it will still work.

Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Yes, I also would not like to be completely bound to Hyrule (no offense), but I think we would have to do some kind of adapters, so we can wire in any validation framework to be used by the services. So once the service is created, you can determine what validation framework to use. This is more scalable and abstract.

Anyways, validations are always picky and different opinions. Maybe we can lay out here a good approach from start to finish. How it should behave, etc. Here is my 2 cents:

I would like to keep my validations for my entities at the entity level via metadata or a nice “constraints” structure that can be composed of entity rules:

constraints = {
propertyName = {
required = true, validate = “email”, etc…
}
}

I would then want to say to my service layer, here is a populated entity, validate it for me:

service.validate(entity=entity,throwExceptions=true/false)

There you can put the flag to either validate and save the validation information or throw exceptions.

The entity if it does not validate it should be evicted from session, just in case. And maybe the validate() method returns a validation Result object or a structure with fields like:
error = true/false
messages = [ array of error messages ]

So if I was putting this together it could look like this from a handler perspective:

var product = service.get(rc.id);
populateModel(product);
var validation = service.validate( entity );
if( NOT validation.error ){
service.save( entity );
}
else{
flash.put(“notice”, “Entity does not validate: #arrayToList( validation.messages )#”)
setNextEvent();
}

Ideas? Thoughts?

Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Here is the ORM Transaction Aspect interceptor.

Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

ORMTransactionAspect.cfc (10.7 KB)

Awesome! Thanks Luis :slight_smile:

I like this approach. A couple of thoughts though;

  • shouldn’t the rules syntax be dependant on the Validation Framework you’re choosing? Otherwise you’ll have to create adapters to translate the rules for each framework, it also means if a framework adds a new rule type, then the translator won’t support it.
  • personally I’d prefer to have an result object instead of a struct. This means that you could potentially extend or substitute it for your own result object. For example with the validatethis framework I have extended the in-built result object so that it has an additional method called renderit() which uses the MessageBox to display the error messages. This is nice as it means I can just do something like this in my view:

<cfif result.hasErrors()>
<cfset result.renderit()>

  • I’m not sure about evicting the entity from the session as you wouldn’t be able to use the “invalid” object to re-populate a form. Yes, you could use the rc scope to get the form field names, but using a object’s getters, to me, is much nicer. Of course that does create an issue with sessions being flushed at the request end, but I think the general consensus is that flushatrequestend should be set to false.

Anyway, those are my initial thoughts - I’m happy to be proved completely wrong!