0
votes

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?

2
The accepted answer below is a bad idea. I would not advocate for doing that. The right answer requires knowing more about the context from which you are interacting with this groovy class. Are you interacting with it from a controller? A service? BootStrap? Something else? - Jeff Scott Brown
@JeffScottBrown From a Grails service - ascu

2 Answers

2
votes

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!

0
votes

I just tested this and it seems to give the datasource

def ds = Holders.grailsApplication.mainContext.getBean('dataSource')
println "DataSource: ${ds}"  ---> DataSource: org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy@5e91ade8

Didn't try any operations on it, but that looks right.