Fetching record counts.
Fetching record counts.
- Subject: Fetching record counts.
- From: Marek Wawrzyczny <email@hidden>
- Date: Tue, 1 Feb 2005 13:33:11 +1100
Hi all,
A long time ago I came across Eric Noyau's famous BugTracker, and the
useful FetchCounting class. Since then we used something similar to
perform counts in our application, however recently it has become the
source of a problem. The source is at the end of the file.
I have to admit, I don't fully understand how it works, but essentially
it appears that the target entity is retrieved from the fetch
specification. Then a additional "count" attribute is added to the
model to perform the count.
There are two problems with that solution we have encountered, the
first one results in the following exception:
"Exception while trying to save the EOEditingContext during the
processing of transaction... Stack trace:
java.lang.IllegalArgumentException: addAttribute: duplicated attribute
name: p_objectCountAttribute
at com.webobjects.eoaccess.EOEntity.addAttribute(EOEntity.java:2153)
at
ish.common.webobjects.utilities.EOFetchCounting.objectCountWithFetchSpec
ification(EOFetchCounting.java:61)
<...>"
I believe that this is caused due to the variable
"_objectCountAttribute" being static. I think that two different
threads are attempting to perform the fetch count 'simultaneously' on
the same entity. One has already added the "count" attribute when the
other attempts to do this again.
The second exception we get is:
"java.lang.IllegalArgumentException: addAttribute: attribute has a
different entity
at com.webobjects.eoaccess.EOEntity.addAttribute(EOEntity.java:2149)
at
ish.common.webobjects.utilities.EOFetchCounting.objectCountWithFetchSpec
ification(EOFetchCounting.java:61)
<...>"
Here I believe two threads are attempting to perform fetch counts on
different entities. Since the static "_objectCountAttribute" can only
be linked to one entity at a time, we get the exception when the second
thread attempts to add the attribute before the first thread had the
opportunity to remove it from its entity.
This is actually where I am stuck. I don't understand the EOModel and
EOAttribute mechanism very well. One solution I tried was to add the
count attribute permanently, that crashed the application. So I tried
creating the attributes on the fly and removing them once finished this
worked. However, it doesn't appear I can make this thread-safe since
there is no locking mechanism on the EOModel (I think this is why
Eric's solution used a static var) and I cannot easily test this
concept under duress (when the exceptions actually start occurring).
Can anyone point me in the right direction on this?
Marek Wawrzyczny
software engineer
-------------------------->
ish group pty ltd
http://www.ish.com.au
7 Darghan St Glebe 2037 Australia
phone +61 2 9660 1400 fax +61 2 9660 7400
<<< Code follows >>>
package ish.common.webobjects.utilities;
import com.webobjects.foundation.*;
import com.webobjects.eocontrol.*;
import com.webobjects.eoaccess.*;
/**
* <p>This class impements methods that allow us to count the number of
* rows for a user defined fetch specification.</p>
*
* <p>The count is performed directly on the database source without
having to load
* them into memory.</p>
*
* <p>Original code by Eric Noyau. This class has been taken from
Eric's Bugtracker application.</p>
*
* @author Eric Noyau
* @author Marek Wawrzyczny
* @version 1.1
*/
public class EOFetchCounting extends Object {
private static EOAttribute _objectCountAttribute = null;
/**
* <p>Returns the number of rows metching the fetch
specifivation.</p>
*
* @param editingContext - editing context to perform the fetch in
* @param fetchSpecification - the fetch specification
*/
public static Number objectCountWithFetchSpecification(
EOEditingContext editingContext,
EOFetchSpecification fetchSpecification) {
if (fetchSpecification != null) {
EOFetchSpecification rawFetchspecification;
try {
rawFetchspecification =
(EOFetchSpecification)fetchSpecification.clone();
} catch (Exception e) {
System.out.println("\nException in
objectCountWithFetchSpecification :\n"+e);
return null;
}
EOEntity entity =
EOModelGroup.defaultGroup().entityNamed(rawFetchspecification.entityName
());
EOQualifier schemaBasedQualifier =
entity.schemaBasedQualifier(rawFetchspecification.qualifier());
EOAttribute attribute =
EOFetchCounting.objectCountAttribute();
NSArray results = null;
entity.addAttribute(attribute);
// Fix to ensure that fetch counting does not trip the site
when
// the fetch fails for some reason
try {
rawFetchspecification.setQualifier(schemaBasedQualifier);
rawFetchspecification.setRawRowKeyPaths(new
NSArray(attribute.name()));
results =
editingContext.objectsWithFetchSpecification(rawFetchspecification);
} finally {
// IMPORTANT!!!
// This needs to run every time, exception or not
entity.removeAttribute(attribute);
}
if ((results != null) && (results.count() == 1)) {
NSDictionary row = (NSDictionary) results.lastObject();
return (Number)row.objectForKey(attribute.name());
}
}
return null;
}
/**
* Returns the number of rows metching the fetch specifivation.
*
* @param editingContext - editing context to perform the fetch in
* @param entityName - the entity to perform the count on
* @param fetchSpecification - the fetch specification
* @param bindings - the fetch bindings
*/
public static Number objectCountWithFetchSpecificationAndBindings(
EOEditingContext editingContext,
String entityName,
String fetchSpecName,
NSDictionary bindings) {
EOModelGroup modelGroup = EOModelGroup.defaultGroup();
EOFetchSpecification unboundFetchSpec;
EOFetchSpecification boundFetchSpec;
unboundFetchSpec =
modelGroup.fetchSpecificationNamed(fetchSpecName, entityName);
if (unboundFetchSpec == null) {
return null;
}
boundFetchSpec =
unboundFetchSpec.fetchSpecificationWithQualifierBindings(bindings);
return objectCountWithFetchSpecification(editingContext,
boundFetchSpec);
}
/**
* <p>Private.</p>
*/
private static EOAttribute objectCountAttribute() {
if ( _objectCountAttribute == null ) {
_objectCountAttribute = new EOAttribute();
_objectCountAttribute.setName("p_objectCountAttribute");
_objectCountAttribute.setColumnName("p_objectCountAttribute");
_objectCountAttribute.setClassName("Number");
_objectCountAttribute.setValueType("i");
_objectCountAttribute.setReadFormat("count(*)");
}
return _objectCountAttribute;
}
}
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden