Grails

Assign blank value as Null to a nullable domain class attribute

Details

  • Type: Improvement Improvement
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: 0.5.6
  • Fix Version/s: 1.0-RC2
  • Component/s: Persistence
  • Labels:
    None

Description

null should be assign to a domain attribute when it is nullable from a blank user input

Activity

Hide
David Smiley added a comment -

I think this is a big deal.

I think I'll be forced to essentially write my own generic data binder at the controller because of this. But I suspect it'll be hard so there will be corner cases I can't easily address.

Show
David Smiley added a comment - I think this is a big deal. I think I'll be forced to essentially write my own generic data binder at the controller because of this. But I suspect it'll be hard so there will be corner cases I can't easily address.
Hide
Marc Palmer added a comment -

What do you mean by blank user input here?

Blank HTML forms do not include their attributes when posting to a URL, so this is a non-issue.

If you mean:

def x = new SomeDomainClass()
x.someProperty = ""

...and that you expect someProperty to be null, I would say that is fundamentally wrong.

Show
Marc Palmer added a comment - What do you mean by blank user input here? Blank HTML forms do not include their attributes when posting to a URL, so this is a non-issue. If you mean: def x = new SomeDomainClass() x.someProperty = "" ...and that you expect someProperty to be null, I would say that is fundamentally wrong.
Hide
David Smiley added a comment -

You jogged my memory on the blank attributes not being transmitted... but I can't seem to reproduce that. I created a basic HTML form with no values (one a select with a blank value), the other a input text where I delete the value in the browser before hitting submit. I tried with GET and POST, and with POST I tried AJAX submission as well (using the very latest Yahoo and with the dojo that comes with grails). Every time I'm getting it through and with no value; I believe that ends up being an empty string

I agree that setting a value to the empty string directly on the domain should probably not be coerced to a null; though I am open to that idea. I do think that a blank value should be null, not an empty string on the params.

The blank value is troublesome for me at the moment because I'd like to update my domain object's value to null. I wonder how rails does this off hand; I forget. I'm using the model.properties = params method now, so it is grails and/or SpringMVC that is doing the params to domain object binding. If I can't use it anymore... I guess I'll have to re-implement it, more or less. I have developed a scaffolding abstract base class and I like to generalize as much as possible. My domain models are numerous. Forcing every controller to manually build-out fill in the domain model from the params to then save would be lots of code that I don't think I should have to do.

Thanks for your time, Marc

Show
David Smiley added a comment - You jogged my memory on the blank attributes not being transmitted... but I can't seem to reproduce that. I created a basic HTML form with no values (one a select with a blank value), the other a input text where I delete the value in the browser before hitting submit. I tried with GET and POST, and with POST I tried AJAX submission as well (using the very latest Yahoo and with the dojo that comes with grails). Every time I'm getting it through and with no value; I believe that ends up being an empty string I agree that setting a value to the empty string directly on the domain should probably not be coerced to a null; though I am open to that idea. I do think that a blank value should be null, not an empty string on the params. The blank value is troublesome for me at the moment because I'd like to update my domain object's value to null. I wonder how rails does this off hand; I forget. I'm using the model.properties = params method now, so it is grails and/or SpringMVC that is doing the params to domain object binding. If I can't use it anymore... I guess I'll have to re-implement it, more or less. I have developed a scaffolding abstract base class and I like to generalize as much as possible. My domain models are numerous. Forcing every controller to manually build-out fill in the domain model from the params to then save would be lots of code that I don't think I should have to do. Thanks for your time, Marc
Hide
Marc Palmer added a comment -

Hmm well I don't think we should be getting blanks in params, maybe I'm hallucinating.

I will check tomorrow.

For now, can't you add this to your controllers / beforeinterceptor:

def newparams = [:]
params.each() { k, v -> 
   if (value instanceof String) { 
      if (!value.trim().length()) {
         return
      }
   } 
   newparams[k] = v
}
params.clear()
params.putAll(newparams)

Somethign like that should filter out all the blank inputs for you prior to binding.

Show
Marc Palmer added a comment - Hmm well I don't think we should be getting blanks in params, maybe I'm hallucinating. I will check tomorrow. For now, can't you add this to your controllers / beforeinterceptor:
def newparams = [:]
params.each() { k, v -> 
   if (value instanceof String) { 
      if (!value.trim().length()) {
         return
      }
   } 
   newparams[k] = v
}
params.clear()
params.putAll(newparams)
Somethign like that should filter out all the blank inputs for you prior to binding.
Hide
David Smiley added a comment -

