On 11/02/2010 10:14 AM, Ian Springer wrote:
Yeah, I don't like it either. I was just trying to avoid API
changes.
But since that method is only in the local interface, I guess I can add
a new method, or methods, with less cryptic semantics, and then
deprecate, or even just remove, this one.
The root issue here is that we're
trying to weave a couple of difference
use cases into a very concise API:
1) enable the passed metric templates, setting their collection
intervals to the passed value
2) enable the passed metric templates, keeping collection intervals to
their existing values
3) disable the passed metric templates (which implies keeping collection
intervals to their existing values)
Arguably, a user might also want to change the collection intervals
without affecting the enabled/disabled bit. If so, both arguments types
would be optional, which makes using wrapper classes rather natural:
MeasurementScheduleManagerBean {
updateMeasurementDefinitions(..., Long collectionInterval, Boolean
enabled) {
// if collectionInterval not-null, validate it, then include it
in the update-statement
// if enabled not-null, include it in the update-statement
}
}
But that strategy has its own drawbacks:
1) several different permutations of the query need to exist (in this
case 4 difference versions, because each of the two arguments here can
be in 2 fundamentally different states [null or not null])
2) a base query must exist, which is then (via string concatenation)
dynamically augmented/rewritten to account for non-null arguments
-----
I would like us to look into the possibility of using a criteria-based
solution here, whose mantra from day-1 has been the optionality of
arguments. Something like:
MeasurementDefinitionCriteria {
List<Integer> filterMeasurementDefinitionIds;
...
other existing filters, which help narrow down the list of
definitions to update
...
// new part of the API
Long updateCollectionInterval; // if non-null, set the
collectionInterval
Boolean updateEnabled; // if non-null, override enabled/disabled bit
}
Here, we've enhanced the criteria API to include new parameter types,
those with the naming convention "updateXXX". If any of those are set,
then the CriteriaQueryGenerator will produce an update-statement
(instead of a select-statement), whose non-null "updateXXX" values
generate the list of fields to update on the corresponding entity.
MeasurementScheduleManagerBean {
int updateMeasurementDefinitionsByCriteria(subject, criteria) {
// check authorization to ensure caller has permission to update
monitoring defaults
CriteriaQueryGenerator generator = new
CriteriaQueryGenerator(subject, criteria);
// potentially add authz fragments to the generated query, to
restrict the update list
CriteriaQueryRunner<MeasurementDefinition> queryRunner = null;
queryRunner = new CriteriaQueryRunner(criteria, generator,
entityManager);
return queryRunner.executeUpdate();
}
}
Notice that a new method would be added to the CriteriaQueryRunner
class, executeUpdate(). When the criteria object is not selecting
objects (requiring PageList<T> to be returned) the executeUpdate()
method can be called which will return the count of affected rows instead.
The benefit with this type of solution is that it enables users to
perform finely-tuned mass updates. Now, the same Criteria object that
was able to flexibly find MeasurementDefinition objects (today you can
search for definitions by id, name, displayName, description,
resourceTypeName, resourceTypeId, measurementCategory, measurementUnits,
nmericType, dataType, displayType, enabledDisabled bit, and current
collectionInterval), and take what it finds and set new values for fields.
So with just a little work at the CriteriaQueryGenerator and
CriteriaQueryRunner layers, we have a powerful solution that can be used
to deprecate a wide variety of existing methods across many of our SLSBs.
Note: a criteria-based solution could use the same style to perform
deletes as well:
SomeManagerBean {
int deleteSomethingByCriteria(subject, criteria) {
// check authorization to ensure caller has permission to delete
something
CriteriaQueryGenerator generator = new
CriteriaQueryGenerator(subject, criteria);
CriteriaQueryRunner<SomeThing> queryRunner = null;
queryRunner = new CriteriaQueryRunner(criteria, generator,
entityManager);
return queryRunner.executeDelete();
}
}
Again, the benefit is that it exposes the familiar, fine-grained search
users got from the criteria filters...but now (instead of just returning
entities across the wire) the criteria framework can update and delete
stuff too.