3 Replies Latest reply on Nov 23, 2007 11:45 AM by Flavio Pompermaier

    ManyToMany relationship do not work properly

    Flavio Pompermaier Newbie

      Hi to all,
      I'm working on jboss 4.2.1GA and I'm stuck with a simple ManyToMany relationship..

      I have this relation between Users and Sources (of rss feeds - URLs in practice). I followed all tutorial about how to do it and I tried a lot of solutions but I can't figure out why it doesn't work!

      Here's the entity class User:

      package nc.entities.user;
      
      import javax.persistence.*;
      import java.util.Collection;
      import java.util.ArrayList;
      import nc.entities.source.Source;
      
      
      @Entity(name="User")
      @Table(name="USERS")
      //Named queries expressed the Java Persistence query language
      @NamedQueries(
       { @NamedQuery(
       name="User.findByNickName",
       query="SELECT u FROM User u WHERE u.nick LIKE :nick"
       ),
       @NamedQuery(
       name="User.deleteAll",
       query="DELETE FROM User u"
       ),
       @NamedQuery(
       name="User.selectAll",
       query="SELECT u FROM User u"
       )
      
       }
      )
      public class User {
      
       private int userId;
       private String name;
       private String password;
       private String nick;
       private Collection<Source> sources;
      
       /** Entities must have a public no-arg constructor */
       public User() { }
      
       @Id
       @TableGenerator(name="USER_ID_GENERATOR",
       table="ID_GEN",
       pkColumnName="GEN_KEY",
       valueColumnName="GEN_VALUE",
       pkColumnValue="USER_ID",
       allocationSize=1)
       @GeneratedValue(strategy=GenerationType.TABLE,generator="USER_ID_GENERATOR")
       public int getUserId() { return userId; }
       public String getName() { return name; }
       public String getPassword(){ return password; }
       public String getNick() { return nick; }
      
       public void setUserId(int id) { this.userId=id; }
       public void setName(String name) { this.name=name; }
       public void setPassword(String password){ this.password=password; }
       public void setNick(String nick) { this.nick=nick; }
      
       // N:M bidirectional - TO
       @ManyToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY,mappedBy="users",targetEntity=Source.class)
       public Collection<Source> getSources() { return sources;}
       public void setSources(Collection<Source> sources) { this.sources = sources;}
      
      }


      and here's the code of entity Source
      package nc.entities.source;
      
      
      import javax.persistence.*;
      import java.util.Collection;
      import java.util.ArrayList;
      import nc.entities.user.User;
      import nc.entities.feed.Feed;
      
      @Entity(name="Source")
      @Table(name="SOURCES")
      @NamedQueries(
       { @NamedQuery(
       name="Source.findByUrl",
       query="SELECT s FROM Source s WHERE s.url LIKE :url"
       ),
       @NamedQuery(
       name="Source.deleteAll",
       query="DELETE FROM Source s"
       )
       }
      )
      public class Source {
      
       private int sourceId;
       private String url;
       private Collection<User> users;
       private Collection<Feed> feeds;
      
       /** Entities must have a public no-arg constructor */
       public Source(){ }
      
       public void setUrl(String url){this.url=url;}
       public void setSourceId(int id){this.sourceId=id;}
      
       @Id
       @TableGenerator(name="SOURCE_ID_GENERATOR",
       table="ID_GEN",
       pkColumnName="GEN_KEY",
       valueColumnName="GEN_VALUE",
       pkColumnValue="SOURCE_ID",
       allocationSize=5)
       @GeneratedValue(strategy=GenerationType.TABLE,generator="SOURCE_ID_GENERATOR")
       public int getSourceId(){return sourceId;}
       public String getUrl() {return url;}
      
       // N:M: bidirectional - FROM
       @ManyToMany(cascade={CascadeType.ALL}, fetch=FetchType.LAZY, targetEntity=User.class)
       //@JoinTable(name="SOURCE_USER")
       @JoinTable(name = "SOURCE_USER",
       joinColumns = @JoinColumn(name = "SOURCE_ID", referencedColumnName="sourceId"),
       inverseJoinColumns = @JoinColumn(name = "USER_ID", referencedColumnName="userId"))
       public Collection<User> getUsers() { return users; }
       public void setUsers(Collection<User> users) { this.users = users; }
      
       // 1:N unidirectional: FROM
       @OneToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY)
       public Collection<Feed> getFeeds() { return feeds; }
       public void setFeeds(Collection<Feed> feeds) { this.feeds = feeds; }
      
      }


      The table is SOURCE_USER is created correctly, users and sources are also stored correctly! The function getUsers or getSources works fine if some data are inserted manually in the SOURCE_USER table, but if I perform a setUsers nothing is inserted in that table.

      Here's the code of a session bean where I try to insert relationship data:

      @Stateless
      @Remote(UserSessionFacade.class)
      @TransactionManagement(TransactionManagementType.CONTAINER)
      public class UserSessionFacadeBean implements UserSessionFacade {
       static Logger logger=Logger.getLogger(UserSessionFacadeBean.class);
       @Resource // Injection using annotations
       private SessionContext sessionContext;
       //@PersistenceContext(type=PersistenceContextType.EXTENDED,unitName="NewsCrawlerPu")
       @PersistenceContext//(unitName="NewsCrawlerPu")
       private EntityManager manager;
      
       @TransactionAttribute(TransactionAttributeType.REQUIRED)
       public void createSources(UserDTO userDTO) {
       if (userDTO == null) { logger.error("UserDTO passed is null!");return; }
       logger.debug("Adding sources to a user - dto passed is:".concat(userDTO.toString()));
       if(userDTO.getId()!=null || userDTO.getNick()!=null ){
       User user=null;
       if(userDTO.getId()!=null)
       user = manager.find(User.class,userDTO.getId());
       else
       user = findUserByNick(userDTO.getNick());
       if(user==null){
       logger.error("User not found in Db..");
       //Abort if user does not exist
       sessionContext.setRollbackOnly();
       }
       else{
       logger.trace("User found in db");
       SourceDTO[] s=userDTO.getSources();
       Collection<User> list;
       Object o=null;
       int length;
       if(s!=null && (length=s.length)!=0){
       for (int i = 0; i < length; i++) {
       logger.trace("for cicle: search for "+s.getUrl());
       Source src = null;
       Query q=manager.createNamedQuery("Source.findByUrl");
       logger.trace("Creating namedQuery...");
       try{
       o=q.setParameter("url",s.getUrl()).getSingleResult();
       }catch(NoResultException e){
       logger.warn("Source "+s.getUrl()+" not found!");
       o=null;
       }catch(NonUniqueResultException e){
       logger.error("No unique result found!");
       o=null;
       }catch(Exception e){
       logger.error("Wrong query: MUST be a SELECT!");
       o=null;
       }
       if(o!=null){
       src=(Source)o;
       logger.debug("Source already present in Db: ");
       logger.debug("checking if user already hold it");
       Collection <User>coll = src.getUsers();
       Iterator<User> iter = coll.iterator();
       boolean found = false;
       while (iter.hasNext() && found==false) {
       User u = iter.next();
       logger.trace("User: "+u.getUserId()+", "+u.getName());
       if (u.getUserId()==user.getUserId())
       found = true;
       }
       list = new ArrayList(coll);
       if (!found) {
       logger.debug("Register user to that Source");
       list.add(user);
       src.setUsers(list);
       manager.merge(src);
       manager.refresh(src);
       } else {
       logger.debug("User already registered to that Url");
       }
       }else{
       logger.debug("New Source (not found in Db): adding user to it");
       src=SourceAssembler.createSource(s);
       list = new ArrayList<User>();
       list.add(user);
       src.setUsers(list);
       manager.persist(src);
       manager.refresh(src);
       }
       }
       }else{
       logger.warn("No source to add...Strange!");
       }
       }
       }else{
       logger.error("Missing user id in the passed DTO or id not valid!");
       }
       }
      .....
      .....
      


      Any suggest?

        • 2. Re: ManyToMany relationship do not work properly
          Flavio Pompermaier Newbie

          I'm working with Google Web Toolkit so Exception are not serialized..However I told hibernate to print sql queries and no insert was performed..however now I'll try to see the Exception and I'll report it on the forum! For the moment, do you see something wrong of declaration of relationships?

          • 3. Re: ManyToMany relationship do not work properly
            Flavio Pompermaier Newbie

            Now I'm able to see the Exception..the problem occurs when I try to register a user to a source that does not exist yet. The Exception throwed is:

            javax.ejb.EJBTransactionRolledbackException: org.hibernate.HibernateException: this instance does not yet exist as a row in the database
             [java] at org.jboss.ejb3.tx.Ejb3TxPolicy.handleInCallerTx(Ejb3TxPolicy.java:87)
             [java] at org.jboss.aspects.tx.TxPolicy.invokeInCallerTx(TxPolicy.java:130)
             [java] at org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:195)
             [java] at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
             [java] at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)
             [java] at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
             [java] at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:62)
             [java] at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
             [java] at org.jboss.aspects.security.AuthenticationInterceptor.invoke(AuthenticationInterceptor.java:77)
             [java] at org.jboss.ejb3.security.Ejb3AuthenticationInterceptor.invoke(Ejb3AuthenticationInterceptor.java:106)
             [java] at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
             [java] at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:46)
             [java] at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
             [java] at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:106)
             [java] at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
             [java] at org.jboss.ejb3.stateless.StatelessContainer.localInvoke(StatelessContainer.java:214)
             [java] at org.jboss.ejb3.stateless.StatelessContainer.localInvoke(StatelessContainer.java:184)
             [java] at org.jboss.ejb3.stateless.StatelessLocalProxy.invoke(StatelessLocalProxy.java:81)
             [java] at $Proxy203.createSources(Unknown Source)
             [java] at nc.session.facade.admin.AdminSessionFacadeBean.addSourceToUser(AdminSessionFacadeBean.java:191)
            


            I also found a patch to this...I entered this lines in the code:

            logger.debug("New Source (not found in Db): adding user to it");
             src=SourceAssembler.createSource(s);
             logger.debug("Source created: "+src.getSourceId()+", "+src.getUrl());
             manager.persist(src);
             manager.flush();
             list = new ArrayList<User>();
             list.add(user);
             src.setUsers(list);
             manager.persist(src);
            


            However, why do I need to make the source persistent and synching with the database before adding user to it?? In all examples the persist method is called only once and without any flush!!

            Any suggest?