Wednesday, May 30, 2012

ORM gotchas - Child Mapping Issues

This is another work use-case example. We had a need to use some of the parent/child inheritance that ORM provides using a discriminator column (adobe's docs on using a subclass with discriminator).

The reasoning, is that there were specific types of children that had specific data attributes (it all revolved around a rule engine where the results had a shared set of data points, but depending on the rule type, the actual result data would change dramatically) and so, to avoid having a bunch of null values in the parent table, we were going to create the child tables appropriately.

Here's where it started to get tricky. We needed to map a relationship from the child object to a different table because, well because that relationship existed. Unfortunately, ORM didn't like that...


Here's the real world example:

We have three entities:



The entities are rather silly I know, but they will suffice. Car is our parent class, hyundai is our child, and because in my universe only a hyundai can play music, we have a one to many relationship from music to hyundai.
In this code block, you can see I create an instance of music, one of hyundai (our child class) and persist them to the database.
If I keep running this over and over, I end up with errors:
















You can see it as [undefined array element] could not retrieve collection size: [music.cars #3]. It shows the 3rd array item because that's still loaded into the ormSession so it's aware of it.

So what's going on here?

[localhost]:    select
[localhost]:        count(car_id)
[localhost]:    from
[localhost]:        car
[localhost]:    where
[localhost]:        album_id =?


It's looking for the child column on the parent class! To me this seems like a bug, (and I think I already logged it) but there is a way around it.

Hibernate supports mapping through #entityName#.hbmxml files. You have to manually map it so that the relation to the child class knows that the column exists on the child class, not on the parent. We'll start with the music entity:


You can see where I defined the relationship specifically to the child table.

Then, we have the child class, hyundai:

It's not very elegant, but it's doable.
I also had to modify the music and hyundai entities like so:  

The results:















And if we look at the sql:

[localhost]:    select
[localhost]:        hyundais0_.album_id as album3_695_1_,
[localhost]:        hyundais0_.car_id as car1_1_,
[localhost]:        hyundais0_.color as color1_,
[localhost]:        hyundai1_.car_id as car1_697_0_,
[localhost]:        hyundai1_.[year] as year3_697_0_,
[localhost]:        hyundai1_1_.color as color696_0_,
[localhost]:        hyundai1_1_.album_id as album3_696_0_
[localhost]:    from
[localhost]:        hyundai hyundais0_
[localhost]:    inner join
[localhost]:        car hyundai1_
[localhost]:            on hyundais0_.car_id=hyundai1_.car_id
[localhost]:    left outer join
[localhost]:        hyundai hyundai1_1_
[localhost]:            on hyundai1_.car_id=hyundai1_1_.car_id
[localhost]:    where
[localhost]:        hyundais0_.album_id=?


Much better!

For documentation on creating hbmxml files, see the Hibernate documentation

Special shout out to Ray Camden for developing an awesome Gist creation extension for CFBuilder. Get it here: Gist Creator 

2 comments:

  1. Did you raise a bug for this? I just encountered this myself today. Pretty frustrating...

    ReplyDelete
    Replies
    1. I thought I had but I couldn't find it either. Regardless, Adobe is pretty notorious for crawling when it comes to addressing bugs and since I was able to accomplish what I needed to with the .hbmxml file I moved forward. I agree with a co-worker when it comes to ORM: It makes the easy things easier, and the hard things harder.

      Delete