That's a nice try but it doesn't work; I tried this already. When you do model.properties = params it appears as if grails/SpringMVC is not using it; presumably it's going to the request object to get the parameters. To test this, just do params.clear() and watch grails go save the original parameters any way. Very annoying and unexpected.

So, either I do generic data binding logic myself (seems hard) or I patch grails (seems easier) but problematic.

Show
David Smiley added a comment - That's a nice try but it doesn't work; I tried this already. When you do model.properties = params it appears as if grails/SpringMVC is not using it; presumably it's going to the request object to get the parameters. To test this, just do params.clear() and watch grails go save the original parameters any way. Very annoying and unexpected. So, either I do generic data binding logic myself (seems hard) or I patch grails (seems easier) but problematic.
Hide
David Smiley added a comment -

I just found a work-around. Basically, you need to avoid using the params instance directly for data binding since GrailsDataBinder has different behavior if it gets that. You need to feed it some other map (HashMap is convenient). Also, in your code, you skipped over the blanks... but in my case, I want the map to contain a null value so that it will set the corresponding property on the model to null.

Show
David Smiley added a comment - I just found a work-around. Basically, you need to avoid using the params instance directly for data binding since GrailsDataBinder has different behavior if it gets that. You need to feed it some other map (HashMap is convenient). Also, in your code, you skipped over the blanks... but in my case, I want the map to contain a null value so that it will set the corresponding property on the model to null.
Hide
Graeme Rocher added a comment -

If we change this behaviour then we'll have some other guy raising an issue saying that we should assign blank to nullable objects when there is a blank string. It is neither here nor there

Show
Graeme Rocher added a comment - If we change this behaviour then we'll have some other guy raising an issue saying that we should assign blank to nullable objects when there is a blank string. It is neither here nor there
Hide
David Smiley added a comment -

Presently, one cannot use grails built-in binding if there's a property that won't accept the empty string. If someone wants empty strings, then let constraints indicate that is so and then bind accordingly. You don't have to pick one side or the other Graeme, let constraints indicate desired behavior.

BTW, IMO, I think the blank constraint should be false by default, and nullable true.

Show
David Smiley added a comment - Presently, one cannot use grails built-in binding if there's a property that won't accept the empty string. If someone wants empty strings, then let constraints indicate that is so and then bind accordingly. You don't have to pick one side or the other Graeme, let constraints indicate desired behavior. BTW, IMO, I think the blank constraint should be false by default, and nullable true.
Hide
Graeme Rocher added a comment -

In Grails SVN head Grails' data binder now uses the params object and not the original request so Marc's code will work with SVN head as your workaround

Show
Graeme Rocher added a comment - In Grails SVN head Grails' data binder now uses the params object and not the original request so Marc's code will work with SVN head as your workaround
Hide
Carsten Block added a comment -

If I implement Marc's solution in my controller class (ShoutController)I always get "object references an unsaved transient instance - save the transient instance before flushing: Shout" errrors, though the data will still be saved to the database. Not too sure why this happens. I also tried to switch from the params object to another HashMap defined inside the Controller but the result remained the same.

[55113] StackTrace Sanitizing stacktrace:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:219)
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:397)
at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:225)
at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:235)
at org.hibernate.type.TypeFactory.findDirty(TypeFactory.java:597)
at org.hibernate.persister.entity.AbstractEntityPersister.findDirty(AbstractEntityPersister.java:3123)
at org.hibernate.event.def.DefaultFlushEntityEventListener.dirtyCheck(DefaultFlushEntityEventListener.java:479)
at org.hibernate.event.def.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:204)
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:127)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:196)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:390)
at org.codehaus.groovy.grails.orm.hibernate.support.GrailsOpenSessionInViewInterceptor.flushIfNecessary(GrailsOpenSessionInViewInterceptor.java:67)
at org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor.postHandle(OpenSessionInViewInterceptor.java:181)
at org.codehaus.groovy.grails.orm.hibernate.support.GrailsOpenSessionInViewInterceptor.postHandle(GrailsOpenSessionInViewInterceptor.java:56)
at org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter.postHandle(WebRequestHandlerInterceptorAdapter.java:61)
at org.codehaus.groovy.grails.web.servlet.GrailsDispatcherServlet.doDispatch(GrailsDispatcherServlet.java:247)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:790)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:476)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:441)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:367)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:268)
at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:126)
at org.codehaus.groovy.grails.web.mapping.filter.UrlMappingsFilter.doFilterInternal(UrlMappingsFilter.java:104)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089)
at com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:119)
at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:55)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089)
at org.codehaus.groovy.grails.web.servlet.filter.GrailsReloadServletFilter.doFilterInternal(GrailsReloadServletFilter.java:155)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089)
at org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:54)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:183)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:138)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:365)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
at org.mortbay.jetty.Server.handle(Server.java:295)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:503)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:841)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:639)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:210)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:379)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:361)
at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:442)
[55117] StackTrace Sanitizing stacktrace:
org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: Shout; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:634)
at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412)
at org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor.postHandle(OpenSessionInViewInterceptor.java:184)
at org.codehaus.groovy.grails.orm.hibernate.support.GrailsOpenSessionInViewInterceptor.postHandle(GrailsOpenSessionInViewInterceptor.java:56)
at org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter.postHandle(WebRequestHandlerInterceptorAdapter.java:61)
at org.codehaus.groovy.grails.web.servlet.GrailsDispatcherServlet.doDispatch(GrailsDispatcherServlet.java:247)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:790)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:476)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:441)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:367)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:268)
at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:126)
at org.codehaus.groovy.grails.web.mapping.filter.UrlMappingsFilter.doFilterInternal(UrlMappingsFilter.java:104)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089)
at com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:119)
at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:55)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089)
at org.codehaus.groovy.grails.web.servlet.filter.GrailsReloadServletFilter.doFilterInternal(GrailsReloadServletFilter.java:155)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089)
at org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:54)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:183)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:138)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:365)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
at org.mortbay.jetty.Server.handle(Server.java:295)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:503)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:841)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:639)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:210)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:379)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:361)
at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:442)
Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout
[55119] StackTrace Sanitizing stacktrace:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout
[55119] StackTrace Sanitizing stacktrace:
org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: Shout; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout
Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout

