Search code examples
grailsgrails-3.3

How do I access datasources in ordinary groovy classes in Grails 3?


I'm trying to run sql-code from inside an ordinary Groovy class (no service). In Grails 2 I could access a datasource by doing this:

 public GroovyClass() {
    def ctx = ServletContextHolder.servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
    def dataSource = ctx.getBean('dataSource')
    sql = new Sql(dataSource)
 }

After migrating to Grails 3.3.8 the code no longer works. What is the correct method in Grails 3.3.8?


Solution

  • The "Hollywood Principle" says "Don't call us, we will call you" and that is what dependency injection is about. In your case, don't go get the dataSource, have the dataSource given to you.

    There are a number of ways to do that. See the project at https://github.com/jeffbrown/asdudemo.

    https://github.com/jeffbrown/asdudemo/blob/master/src/main/groovy/ascudemo/helpers/FirstGroovyClass.groovy

    // src/main/groovy/ascudemo/FirstGroovyClass.groovy
    package ascudemo.helpers
    
    import groovy.sql.Sql
    import groovy.util.logging.Slf4j
    import org.springframework.beans.factory.InitializingBean
    
    import javax.sql.DataSource
    
    @Slf4j
    class FirstGroovyClass implements InitializingBean {
        DataSource dataSource
        Sql sql
    
        void logSomeInfo() {
            // both have been initialized
            log.debug "dataSource: ${dataSource}"
            log.debug "sql: ${sql}"
        }
    
        @Override
        void afterPropertiesSet() throws Exception {
            sql = new Sql(dataSource)
        }
    }
    

    https://github.com/jeffbrown/asdudemo/blob/master/src/main/groovy/ascudemo/helpers/SecondGroovyClass.groovy

    // src/main/groovy/ascudemo/SecondGroovyClass.groovy
    package ascudemo.helpers
    
    import groovy.sql.Sql
    import groovy.util.logging.Slf4j
    
    import javax.annotation.PostConstruct
    import javax.sql.DataSource
    
    @Slf4j
    class SecondGroovyClass {
        DataSource dataSource
        Sql sql
    
        void logSomeInfo() {
            // both have been initialized
            log.debug "dataSource: ${dataSource}"
            log.debug "sql: ${sql}"
        }
    
        @PostConstruct
        void initSql() throws Exception {
            sql = new Sql(dataSource)
        }
    }
    

    https://github.com/jeffbrown/asdudemo/blob/master/src/main/groovy/ascudemo/helpers/ThirdGroovyClass.groovy

    // src/main/groovy/ascudemo/SecondGroovyClass.groovy
    package ascudemo.helpers
    
    import groovy.sql.Sql
    import groovy.util.logging.Slf4j
    import org.springframework.beans.BeansException
    import org.springframework.context.ApplicationContext
    import org.springframework.context.ApplicationContextAware
    
    import javax.sql.DataSource
    
    @Slf4j
    class ThirdGroovyClass implements ApplicationContextAware {
        Sql sql
    
        void logSomeInfo() {
            // sql been initialized
            log.debug "sql: ${sql}"
        }
    
        @Override
        void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            DataSource dataSource = applicationContext.getBean('dataSource', DataSource)
            sql = new Sql(dataSource)
        }
    }
    

    https://github.com/jeffbrown/asdudemo/blob/master/grails-app/controllers/ascudemo/DemoController.groovy

    // grails-app/controllers/ascudemo/DemoController.groovy
    package ascudemo
    
    class DemoController {
    
        SomeService someService
    
        def index() {
            someService.logSomeInfo()
            render 'Success'
        }
    }
    

    https://github.com/jeffbrown/asdudemo/blob/master/grails-app/services/ascudemo/SomeService.groovy

    // grails-app/services/ascudemo/SomeService.groovy
    package ascudemo
    
    import ascudemo.helpers.FirstGroovyClass
    import ascudemo.helpers.SecondGroovyClass
    import ascudemo.helpers.ThirdGroovyClass
    
    class SomeService {
    
        FirstGroovyClass firstGroovyBean
        SecondGroovyClass secondGroovyBean
        ThirdGroovyClass thirdGroovyBean
    
        def logSomeInfo() {
            firstGroovyBean.logSomeInfo()
            secondGroovyBean.logSomeInfo()
            thirdGroovyBean.logSomeInfo()
        }
    }
    

    https://github.com/jeffbrown/asdudemo/blob/master/grails-app/conf/spring/resources.groovy

    // grails-app/conf/spring/resources.groovy
    import ascudemo.helpers.FirstGroovyClass
    import ascudemo.helpers.SecondGroovyClass
    import ascudemo.helpers.ThirdGroovyClass
    
    beans = {
        // demonstrates one approach
        firstGroovyBean(FirstGroovyClass) { bean ->
            bean.autowire = 'byName'
        }
    
        // demonstrates another approach
        secondGroovyBean(SecondGroovyClass) { bean ->
            bean.autowire = 'byName'
        }
    
        // demonstrates a third approach
        thirdGroovyBean ThirdGroovyClass
    }
    

    Run the app and send a request to http://localhost:8080/demo/ and that will verify that all 3 approaches worked.

    There are other ways to do this but I hope that one of the above will be helpful to you.

    Best of luck!