我有以下两种简单的模型创造了一个Grails应用2.4.3双向一个一对多的关系:Grails的NullPointerException异常节约双向one-to-many关系模型
class Player {
String firstName
String lastName
String position
static belongsTo = [team: Team]
}
class Team {
String name
List players = new ArrayList()
static hasMany = [players: Player]
static mapping= {
players cascade:"all-delete-orphan"
}
}
我想能够通过嵌套JSON保存和更新团队和他们相关的球员,像这样:
{
name : "team A",
players : [
{
firstName : "john",
lastName :"doe",
position : "center"
}
]
}
我TeamController保存操作是这样的:
def save() {
def team = new Team(request.JSON)
team.save()
respond team
}
当我做与JSON的请求,我得到一个错误:
curl -X POST -d '{name:"team a",players:[{firstName:"john",lastName:"doe",position:"center"}]}' http://localhost:8080/team-test/team/save.json --header "Content-Type:application/json"
产生这样的输出控制台:
| Error 2014-11-14 10:32:26,111 [http-bio-8080-exec-6] ERROR errors.GrailsExceptionResolver - NullPointerException occurred when processing request: [POST] /team-test/team/save.json
Stacktrace follows:
Message: null
Line | Method
->> 8 | save in team.test.TeamController$$EOvbjH0K
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 198 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter in grails.plugin.cache.web.filter.AbstractFilter
| 1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
| 615 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^ 745 | run in java.lang.Thread
因为这是一个相当模糊的消息,我把一个try/catch围绕team.save()调用TeamController并打印出堆栈跟踪:
def save() {
def team = new Team(request.JSON)
try {
team.save()
}
catch(Exception e)
{
e.printStackTrace()
}
respond team
}
Error java.lang.NullPointerException
| Error at org.hibernate.engine.spi.BatchFetchQueue.removeBatchLoadableEntityKey(BatchFetchQueue.java:163)
| Error at org.hibernate.engine.internal.StatefulPersistenceContext.addEntity(StatefulPersistenceContext.java:388)
| Error at org.hibernate.engine.internal.StatefulPersistenceContext.addEntity(StatefulPersistenceContext.java:461)
| Error at org.hibernate.action.internal.AbstractEntityInsertAction.makeEntityManaged(AbstractEntityInsertAction.java:143)
| Error at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:201)
| Error at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:179)
| Error at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:214)
| Error at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:324)
| Error at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288)
| Error at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)
| Error at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
| Error at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:209)
| Error at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:194)
| Error at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:114)
| Error at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
| Error at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:684)
| Error at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:676)
| Error at org.hibernate.engine.spi.CascadingActions$5.cascade(CascadingActions.java:235)
| Error at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350)
| Error at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293)
| Error at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
| Error at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:379)
| Error at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:319)
| Error at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:296)
| Error at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
| Error at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118)
| Error at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:460)
| Error at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:294)
| Error at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)
| Error at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
| Error at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:209)
| Error at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:194)
| Error at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:114)
| Error at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
| Error at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:684)
| Error at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:676)
| Error at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:671)
| Error at org.codehaus.groovy.grails.orm.hibernate.metaclass.SavePersistentMethod$1.doInHibernate(SavePersistentMethod.java:58)
| Error at org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:179)
| Error at org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:123)
| Error at org.codehaus.groovy.grails.orm.hibernate.metaclass.SavePersistentMethod.performSave(SavePersistentMethod.java:56)
| Error at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractSavePersistentMethod.doInvokeInternal(AbstractSavePersistentMethod.java:215)
| Error at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractDynamicPersistentMethod.invoke(AbstractDynamicPersistentMethod.java:68)
| Error at org.codehaus.groovy.grails.orm.hibernate.HibernateGormInstanceApi.save(HibernateGormInstanceApi.groovy:156)
| Error at team.test.Team$$EOvbjH0K.save(Team.groovy)
| Error at team.test.Team$$DOvbjH0K.save(Unknown Source)
| Error at team.test.Team.save(Team.groovy)
| Error at team.test.Team$save.call(Unknown Source)
| Error at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
| Error at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
| Error at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
| Error at team.test.TeamController$$EOvbkj0y.save(TeamController.groovy:9)
| Error at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
| Error at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
| Error at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
| Error at java.lang.reflect.Method.invoke(Method.java:606)
| Error at org.springsource.loaded.ri.ReloadedTypeInvoker$2.invoke(ReloadedTypeInvoker.java:122)
| Error at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1299)
| Error at org.codehaus.groovy.grails.web.servlet.mvc.MixedGrailsControllerHelper.invoke(MixedGrailsControllerHelper.java:154)
| Error at org.codehaus.groovy.grails.web.servlet.mvc.AbstractGrailsControllerHelper.handleAction(AbstractGrailsControllerHelper.java:375)
| Error at org.codehaus.groovy.grails.web.servlet.mvc.AbstractGrailsControllerHelper.executeAction(AbstractGrailsControllerHelper.java:252)
| Error at org.codehaus.groovy.grails.web.servlet.mvc.AbstractGrailsControllerHelper.handleURI(AbstractGrailsControllerHelper.java:205)
| Error at org.codehaus.groovy.grails.web.servlet.mvc.AbstractGrailsControllerHelper.handleURI(AbstractGrailsControllerHelper.java:126)
| Error at org.codehaus.groovy.grails.web.servlet.mvc.SimpleGrailsController.handleRequest(SimpleGrailsController.java:72)
| Error at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:50)
| Error at org.codehaus.groovy.grails.web.servlet.GrailsDispatcherServlet.doDispatch(GrailsDispatcherServlet.java:347)
| Error at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
| Error at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
| Error at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
| Error at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
| Error at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
| Error at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
| Error at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
| Error at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
| Error at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
| Error at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
| Error at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
| Error at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:198)
| Error at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
| Error at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
| Error at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
| Error at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
| Error at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
| Error at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
| Error at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
| Error at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
| Error at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
| Error at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
| Error at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
| Error at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
| Error at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
| Error at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
| Error at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
| Error at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
| Error at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
| Error at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
| Error at org.codehaus.groovy.grails.web.mapping.UrlMappingUtils.forwardRequestForUrlMappingInfo(UrlMappingUtils.java:178)
| Error at org.codehaus.groovy.grails.web.mapping.UrlMappingUtils.forwardRequestForUrlMappingInfo(UrlMappingUtils.java:144)
| Error at org.codehaus.groovy.grails.web.mapping.UrlMappingUtils.forwardRequestForUrlMappingInfo(UrlMappingUtils.java:135)
| Error at org.codehaus.groovy.grails.web.mapping.filter.UrlMappingsFilter.doFilterInternal(UrlMappingsFilter.java:216)
| Error at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
| Error at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
| Error at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
| Error at org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:69)
| Error at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
| Error at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
| Error at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
| Error at org.codehaus.groovy.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
| Error at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
| Error at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
| Error at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
| Error at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
| Error at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
| Error at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
| Error at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
| Error at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
| Error at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
| Error at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
| Error at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
| Error at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
| Error at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
| Error at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
| Error at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
| Error at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070)
| Error at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
| Error at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
| Error at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
| Error at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
| Error at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
| Error at java.lang.Thread.run(Thread.java:745)
为了进一步调查,我在日志中放了一些日志控制器动作,看看发生了什么事情的对象中:
def save() {
JSON.use('deep')
def team = new Team(request.JSON)
println "errors? " + team.getErrors()
println "team: " + team.toString()
println ((team as JSON).toString())
respond team
}
产生以下输出:
errors? grails.validation.ValidationErrors: 0 errors
team: team.test.Team : (unsaved)
{
"class":"team.test.Team",
"id":null,
"name":"team a",
"players":[
{
"class":"team.test.Player",
"id":null,
"firstName":"john",
"lastName":"doe",
"position":"center",
"team":null
}
]
}
有什么错我的JSON?为什么我会得到这个错误?是否因为球员对象中的球队引用为空?为什么团队在玩家对象中引用null?
出于好奇,我删除从播放器类的属于关联性,工作的事情如预期...
{"class":"team.test.Team","id":2,"name":"team a","players":[{"class":"team.test.Player","id":2}]}
为什么它不能在双向的情况下工作?在表格关系,数据绑定等方面,双向和单向的不同含义是什么?我理解使用belongsTo或不在一对多关系中的不同级联策略,但都应该级联保存我认为。
我知道我在问很多问题。我需要帮助完成这项工作,但也想了解原则上发生了什么。非常感谢帮助。
编辑: per @ th3morg的建议我尝试通过Team.addToPlayers()手动构建团队和玩家模型并将它们关联起来。
def save() {
JSON.use('deep')
def teamData = request.JSON
def team = new Team()
team.name = teamData.name
teamData.players.each { playerData ->
def player = new Player(playerData)
team.addToPlayers(player)
}
team.save()
println ((team as JSON).toString())
respond team
}
生产:
{"class":"team.test.Team","id":3,"name":"team a","players":[{"class":"team.test.Player","id":3,"firstName":"john","lastName":"doe","position":"center","team":{"_ref":"../..","class":"team.test.Team"}}]}
这似乎是工作,但它是冗长和脆弱。有没有办法通过自动绑定或通过其他传统魔术来达到相同的效果?
您的播放器对象尚未保存,所以我认为您可能会得到一个暂时的异常,但通过嵌套的json创建播放器仍然可能是问题的一部分。作为测试,请先尝试先明确创建玩家,然后创建团队并添加玩家。 – th3morg 2014-11-14 17:40:36
谢谢。根据你的建议更新,这是可行的,但不是一个理想的方法 – speakingcode 2014-11-14 20:00:54