Goal
I want to have a bidirectional one-to-one relationship between a Person and a Passport, such that a Person can have either zero or one Passports, and each Passport is owned by exactly one Person (not real life, but work with me). When I delete a Person, any Passport they have should also be deleted, but not vice versa (i.e. you should be able to delete a Passport without deleting its holder). In the database, I want the foreign key to be in the "passport" table.
Here's my script:
I've edited my entities to look like this (noting that the Roo 1.0.2 shell doesn't seem to allow the creation of 1-1 relationships):Code:project --topLevelPackage one persistence setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY entity --class ~.Person entity --class ~.Passport field reference --class ~.Passport --fieldName holder --type ~.Person field reference --class ~.Person --fieldName passport --type ~.Passport controller all --package ~.web
Code:@Entity @RooJavaBean @RooToString @RooEntity public class Person { // Inverse side ("passport" table has the FK column) @OneToOne(cascade = CascadeType.ALL, mappedBy = "holder") private Passport passport; @Override public String toString() { return super.toString(); // avoids circular references } }OutcomeCode:@Entity @RooJavaBean @RooToString @RooEntity public class Passport { // Owning side (this table has the FK column) @OneToOne @JoinColumn private Person holder; @Override public String toString() { return super.toString(); // avoids circular references } }
If I delete a Person from the Person list, it's all good; both the Person and the Passport are deleted. So the cascading works. But if I delete a Passport, I get this rather unhelpful error (and no errors in the Jetty console):
Code:Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while commiting the transactionWorkaroundCode:org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:476) org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754) org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723) org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:394) org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$afterReturning$org_springframework_transaction_aspectj_AbstractTransactionAspect$3$2a73e96c(AbstractTransactionAspect.aj:78) one.Passport_Roo_Entity.ajc$interMethod$one_Passport_Roo_Entity$one_Passport$remove(Passport_Roo_Entity.aj:61) one.Passport.remove(Passport.java:1) one.Passport_Roo_Entity.ajc$interMethodDispatch1$one_Passport_Roo_Entity$one_Passport$remove(Passport_Roo_Entity.aj) one.web.PassportController_Roo_Controller.ajc$interMethod$one_web_PassportController_Roo_Controller$one_web_PassportController$delete(PassportController_Roo_Controller.aj:79) one.web.PassportController.delete(PassportController.java:1) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) java.lang.reflect.Method.invoke(Method.java:597) org.springframework.web.bind.annotation.support.HandlerMethodInvoker.doInvokeMethod(HandlerMethodInvoker.java:710) org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:167) org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:414) org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:402) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:771) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:716) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:647) org.springframework.web.servlet.FrameworkServlet.doDelete(FrameworkServlet.java:585) javax.servlet.http.HttpServlet.service(HttpServlet.java:733) javax.servlet.http.HttpServlet.service(HttpServlet.java:820) org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487) org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:362) org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181) org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:726) org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405) org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:285) org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:126) org.tuckey.web.filters.urlrewrite.NormalRewrittenUrl.doRewrite(NormalRewrittenUrl.java:195) org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:159) org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:141) org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:90) org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:417) org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084) org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:68) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084) org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084) org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:113) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084) org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360) org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181) org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:726) org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405) org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:206) org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114) org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) org.mortbay.jetty.Server.handle(Server.java:324) org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505) org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:843) org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:648) org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211) org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380) org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395) org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488
If I push in the PassportController#delete method and insert the highlighted line:
.. then it all works nicely. But is that workaround actually necessary, or is there something wrong with my JPA annotations?Code:@RequestMapping(value = "/passport/{id}", method = RequestMethod.DELETE) public String delete(@PathVariable("id") Long id, @RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "size", required = false) Integer size) { if (id == null) throw new IllegalArgumentException("An Identifier is required"); Passport passport = Passport.findPassport(id); passport.getHolder().setPassport(null); // added this line passport.remove(); return "redirect:/passport?page=" + ((page == null) ? "1" : page.toString()) + "&size=" + ((size == null) ? "10" : size.toString()); }


Reply With Quote
