Twitter: raymondcamden


Address: Lafayette, LA, USA

Some recent ColdFusion ORM Pain

04-09-2013 5,416 views ColdFusion 17 Comments

I'm working on a relatively simple ColdFusion ORM application. It is focused around a core entity type called Content. This entity type is rather large, around 50 or so properties. As you can imagine, some of the properties are simple, some are many-to-one, and some are many-to-many. I ran into some very frustrating issues though and I thought I'd share them.

Invoke

The first issue I ran into was a bit of code I used to set a set of simple values. I've got a bunch of values that don't (really) need any validation and can simply be copied from a structure directly into the entity. So I created a simple list and iterated over it to set my values.

Simple, right? But note the use of invoke. This is a ColdFusion 10 addition that lets you call dynamic methods in CFCs. I noticed that none of my data was actually persisting. Why?

Turns out - in order for my calls to setX (where X is a entity method) to work, I have to pass the value as a structure. Here is the modification:

Why? I've got no freaking clue. To be fair, the docs show passing a structure of arguments, but they don't explicitly state that you must do this. Even worse, an error was never thrown when I passed the argument as a simple value instead. I hate errors that are ignored.

Error Reporting

The second issue was much, much worse. Not that it was difficult to fix once I knew what the issue was, but the problem was in how the issue was reported.

As I mentioned above, my entity has simple properties, many-to-one properties, and a many-to-many. I began by coding in the simple properties (using the technique I described above). I then did my many-to-one. I then did the many-to-many.

I noticed that my join table was not populating. I didn't get an error though. What makes this more frustrating was that I could dump my entity before the save operation and clearly see my related data in the entity. Yet I'd run the save, it would insert a new record into the core table, and just... do nothing else. Again - no damn error. Anywhere.

So I backed up a bit. First I decided - let's turn off the auto flush feature and use transactions. I didn't think that would help per se, I just decided to give it a try. All of a sudden I got something - an error:

coldfusion.orm.PersistentTemplateProxy cannot be cast to java.util.Collection

Ok... so.... first off. Why would ColdFusion persist the entity, have a problem, and just not report it until I started handling the flush myself with a transaction? I can't imagine any reason why that would make sense.

Here's where things got even more interesting. I commented out the code handling the many-to-many and I still got the error!

On a whim, I decided to comment out the code handling 3 of my many-to-one properties. All of a sudden, that fixed the issue. I then uncommented out the many-to-many and it still worked fine (and persisted data to the join table). So obviously my issue was in the many-to-one blocks.

When you work with relationships, you're supposed to ensure you set both sides of a relationship. You can sometimes get by without, but you really shouldn't. In order to make this simpler, you can simply use some custom code in the core entity and have it do both sides of the relationship. So for example, here is one of those methods:

Simple, right? In my content entity I call setSegment. The method handles the logic of translating an ID to an entity, setting it, and doing the reverse side. But something in here was wrong.

At no time, though, was I told what was wrong. Outside of the error message I pasted above, I was lost.

Finally I started commenting the lines in that method and got it down to this:

segment.setContent(this);

And then it hit me. On the content side, it has one segment. On the segment side, it has many content entities.

So right away I've learned that the built-in methods are apparently not validating for the cases when an array is required.

Ugh.

17 Comments

These comments will soon be imported into Disqus. To add a comment, use Disqus above.
  • Jim #
    Commented on 04-09-2013 at 11:58 AM
    I pretty much stopped using ORM in Coldfusion for such reasons. I found it frustratingly difficult to figure out what an error message was telling me, or lack thereof. I got a few things working, but it just took an incredible amount of time. As soon as you get into more complex relationships it gets really harry.

    We don't require database vendor independence so I just created my own way of doing things with queries.
  • MikeZ #
    Commented on 04-09-2013 at 1:09 PM
    I'm with Jim, though for slightly different reasons.
    While plain Hibernate is a pure godsend in a Java environment, the ORM integration in CF can be such a pain at times that I'm also moving away from it. And don't even get me started on how useful easy access to Envers can be.
  • Misty #
    Commented on 04-09-2013 at 1:44 PM
    Me too :),

    I started building a Legder Application with ORM but have to come back to CFC and Queries due to its undoubtedly complexity
  • Commented on 04-10-2013 at 1:33 PM
    I agree the CF ORM integration can be difficult to work with in the beginning, but once you get the hang of it, the productivity gain is amazing.

    When it comes to debugging, please remember that the actual SQL queries are not run by Hibernate until the Hibernate session is flushed. ColdFusion flushes the Hibernate session on request end by default, after the HTTP response is sent back to the client (usually a browser). I think this is why Ray did not see any error message at first. During debugging it can be helpful to flush the ORM session manually using OrmFlush(). Also, setting this.ormsetting.logsql to true in Application.cfc can be a lifesaver.
  • Commented on 04-10-2013 at 4:39 PM
    Oh fascinating Martijn. So would it have been logged anywhere then? To be clear, I had turned on logsql. It showed nothing. Well, it showed inserts, but no errors.
  • Salvatore fusto #
    Commented on 04-11-2013 at 4:51 AM
    Well,
    i was just considering to move to CF ORM, from plain queries and dataMgr, but this article and comments made me turn back, so thanks very much!!
  • Commented on 04-11-2013 at 6:54 AM
    Well, I wouldn't avoid ORM. It just can be a bit tricky at times. Plus I'm a bit rusty. It has been - possibly - a good whole year since I used it.
  • Salvatore fusto #
    Commented on 04-11-2013 at 8:21 AM
    Neither tricky nor rusty, sir. Surely we are not the only peoples that does not like, or even hate, cf orm: there are many many others.
  • Jim #
    Commented on 04-11-2013 at 12:23 PM
    To log the full queries and errors for ORM, see the following:

    http://www.rupeshk.org/blog/index.php/2009/07/cold...
  • Commented on 04-11-2013 at 12:25 PM
    To be clear Jim, I was doing this. It didn't help. (Well, outside of confirming it wasn't doing the inserts.)
  • Commented on 04-11-2013 at 2:04 PM
    Hi Ray,

    In Railo the ORM exceptions are logged in a file called orm.log. I can image it's the same for Adobe ColdFusion. Could you check that?

    For me it usually gives me useful information like:

    "Cannot delete or update a parent row: a foreign key constraint fails ....."
  • Commented on 04-14-2013 at 3:00 AM
    Hi Ray,

    Just FYI, this should also work: invoke(content, "set#li#", [data[li]]);

    A benefit of the new argument array support is that unnamed arguments maintain their order, even when the function signature defines arguments but the argument array length exceeds the number of defined arguments.

    Just like w/ superfluous struct keys, the entity's dynamic setter will ignore any superfluous array elements.

    Thanks!,
    -Aaron
  • Commented on 04-14-2013 at 10:02 AM
    Cool, thanks for sharing that Aaron.
  • papichulo #
    Commented on 04-22-2013 at 8:28 PM
    I got away from CF ORM and datamgr all together. Been a happy cfwheels user for two years and I've never looked back. Cfwheels has it's own ORM, and you should check out how easy you can handle nested properties http://cfwheels.org/docs/1-1/chapter/nested-proper...
  • Commented on 04-24-2013 at 12:30 AM
    Hi Ray,

    You're very welcome. That was a very small piece of info compared to what I've learned from your blog over the years. So I'm glad that it'll possibly help.

    Thanks!,
    -Aaron
  • Commented on 09-11-2014 at 4:10 PM
    I just ran into this issue ("coldfusion.orm.PersistentTemplateProxy cannot be cast to java.lang.String") because I was attempting to set an entity into a string column (duh). Just wanted to add that to this record for other people and probably future me. :)
  • Michael Zock #
    Commented on 09-11-2014 at 8:07 PM
    Adam, there's actually a Hibernate feature along those lines: http://256.com/gray/docs/misc/hibernate_lazy_field...