4 Replies Latest reply on Jan 24, 2008 11:11 AM by Pete Muir

    NonUniqueObjectException and EntityManager

    Bernard Labno Master

      Hello, I get NonUniqueObjectException when I call addCountry() on following bean.
      em.contains(country) does not find country object, however Contexts.lookupInStatefulContexts("country") does find it. But why calling em.find method invocation makes something to inert found entity into any contexts ?
      I found in seam examples that db lookup is based on query result, why is that so and not based on em.find or em.contains ?

      @Stateful
      @Scope(ScopeType.SESSION)
      @Name("countryCreator")
      public class CountryCreatorBean implements CountryCreatorLocal {
       @PersistenceContext
       private EntityManager em;
       @In
       private Country country;
       @EJB
       private LocationLocal locationBean;
       public void addCountry() {
       country.setId("pl");
       country.setName("Poland");
       System.out.println(em.contains(country)); //prints false
       System.out.print(Contexts.lookupInStatefulContexts("country")); //this finds object [id=pl;name=Poland]
       if( em.find(Country.class,country.getId()) != null ) //this triggers exception
       em.persist(country);
      // locationBean.add(country);
       }
       @Remove
       public void destroy() {}
      }



      ERROR [GraphElement] action threw exception: javax.ejb.EJBTransactionRolledbackException: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [pl.labno.bernard.bidbull.locations.Country#pl]
      javax.el.ELException: javax.ejb.EJBTransactionRolledbackException: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [pl.labno.bernard.bidbull.locations.Country#pl]


        • 1. Re: NonUniqueObjectException and EntityManager
          Bernard Labno Master

           

          org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session


          What throws that exception ? Hibernate or SEAM ? Cause i don't understand which session is metioned in quoted exception.



          • 2. Re: NonUniqueObjectException and EntityManager
            Pete Muir Master

            Hibernate. You try to insert a duplicate entity into the database

            • 3. Re: NonUniqueObjectException and EntityManager
              Bernard Labno Master

              Abrakadabra !

              I have stateful CountryCreatorBean, that has a reference to stateless LocationBean. When CountryCreatorBean.addCountry() is called then in the body of that method LocationBean.addCountry(country) is called. Country variable is injected into CountryCreatorBean by the SEAM, by the way.
              It is responsibility of LocationBean, who plays role of location manager, to check if entity already exists in DB. If such a country already exists then some exception should be thrown by LocationBean.

              Now is the fun part. At first I've decided to use org.hibernate.NonUniqueObjectException, since it fits the purpose perfectly. Of course in CountryCreatorBean.addCountry method I prepaired try/catch for such an ocasion. But guess what ! Transaction gets rolled back and exception is not caught, and web page gets filled with stack trace.
              After a while I figured that in stead of org.hibernate.NonUniqueObjectException the javax.ejb.EJBTransactionRolledbackException gets to try/catch block.

              Remember to read stack trace carefully ! By the way, what do you think about such approach to move persistance to stateless bean ? I chose this way to write business logic independently from web-framework.

              @Stateful
              @Scope(ScopeType.SESSION)
              @Name("countryCreator")
              public class CountryCreatorBean implements CountryCreatorLocal {
              
               @In(create = true)
               private Country country;
               @EJB
               private LocationLocal locationBean;
              
               public void addCountry() {
               try {
               locationBean.add(country);
               } catch (javax.ejb.EJBTransactionRolledbackException exc) {
               System.out.println("RuntimeException caught !"+exc);
               FacesMessages.instance().add("#{messages.countryAlreadyExists}");
               }
               }
              
               @Remove
               public void destroy() {
               }
              }


              @Stateless
              public class LocationBean implements LocationLocal {
              
               @PersistenceContext
               private EntityManager em;
              
               public void add(Country country) throws NonUniqueObjectException {
               if( em.find(Country.class,country.getId()) == null )
               em.persist(country);
               else throw new NonUniqueObjectException(country.getId(),Country.class.getCanonicalName());
               }
              }


              And one more thing, nobody noticed stupid condition from previous post

              if( em.find(Country.class,country.getId()) != null ) //this triggers exception
               em.persist(country);


              • 4. Re: NonUniqueObjectException and EntityManager
                Pete Muir Master

                Well, given Seam enables you to write stateful web apps, I wouldn't choose the stateless path.