class BuggyTagLib {
def applicationData = { attrs ->
...
out << "<div class='u'>${campaign.betaTesters.size()}</div>"
...
}
}
class Campaign {
static hasMany = [ betaTesters: CampaignUser ]
int betaTestersCount
static mapping = {
betaTestersCount formula: '(select count(*) from campaign_betatesters b where b.campaign_id = id)'
}
}
class Campaign {
static hasMany = [ betaTesters: CampaignUser ]
int getBetaTestersCount() {
betaTesters == null ? 0 : withSession {
it.createFilter(betaTesters, 'select count(*)').uniqueResult()
}
}
}
class BootStrap {
def init = { injectMappedCollectionCountMethod() }
private void injectMappedCollectionCountMethod() {
def classesWithCollections = grailsApplication.domainClasses.findAll { it.hasProperty('hasMany') }.collect { it.clazz }
classesWithCollections.each {
it.hasMany.each { colName, colClass ->
String countMethodName = 'get' + colClass.subString(0,1).toUppercase() + colClass.subString(1) + 'Count'
it.metaClass."$countMethodName" << { ->
withSession { session ->
delegate."$colName" == null ? 0 :
session.createFilter(delegate."$colName", 'select count(*)').uniqueResult()
}
}
}
}
DON'T REPEAT YOUR FAILURES
¿Puedo mejorar el proceso de desarrollo para que este error no vuelva a producirse?
Puede tratarse de cualquier restricción (por simple que sea) incorporada al diseño de un proceso para evitar errores o detectarlos lo antes posible.
"AOP was invented to be a move away from explicit metaprogramming to a direct semantics for some of the kinds of problems metaprogramming could be used for. It helps to raise abstraction level."
import org.codehaus.groovy.runtime.callsite.Callsite;
import org.hibernate.collection.PersistentCollection;
public aspect GormCollectionSize extends AbstractPokaYoke {
public pointcut violation():
sizeOnPersistentCollection(Object, Callsite);
public sizeOnPersistentCollection(Object receiver, Callsite callsite):
GroovyPointCuts.groovyCall(receiver, callsite) &&
if ((receiver instanceof PersistentCollection) &&
("size".equals(callsite.getName())));
}
public aspect GroovyPointCuts {
public pointcut groovyCall(Object receiver, CallSite callsite):
call(public Object CallSite.call*(..)) &&
target(callsite) &&
args(receiver);
}
public abstract aspect AbstractPokaYoke implements PokaYoke {
public abstract pointcut violation();
public abstract String getDescription();
before(): violation() {
warn(thisJoinPoint);
}
...
}
Be careful when calling an annotated method within a service when the annotation settings are different.
Because you're underneath the proxy it's a direct method call, and any checks that the proxy would have done will be bypassed.
@Trasactional
void someMethod() {
anotherMethod()
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
void anotherMethod() {
...
}
public aspect ServiceTransactionalServiceBypassed extends AbstractPokaYoke {
public pointcut violation():
proxyBypassed(Object, Transactional, Object, Transactional);
public pointcut proxyByPassed(Object caller, Transactional callerAnnotation,
Object callee, Transactional calleeAnnotation):
cflowbelow(annotatedPublicServiceMethod(caller, callerAnnotation)) &&
annotatedPublicServiceMethod(callee, calleeAnnotation) &&
if ((caller.getClass() == callee.getClass()) &&
(!callerAnnotation.equals(calleeAnnotation)) &&
(!isCalledThroughProxy()));
...
}
public boolean isCalledThroughProxy() {
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
StackTraceElement callerElement = elements[3];
return ClassUtils.isCglibProxyClassName(callerElement.getClassName());
}
public pointcut annotatedPublicServiceMethod(
Object receiver, Transactional ann):
GrailsPointCuts.publicServiceMethod() && this(receiver) && @annotation(ann);
public aspect GrailsPointCuts {
public pointcut inService(): within(..*Service);
public pointcut publicServiceMethod():
inService() && execution(public * *.*(..));
...
}
public aspect GormLayeringViolation extends AbstractPokaYoke {
public pointcut violation():
GrailsPointCuts.isDomain() &&
GormPointCuts.gormMethod() &&
!cflowbelow(GrailsPointCuts.isService());
}
public pointcut gormMethod():
saveMethod() || ...;
public pointcut saveMethod():
execution(public Object save()) ||
execution(public Object save(java.util.Map)) ||
execution(public Object save(boolean));
public pointcut isDomain():
@within(grails.persistence.Entity);