Results 1 to 5 of 5

Thread: Error deleting owning side of one-to-one relationship

  1. #1
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    667

    Question Error deleting owning side of one-to-one relationship

    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:

    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
    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:
    @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
        }
    }
    Code:
    @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
        }
    }
    Outcome

    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 transaction
    Code:
    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
    Workaround

    If I push in the PassportController#delete method and insert the highlighted line:

    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());
    }
    .. then it all works nicely. But is that workaround actually necessary, or is there something wrong with my JPA annotations?
    Andrew Swan
    "Now is the EJB of our discontent made glorious Spring"

  2. #2
    Join Date
    Mar 2008
    Location
    Sydney, AU
    Posts
    974

    Default

    Yes, currently the support of the JPA @OneToOne has somewhat limited support in Roo. Can you please open a Jira ticket with your description below? Maybe even attach the complete script (and manual editing steps) to replicate the app. This will probably not be fixed in time for the 1.1 M1 release but possibly for M2.

    -Stefan
    Stefan Schmidt
    Software Engineer, Spring Roo
    SpringSource - a division of VMware
    twitter @schmidtstefan

  3. #3
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    667

    Question

    Stefan, I'm happy to log an issue if necessary, but are you sure it's a problem with Roo? I'm still not clear whether my workaround is desirable or correct; are you saying that it's OK and Roo should add it by default?
    Andrew Swan
    "Now is the EJB of our discontent made glorious Spring"

  4. #4
    Join Date
    Mar 2008
    Location
    Sydney, AU
    Posts
    974

    Default

    Andrew,

    My asking for a Jira ticket was more intended to say that I need to review exactly what Roo should do to help users which such scenarios. I am not sure if your workaround would be generally advisable. Maybe, all we need to do is to offer this as a solution for this specific problem in the Roo documentation.

    The problem with the forum is that these threads vanish at some stage and all your valuable input is lost, so creating a jira ticket will help us to track the issue (which may result in a code change or just docs adjustments).

    Cheers,
    Stefan
    Stefan Schmidt
    Software Engineer, Spring Roo
    SpringSource - a division of VMware
    twitter @schmidtstefan

  5. #5
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    667

    Thumbs up Done

    OK, I've logged it as ROO-669.
    Andrew Swan
    "Now is the EJB of our discontent made glorious Spring"

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •