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?
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.
// 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)
}
}
// 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)
}
}
// 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)
}
}
// 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!