Show
Carsten Block added a comment - If I implement Marc's solution in my controller class (ShoutController)I always get "object references an unsaved transient instance - save the transient instance before flushing: Shout" errrors, though the data will still be saved to the database. Not too sure why this happens. I also tried to switch from the params object to another HashMap defined inside the Controller but the result remained the same. [55113] StackTrace Sanitizing stacktrace: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:219) at org.hibernate.type.EntityType.getIdentifier(EntityType.java:397) at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:225) at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:235) at org.hibernate.type.TypeFactory.findDirty(TypeFactory.java:597) at org.hibernate.persister.entity.AbstractEntityPersister.findDirty(AbstractEntityPersister.java:3123) at org.hibernate.event.def.DefaultFlushEntityEventListener.dirtyCheck(DefaultFlushEntityEventListener.java:479) at org.hibernate.event.def.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:204) at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:127) at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:196) at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76) at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26) at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000) at org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:390) at org.codehaus.groovy.grails.orm.hibernate.support.GrailsOpenSessionInViewInterceptor.flushIfNecessary(GrailsOpenSessionInViewInterceptor.java:67) at org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor.postHandle(OpenSessionInViewInterceptor.java:181) at org.codehaus.groovy.grails.orm.hibernate.support.GrailsOpenSessionInViewInterceptor.postHandle(GrailsOpenSessionInViewInterceptor.java:56) at org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter.postHandle(WebRequestHandlerInterceptorAdapter.java:61) at org.codehaus.groovy.grails.web.servlet.GrailsDispatcherServlet.doDispatch(GrailsDispatcherServlet.java:247) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:790) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:476) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:441) at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487) at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:367) at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181) at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712) at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405) at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:268) at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:126) at org.codehaus.groovy.grails.web.mapping.filter.UrlMappingsFilter.doFilterInternal(UrlMappingsFilter.java:104) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089) at com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:119) at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:55) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089) at org.codehaus.groovy.grails.web.servlet.filter.GrailsReloadServletFilter.doFilterInternal(GrailsReloadServletFilter.java:155) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089) at org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:54) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:183) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:138) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089) at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:365) at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181) at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712) at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405) at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139) at org.mortbay.jetty.Server.handle(Server.java:295) at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:503) at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:841) at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:639) at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:210) at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:379) at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:361) at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:442) [55117] StackTrace Sanitizing stacktrace: org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: Shout; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:634) at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412) at org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor.postHandle(OpenSessionInViewInterceptor.java:184) at org.codehaus.groovy.grails.orm.hibernate.support.GrailsOpenSessionInViewInterceptor.postHandle(GrailsOpenSessionInViewInterceptor.java:56) at org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter.postHandle(WebRequestHandlerInterceptorAdapter.java:61) at org.codehaus.groovy.grails.web.servlet.GrailsDispatcherServlet.doDispatch(GrailsDispatcherServlet.java:247) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:790) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:476) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:441) at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487) at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:367) at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181) at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712) at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405) at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:268) at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:126) at org.codehaus.groovy.grails.web.mapping.filter.UrlMappingsFilter.doFilterInternal(UrlMappingsFilter.java:104) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089) at com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:119) at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:55) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089) at org.codehaus.groovy.grails.web.servlet.filter.GrailsReloadServletFilter.doFilterInternal(GrailsReloadServletFilter.java:155) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089) at org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:54) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:183) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:138) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1089) at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:365) at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181) at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712) at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405) at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139) at org.mortbay.jetty.Server.handle(Server.java:295) at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:503) at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:841) at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:639) at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:210) at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:379) at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:361) at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:442) Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout [55119] StackTrace Sanitizing stacktrace: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout [55119] StackTrace Sanitizing stacktrace: org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: Shout; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Shout
Hide
David Smiley added a comment -

