Lightwire and transient issues

7 views
Skip to first unread message

garrettjohnson

unread,
Sep 14, 2009, 10:58:51 AM9/14/09
to CFCDev
I am trying out lightwire for the first time and I am noticing a small
problem when it comes to returning an array of beans from my DAO. It
seems that its my array is an array of the same exact bean, rather
then multiple instances of my bean.

I have the following in my bean config:

addSingleton("educationService", "educationService");
addSingleton("educationDAO", "educationDAO");
addSingleton("educationGateway", "educationGateway");
addTransient("highschool", "highschoolBean");

addSetterDependency("educationDAO", "highschoolBean");
addConstructorDependency("educationService", "educationGateway");
addConstructorDependency("educationService", "educationDAO");

then in my DAO layer:

<cffunction name="read" access="public" returntype="any"
output="false" hint="I return a populated an arrray of highschool
objects">
<cfargument name="ID" type="numeric" required="true" />

<cfset var q = "" />
<cfset var objects = arrayNew(1) />

<cfquery datasource="#getSettings().getDatasource()#"
name="q">
<!--- some query --->
</cfquery>
<!--- populate highschool Beans --->
<cfloop query="q">
<cfset objects[q.currentRow] = getHighschoolBean().init
(q.highschoolID, q.graduationDate, q.highschoolGPA,
q.highschoolClassRank, q.highschoolClassSize, q.highschoolPercentile,
q.dateUpdated) />
</cfloop>

<cfreturn objects />
</cffunction>


Now I do not get an error or anything but if my query return 3
records, lets say Foo high, Woo high, Moo high... then when I call my
code in my cfm template.... loop array... #school.getName()#..... It
will return Moo high 3 times, rather then what it should really be
doing!


Any thoughts?

Peter Bell

unread,
Sep 14, 2009, 11:29:39 AM9/14/09
to cfc...@googlegroups.com
Hi Garrett,

I'd be surprised if that is the issue. I can't see your
getHighSchoolBean() method, but if it's calling
getBean("highschoolbean") (or getTransient("highschoolbean") (either
is fine) then it should work just fine as LW *always* does a new
create object for transients. I'd check you haven't elsewhere done an
addSingleton("highschoolbean") (don't what would happen but it could
break things) and I'd also double check the query, etc. Also, feel
free to look at the lightwire code - if doesn't do much so it's pretty
easy to understand and debug.

I separately notice you're manually calling init on your bean - but
LightWire also calls that, so I'd consider having a separate load
method for your bean for loading up the data.

If you *really* think the problem is in lightwire, create the simplest
possible test case where you have code something like:
<cfscript>
HSBean1 = getBean("highschoolbean").load(title="title1");
HSBean2 = getBean("highschoolbean").load(title="title2");
</cfscript>

HSBean1 Title = #HSBean1.getTitle()#<br/>
HSBean2 Title = #HSBean2.getTitle()#<br/>

If that code DOES break, post sample code to the list including a copy
of a bean with a load method and a getTitle method and I'll test, but
I'd be pretty surprised if it did.

Best Wishes.
Peter

garrettjohnson

unread,
Sep 14, 2009, 2:22:13 PM9/14/09
to CFCDev
Peter,

Thanks for the help! I do not think it has anything to do with
LightWire, more like my error!

Your tests work fine...

So:

I inject my transient bean into my DAO via addSetterDependency(bean,
dao)... I then defined my setHighschoolBean to take in my argument
from the injector, so the getHighschoolBean() just returns that
variable that gets set in the setter.

Is that the correct way to inject a the transient into a singleton ?

Jared Rypka-Hauer

unread,
Sep 14, 2009, 2:44:56 PM9/14/09
to cfc...@googlegroups.com
I'm watching this with some interest because (I think) the terminology
seems a bit off.

Generally something that's "injected" lasts the lifetime of the object
it's being injected into... so injecting a transient into a singleton
_seems_ like the wrong way to go about things. I am interested to see
how this works out. If you're actually injecting the transient into
the DAO, that could explain why you're only getting one instance in
your resulting array. CFCs are passed by reference, and when you
inject a bean into an instance of a DAO, you only really have 1 bean,
no matter how many "pointers" you have... you could have an array of
1000 beans all pointing back to the same place in memory that holds
one copy of a bean. Ultimately it means that calling
getHighschoolBean() will only ever touch one physical bean in RAM and
every time you change it, every bean in the array will reflect those
changes.

You need to be creating a copy of the bean on every iteration of the
cfloop, maybe like this:

<cffunction name="read" access="public" returntype="any"
output="false" hint="I return a populated an arrray of highschool
objects">
<cfargument name="ID" type="numeric" required="true" />

<cfset var q = "" />
<cfset var objects = arrayNew(1) />

<cfquery datasource="#getSettings().getDatasource()#" name="q">
<!--- some query --->
</cfquery>
<!--- populate highschool Beans --->
<cfloop query="q">
<cfset objects[q.currentRow] = duplicate(getHighschoolBean()).init(
q.highschoolID, q.graduationDate, q.highschoolGPA,
q.highschoolClassRank, q.highschoolClassSize, q.highschoolPercentile,
q.dateUpdated
) />
</cfloop>

<cfreturn objects />
</cffunction>

Note the addition of the duplicate() function to the line that sets a
bean to objects[q.currentrow]... that'll mean you get a new bean,
instead of the same bean over and over again.

Also, I notice that in your most recent email you say you're calling
addSetterDependency(bean,DAO), but in the sample code in your first
email you are calling
addSetterDependency("educationDAO","hischoolBean"), which is either
backwards or forwards, depending on how the method is actually set up.
If I'm reading the code correctly, your sample code is correct, but
it's worth checking on.

J

Peter Bell

unread,
Sep 14, 2009, 2:46:13 PM9/14/09
to cfc...@googlegroups.com
You generally don't inject transients into singletons. DI of
singletons is usually around injecting SIngletons into Transients -
for example having a User transient with a DAO or a Validator
singleton injected into it.

Usually if you DAO want's to return transients it's going to do
something like:

DAO.cfc

function getById ( Id ) {
var UserProperties = functionthatdoesthesql( Id );
var User = beanFactory.getTransient("User");
User.load( UserProperties );
return User;
}

Note the important points:
- var scope everything for thread safety in your singletons
- Always call the bean factory every time you want a bean. If you want
to loop over an array and create a bean for each of 200 records,
you're going to want to call beanfactory.getTransient() 200 times. If
you call it once, you only get 1 bean.

Best Wishes,
Peter

Peter Bell

unread,
Sep 14, 2009, 2:48:12 PM9/14/09
to cfc...@googlegroups.com
+1 to what Jared said. Two options. Call getBean() for each record in
the loop or use duplicate(). Either is fine, and if it becomes a
bottleneck, run load testing on both approaches to see which is
fastest for your particular beans and environment.

Best Wishes,
Peter

Jared Rypka-Hauer

unread,
Sep 14, 2009, 2:56:40 PM9/14/09
to cfc...@googlegroups.com
I'm gonna take a wild stab at this and guess that duplicate() is going
to be faster in this case, mostly because it's a built-in that
operates directly on the bean... so it's immediately available and
circumvents all the LightWire plumbing, minimal as it may be.

It's actually a decent performance booster trick: inject a copy of the
bean you want an array of and then call duplicate against that one
bean rather than going back to the factory or to createObject() for it
every time.

J

Peter Bell

unread,
Sep 14, 2009, 2:59:35 PM9/14/09
to cfc...@googlegroups.com
Got a feeling you're right!

garrettjohnson

unread,
Sep 14, 2009, 3:12:37 PM9/14/09
to CFCDev
Woah guys!

Thanks for the tips, duplicate() did the trick!

Thanks Peter for LightWire, I am really enjoying it... it seems to be
very fast and the best choice for straight up injections.
Reply all
Reply to author
Forward
0 new messages