Discussion:
How to avoid LazyInitializationException when a Grails Service is called outside of a web scope
David Kowis
2010-04-17 02:21:49 UTC
Permalink
I have a grails service that connects to a jabber server, and responds
to messages, a chat bot, basically.

However, whenever I try to access domain objects, I cannot. I get an
exception:
hibernate.LazyInitializationException - could not initialize proxy - no
Session

I know there's some way to make this work, but I have no clue how I'd
initialize the hibernate session so that I can do this.

Anyone done this before, or do I need to completely rewrite my software?

Thanks,
David
Burt Beckwith
2010-04-17 02:28:13 UTC
Permalink
If the service is transactional it _should_ work. But wrapping your code in a withTransaction block will keep the session open and avoid lazy initialization issues.

Burt
Post by David Kowis
I have a grails service that connects to a jabber server, and responds
to messages, a chat bot, basically.
However, whenever I try to access domain objects, I cannot. I get an
hibernate.LazyInitializationException - could not initialize proxy - no
Session
I know there's some way to make this work, but I have no clue how I'd
initialize the hibernate session so that I can do this.
Anyone done this before, or do I need to completely rewrite my software?
Thanks,
David
---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
David Kowis
2010-04-17 02:34:26 UTC
Permalink
Post by Burt Beckwith
If the service is transactional it _should_ work. But wrapping your code in a withTransaction block will keep the session open and avoid lazy initialization issues.
The service is transactional
<code>
class JabberService {
boolean transactional = true
</code>

but it's not working.
Do I need to wrap it in a withTransaction for each domain object I'm
going to use? Could I see an example real quick?

Thanks again,
David
Burt Beckwith
2010-04-17 02:53:49 UTC
Permalink
How are you accessing the service? If you're getting it from the Spring application context it should work, but if you're creating it via new it won't be proxied.

To use withTransaction, wrap everything inside the closure:

class JabberService {

def someMethod() {
withTransaction { status ->
def thing = Thing.get(123)
def otherThings = thing.otherThings
otherThings.each { ... }
}
}
}

and the lazy-loaded 'otherThings' collection will have an open session to use to initialize with.

Burt
Post by David Kowis
Post by Burt Beckwith
If the service is transactional it _should_ work. But wrapping your code in a withTransaction block will keep the session open and avoid lazy initialization issues.
The service is transactional
<code>
class JabberService {
boolean transactional = true
</code>
but it's not working.
Do I need to wrap it in a withTransaction for each domain object I'm
going to use? Could I see an example real quick?
Thanks again,
David
---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
David Kowis
2010-04-17 03:01:15 UTC
Permalink
Post by Burt Beckwith
How are you accessing the service? If you're getting it from the Spring application context it should work, but if you're creating it via new it won't be proxied.
class JabberService {
def someMethod() {
withTransaction { status ->
def thing = Thing.get(123)
def otherThings = thing.otherThings
otherThings.each { ... }
}
}
}
and the lazy-loaded 'otherThings' collection will have an open session to use to initialize with.
The entire service:
http://pastebin.com/7ksL66Zn

I think because it's not being accessed from within the web context, it
doesn't have a session. Even though I create everything within the web
context, and most of the logic happens in here. The service is being
actuated by an event coming from a thread that is created by the
XMPPConnection class, when you tell it to add a MessageListener.

So I'd need a way to get the session without having the rest of the
application context that's normally there when you access it via a
controller or something.

David
Burt Beckwith
2010-04-17 03:10:38 UTC
Permalink
Don't confuse the web http session with the Hibernate session. It's somewhat coincidental that the web requests trigger the creation of Hibernate sessions, but this is because of the OpenSessionInView interceptor that Grails wires up. But that's used more for controllers since a transactional service method or a withTransaction block will start a Hibernate session if one isn't bound to the thread local (e.g. via the OSIV interceptor) and keep it open the whole time.

So how are you getting access to your JabberService? Do you have is dependency-injected as "def jabberService" somewhere?

One potentially serious unrelated issue is that your service is stateful (i.e. the settings, conn, cm, and chats fields). Spring services are singletons by default and are shared by all callers, so by maintaining state between method calls you run a high risk of two concurrent users stepping on each others' data.

Burt
Post by David Kowis
Post by Burt Beckwith
How are you accessing the service? If you're getting it from the Spring application context it should work, but if you're creating it via new it won't be proxied.
class JabberService {
def someMethod() {
withTransaction { status ->
def thing = Thing.get(123)
def otherThings = thing.otherThings
otherThings.each { ... }
}
}
}
and the lazy-loaded 'otherThings' collection will have an open session to use to initialize with.
http://pastebin.com/7ksL66Zn
I think because it's not being accessed from within the web context, it
doesn't have a session. Even though I create everything within the web
context, and most of the logic happens in here. The service is being
actuated by an event coming from a thread that is created by the
XMPPConnection class, when you tell it to add a MessageListener.
So I'd need a way to get the session without having the rest of the
application context that's normally there when you access it via a
controller or something.
David
---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Burt Beckwith
2010-04-17 03:28:54 UTC
Permalink
It doesn't matter what thread is used to call the methods, if they're genuinely transactional, or if you're inside of a withTransaction block, a Hibernate session will be active.

So you're just creating new instances of the service as "JabberService jabberService = new JabberService()"? If that's the case you're not using Spring, so you won't get a transactional proxy (or dependency injection - your 'def sessionFactory' will always be null) and withTransaction is your best bet.

Burt
Post by Burt Beckwith
Don't confuse the web http session with the Hibernate session. It's
somewhat coincidental that the web requests trigger the creation of
Hibernate sessions, but this is because of the OpenSessionInView
interceptor that Grails wires up. But that's used more for
controllers since a transactional service method or a withTransaction
block will start a Hibernate session if one isn't bound to the thread
local (e.g. via the OSIV interceptor) and keep it open the whole
time.
So how are you getting access to your JabberService? Do you have is
dependency-injected as "def jabberService" somewhere?
No, it is not done that way. There's an external java library that has
access to that textbot closure. When a chat is started with the bot, it
will send the messages to the textbot closure. Like a onMessage event.
That's okay if multiple people access that at one time, its thread safe
enough. The only place the actual JabberService is touched is from a web
gui, to connect and disconnect, which would start and terminate the threads.
I think the problem is that the Hibernate Session isn't there since the
textbot stuff is called from the XMPPConnection thread.
Post by Burt Beckwith
One potentially serious unrelated issue is that your service is
stateful (i.e. the settings, conn, cm, and chats fields). Spring
services are singletons by default and are shared by all callers, so
by maintaining state between method calls you run a high risk of two
concurrent users stepping on each others' data.
Not regarding messages coming in from the jabber connection. That stuff
isn't manipulated during that stage, it should be fine.
David
---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
David Kowis
2010-04-17 03:33:56 UTC
Permalink
Post by Burt Beckwith
It doesn't matter what thread is used to call the methods, if they're
genuinely transactional, or if you're inside of a withTransaction
block, a Hibernate session will be active.
So you're just creating new instances of the service as
"JabberService jabberService = new JabberService()"? If that's the
case you're not using Spring, so you won't get a transactional proxy
(or dependency injection - your 'def sessionFactory' will always be
null) and withTransaction is your best bet.
No, it's not happening that way. I am never creating a new JabberService


In the connect method, I add the textbot closure as a MessageListener to
events from the XMPPConnection class (also created in the connect method)

I think the session is closed when events come back from the
XMPPConnections. They're operating on doTextbot method of the service.
The sessionFactory reports taht it's a closed session.

David
David Kowis
2010-04-17 03:17:51 UTC
Permalink
Post by Burt Beckwith
Don't confuse the web http session with the Hibernate session. It's
somewhat coincidental that the web requests trigger the creation of
Hibernate sessions, but this is because of the OpenSessionInView
interceptor that Grails wires up. But that's used more for
controllers since a transactional service method or a withTransaction
block will start a Hibernate session if one isn't bound to the thread
local (e.g. via the OSIV interceptor) and keep it open the whole
time.
So how are you getting access to your JabberService? Do you have is
dependency-injected as "def jabberService" somewhere?
No, it is not done that way. There's an external java library that has
access to that textbot closure. When a chat is started with the bot, it
will send the messages to the textbot closure. Like a onMessage event.

That's okay if multiple people access that at one time, its thread safe
enough. The only place the actual JabberService is touched is from a web
gui, to connect and disconnect, which would start and terminate the threads.

I think the problem is that the Hibernate Session isn't there since the
textbot stuff is called from the XMPPConnection thread.
Post by Burt Beckwith
One potentially serious unrelated issue is that your service is
stateful (i.e. the settings, conn, cm, and chats fields). Spring
services are singletons by default and are shared by all callers, so
by maintaining state between method calls you run a high risk of two
concurrent users stepping on each others' data.
Not regarding messages coming in from the jabber connection. That stuff
isn't manipulated during that stage, it should be fine.

David

Loading...