Seems your domain model object is referring to a non-persistent object but I can't know for sure without seeing your domain model and binding code. Looking at Hibernate's API for this exception was a clue.
Are you sure the changes you made to params actually had an effect? See posts from Graeme and me here... unless of course you're using some SVN head since Graeme fixed one of the issues.

Show
David Smiley added a comment - Seems your domain model object is referring to a non-persistent object but I can't know for sure without seeing your domain model and binding code. Looking at Hibernate's API for this exception was a clue. Are you sure the changes you made to params actually had an effect? See posts from Graeme and me here... unless of course you're using some SVN head since Graeme fixed one of the issues.
Hide
Carsten Block added a comment -

Domain model that contains a (nullable) self reference

Show
Carsten Block added a comment - Domain model that contains a (nullable) self reference
Hide
Carsten Block added a comment -

The main problem in my case seems to be the self reference to the domain model but I don't understand why...

Show
Carsten Block added a comment - The main problem in my case seems to be the self reference to the domain model but I don't understand why...
Hide
David Smiley added a comment -

I think this doesn't have to do with this JIRA issue. Ask the mailing list. (sorry to punt your inquiry)

Show
David Smiley added a comment - I think this doesn't have to do with this JIRA issue. Ask the mailing list. (sorry to punt your inquiry)
Hide
Carsten Block added a comment -

You're right. Sorry for polluting this thread...

Show
Carsten Block added a comment - You're right. Sorry for polluting this thread...
Hide
David Smiley added a comment -

Graeme, you claimed in this thread in September that changes to params will be used instead of the request. I'm looking at the latest and this line:
http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/web/org/codehaus/groovy/grails/web/metaclass/BindDynamicMethod.java?r=5542#l106
suggests the request object is used instead of params.

Show
David Smiley added a comment - Graeme, you claimed in this thread in September that changes to params will be used instead of the request. I'm looking at the latest and this line: http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/web/org/codehaus/groovy/grails/web/metaclass/BindDynamicMethod.java?r=5542#l106 suggests the request object is used instead of params.
Hide
Graeme Rocher added a comment -

David - Sorry this is fixed now, which will provide your workaround. I'm not sure this issue is valid anymore, if we changed the behaviour then someone else would raise another saying blank values should be assigned as blank

Show
Graeme Rocher added a comment - David - Sorry this is fixed now, which will provide your workaround. I'm not sure this issue is valid anymore, if we changed the behaviour then someone else would raise another saying blank values should be assigned as blank
Hide
David Smiley added a comment -

I disagree... the constraints let you know whether the developer wants "" or null to represent no data. If nullable is true then it's nulls, otherwise it's "".

Show
David Smiley added a comment - I disagree... the constraints let you know whether the developer wants "" or null to represent no data. If nullable is true then it's nulls, otherwise it's "".
Hide
Graeme Rocher added a comment -

Ok i'll see what we can do

Show
Graeme Rocher added a comment - Ok i'll see what we can do
Hide
Graeme Rocher added a comment -

Ok, so now if a String property is nullable null is used instead of a blank string

Show
Graeme Rocher added a comment - Ok, so now if a String property is nullable null is used instead of a blank string
Hide
eranlo added a comment -

I realize this is an old issue, but we just ran into it recently. It seems like there's a conflation of two different things in the database, whether a column is nullable and the column's default value. This fix means that the nullable constraint forces us to use both.

In our legacy database, we allow a null value, but we differentiate between a null value and an empty string, and we want the value we enter in the domain object to end up in the database. Is there a way to do so? To force grails to save an empty string on a nullable field?

Show
eranlo added a comment - I realize this is an old issue, but we just ran into it recently. It seems like there's a conflation of two different things in the database, whether a column is nullable and the column's default value. This fix means that the nullable constraint forces us to use both. In our legacy database, we allow a null value, but we differentiate between a null value and an empty string, and we want the value we enter in the domain object to end up in the database. Is there a way to do so? To force grails to save an empty string on a nullable field?

People

Vote (3)
Watch (4)

Dates

  • Created:
    Updated:
    Resolved: