tags:

views:

541

answers:

3

The Grails documentation defines a "count" static method, defined in the documentation like this:

Description

    Counts the number of instances in the database and returns the result

Parameters

    None

Example

    def noOfBooks = Book.count()

However, whenever I call it, I get this error! I simple added a call to the name of my Domain Class (Company) like this to a working Controller

 def companies = Company.count()

and when it exectutes that line, it blows up with the following error

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pluginManager' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.RuntimeException: Unable to locate constructor with Class parameter for class org.codehaus.groovy.grails.commons.DefaultGrailsControllerClass

Other, working, code in the controller (pre-generated with the static scaffolding commands) access Company.get(...) etc, with no error

What am I doing wrong?

A: 

I just discovered something:

THIS works:

class HomeController {
    def companies = 0

    def index = {
        companies = Company.count()
        render(view:"index")
    }
}

THIS blows up:

class HomeController {
    def companies = Company.count()

    def index = {
        render(view:"index")
    }
}

With a runtime error of:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pluginManager' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.RuntimeException: Unable to locate constructor with Class parameter for class org.codehaus.groovy.grails.commons.DefaultGrailsControllerClass

Why?

ראובן
+1  A: 

The short answer is that the count() method and many others are added to the metaClass at runtime after the Hibernate Plugin is loaded. count() isn't a static method that is available at compile time as Java does it, but rather a static method that is added to the Domain's metaClass at runtime (apparently after parsing the controllers).

Why? Because it doesn't make sense to have count(), get() or read(), etc. methods until after Hibernate/GORM is initialized. It would not be hooked up to the datasource and the methods would be invalid.

I can't help but wonder why you would need to put a property like that on a controller. As soon as you save a new Company or delete one - your companies count would be off.

The long answer would be diving into Grails' source, From the GORMNamespaceHandler -> GORMSessionFactoryDefinitionParser -> GORMEnhancingBeanPostProcessor -> HibernatePluginSupport -> HibernatePluginSupport.addBasicPersistenceMethods()

Colin Harrington
Thanks for the answer! I'm just getting started with Groovy/Grails. I have no prior Java experience (I've avoided it for philosophical reasons) but have written large systems with every other dynamic language from Scheme to Erlang, in addition to a lot of old-school C/C++ experience.There's a lot of behind-the-scenes magic in Groovy/Grails that makes things very confusing. (It would also be nice if it were a true dynamic language and I could pop open a shell inside my environment, like Python/Django lets me do, and try things out...)
ראובן
+1  A: 

This

class HomeController {
    def companies = Company.count()

    def index = {
        render(view:"index")
    }
}

Fails because as lael pointed out the count() method is not available when the application is started. The count() method is a Dynamic method which GORM addeds to the domain classes. Spring (the underlying framework) creates an object for every artifact (Controller/Service/Domain Class etc) at start up. GORM would be after this.

Spring is trying to create a HomeController class, the construction of Home Controller will assign the value of company.count to Companies at startup, However GORM has not started yet so the dynamic methods have not been added.

This code is incorrect anyway, if it did work then the companies variable would only have the value of the number of companies at start up of the application. Once you get "used" to groovy and grails I think you will appreciate the speed of development.

Hope this helps.

Scott Warren