Photo from Chile

ColdFusion ORM Gotcha - Non-String Struct Collection Keys

Here's another tricky issue when working with collections of child objects that are stored as structs. Just like last time, we'll look at a Department object with a one-to-many property that holds a collection of Users which is stored as a struct. The difference this time is that we'll use a numeric property as the key to the struct, rather than a string property:

view plain print about
1component persistent="true" output="false" entityname="Department" {
2    property name="DeptId" fieldtype="id" generator="native";
3    property name="Name";
4    property name="SIN" ormtype="int";
5    property name="Users" fieldtype="one-to-many" type="struct"
6        cfc="User" fkcolumn="DeptId" singularname="User"
7        structkeycolumn="SIN" structkeytype="int" cascade="save-update";
8}

The nice thing about having the collection of Users in a struct is that we can access them individually. For example, if we have a Department object and we know we want the User in that Department with a SIN of 12345, we could simply write:

view plain print about
1Department = entityLoad("Department",1);
2User = Department.getUsers()[12345];

If you're like me you'd probably think that would work, but it doesn't.

You'll get an error like "Element 12345 is undefined in a Java object of type class org.hibernate.collection.PersistentMap." That error message is the key, and we'll get back to it in a minute, but for now, just so you can be as frustrated as I was, I'll point out that if I dump out the struct by calling writeDump(Department.getUsers()), I will see what appears to be a standard ColdFusion struct with a key called 12345. So why, I wondered, can't I get at that key? I tried a number of different methods:

view plain print about
1Department = entityLoad("Department",1);
2Users = Department.getUsers();
3User = structFind(Users,12345); // nope
4User = Users[javacast("int",12345)]; // nope
5User = Users[createObject("java","java.lang.Integer").init(12345)]; // nope

Nothing worked! No matter how hard I tried I couldn't get my struct to give me my object. Then I looked at the error message again. It said "Element 12345 is undefined in a Java object of type class org.hibernate.collection.PersistentMap." So I Googled org.hibernate.collection.PersistentMap and found the pertinent page in the Hibernate documentation. Lo and behold, the org.hibernate.collection.PersistentMap has a get() method that accepts a key. So I tried:

view plain print about
1Department = entityLoad("Department",1);
2Users = Department.getUsers();
3User = Users.get(javacast("int",12345));

And it worked! I did some experimenting and it seems like this problem will occur with any datatype used as a structkeytype that isn't a string. It seems like the problem is that whenever you use standard struct syntax (either struct[key] or structFind(struct,key)) the key is first converted to a string, and that's why Java cannot find the member. This also would explain why my attempts at casting the value to a Java Integer didn't help. This seems like a bug to me. Well, maybe not exactly a bug, but an enhancement that really should be there. We should be able to get at a member of a collection using standard struct syntax, without having to resort to calling methods on the underlying Java object.

TweetBacks
Comments
It definitely seems like a bug to me. Be sure to file a report.
# Posted By Raymond Camden | 2/8/10 10:16 AM
And you can't do User = Users["12345"]; ?
# Posted By Adam Presley | 2/8/10 11:49 AM
@Ray: Done.

@Adam: I hadn't tried that, but I just did and encountered the same problem.
# Posted By Bob Silverberg | 2/8/10 5:05 PM
Hmm, ok. Usually quotes work with the direct key references like that. Filing a bug sounds about right.
# Posted By Adam Presley | 2/8/10 5:29 PM