How to I bind a Hibernate Session to a thread in Grails?
Asked Answered
H

4

18

I'm writing a multi-threaded application in Grails and the additional threads need access to GORM/Hibernate. When they try to access GORM I get the error "org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here".

OK fair enough, can someone guide me on the best way to set the threads up to have access? The error message almost sounds like you just need to change some config options yet I sense, it is not so simple...

Hogshead answered 24/8, 2010 at 12:48 Comment(0)
P
14

There is a bean in Grails applications called “persistenceInterceptor” that can be used for this.

See this example from the JMS plugin on how to use it:

http://github.com/gpc/grails-jms/blob/master/src/groovy/grails/plugin/jms/listener/adapter/PersistenceContextAwareListenerAdapter.groovy#L21

Here is the interface:

https://github.com/grails/grails-core/blob/master/grails-core/src/main/groovy/grails/persistence/support/PersistenceContextInterceptor.java

And Hibernate impl:

https://github.com/grails/grails-data-mapping/blob/master/grails-datastore-gorm-support/src/main/groovy/org/grails/orm/hibernate4/support/HibernatePersistenceContextInterceptor.java

Polyphony answered 25/8, 2010 at 5:29 Comment(1)
Second two links are broken, did the location change?Rhineland
H
14

You need to put any GORM calls in a withTransaction closure. An example taken from a discussion of multi threading at https://fbflex.wordpress.com/2010/06/11/writing-batch-import-scripts-with-grails-gsql-and-gpars/

Single threaded

user = User.findByUsername( photo.username )

multi threaded

User.withTransaction{
user = User.findByUsername( photo.username )
}
Hedda answered 24/8, 2010 at 13:7 Comment(1)
This is probably the easiest way for the occasional thread usage.Bechuanaland
P
14

There is a bean in Grails applications called “persistenceInterceptor” that can be used for this.

See this example from the JMS plugin on how to use it:

http://github.com/gpc/grails-jms/blob/master/src/groovy/grails/plugin/jms/listener/adapter/PersistenceContextAwareListenerAdapter.groovy#L21

Here is the interface:

https://github.com/grails/grails-core/blob/master/grails-core/src/main/groovy/grails/persistence/support/PersistenceContextInterceptor.java

And Hibernate impl:

https://github.com/grails/grails-data-mapping/blob/master/grails-datastore-gorm-support/src/main/groovy/org/grails/orm/hibernate4/support/HibernatePersistenceContextInterceptor.java

Polyphony answered 25/8, 2010 at 5:29 Comment(1)
Second two links are broken, did the location change?Rhineland
A
3

withNewSession will also work. In my case, I have low priority updates where the last update can always "win". version: false is also important here in order to avoid the StaleObjectException:

     Thread.start {
        try {
            Widget.withNewSession {
                xxx()
                log.info "Asynchronously did some updates."
            }
        } catch (Exception ex) {
            log.error "Failed to asynchronously do something...", ex
        }
    }
Alum answered 9/2, 2017 at 4:16 Comment(1)
This is the answer I was looking for. Way better than using withTransaction if you're making some updates in the background that you want to appear as they're being made.Aircrew
T
1

Luke Daley gave the right answer. Unfortunately, the links have changed. Thus, I'll update his answer and provide a code example to make this answer self-contained.

There is a bean in Grails applications called persistenceInterceptor that can be used for initializing the persistence context / session for Hibernate. You can inject the bean into one of your controller / service classes and start a new thread, e.g. using the following code snippet.

class YourControllerOrService {
    PersistenceContextInterceptor persistenceInterceptor
    
    def someOperation() {
        ...
        Runnable yourTask = { ->
            try {
                if (persistenceInterceptor) {
                    persistenceInterceptor.init()
                }
            
                // execute the hibernate operations here in a transaction,
                // e.g. call a method annotated with @Transactional
                ...
            } catch (Exception e) {
                    log.error('Your error message', e)
            } finally {
                if (persistenceInterceptor) {
                    persistenceInterceptor.flush()
                    persistenceInterceptor.destroy()
                }
            }
        }
        Thread workerThread = new Thread(yourTask)
        workerThread.start()
        ...
    }
}

You'll find an exemplary implementation in the Grails JMS plug-in on GitHub.

The PersistenceContextInterceptor interface can be found on GitHub, too.

Taproot answered 7/12, 2021 at 14:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.