0 Replies Latest reply on May 22, 2008 11:21 AM by jfrankman

    Duplicate Objects in Entity's mapped OnetoMany collection

    jfrankman Novice

      What can cause duplicate objects to show up in a collection when the SQL data itself is fine? I have a problem where duplicate java objects are showing up in a @OneToMany relationship on one of my entities. I need to figure out why these duplicate objects show up in my collection.

      Here is the model I am working with:

      -ClientVO has a recursive relationship with itself where one ClientVO object may be the parent one or more other ClientVO ojbects.
      -Each ClientVO object has a OnetoMany relationship with ClientLocationVO. The ClientLocationVO object has a composite key as an identifier.

      In the database ClientVO with id 76237 is is the parent of two other ClientVO objects:


      ClientID Name ParentClientID
      76237 Doe Household NULL
      123456 Jane Doe 76237
      789456 John Doe 76237


      Client 76237 has three ClientLocationVO records

      ClientID LocationID LocationType
      76237 111 1
      76237 333 2
      76237 555 3


      The problem that I am having is that when the following EJB query is run, there are six client members in the client members collection instead of just two:


      @NamedQueries( { @NamedQuery(name = "findClientByIdFetchGraph",
       query = "select distinct client from ClientVO client "
       + "left join fetch client.entityLocations entityLocation "
       + "left join fetch client.clientMembers clientMember "
       + "where (client.id = :id) ")
      })


      for (ClientVO subClient : client.getClientMembers())
       {
       System.out.println(subClient.getSearch());
       }


      Output:
      DOE JANE
      DOE JOHN
      DOE JANE
      DOE JOHN
      DOE JANE
      DOE JOHN


      However, if I change the query and remove the client locations join, the correct number of clientmembers is returned in my collection:


      @NamedQueries( { @NamedQuery(name = "findClientByIdFetchGraph",
       query = "select distinct client from ClientVO client "
       + "left join fetch client.clientMembers clientMember "
       + "where (client.id = :id) ")
      })



      for (ClientVO subClient : client.getClientMembers())
       {
       System.out.println(subClient.getSearch());
       }


      Output:
      DOE JANE
      DOE JOHN


      Something is wrong with my query or with my mappings that is causing duplicate objects to show up in the clientMembers collection when the database only contains two records. This only happens when I include a join to the clientLocations in the query. I am looking for ideas as to why this is happening and what I can do about it. Any thoughts or suggestions is appreciated. Here are the two entities in question:



      @Entity
      @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
      @DiscriminatorColumn(name = "CLNTTYPE", discriminatorType = DiscriminatorType.STRING)
      @Table(name = "FBCLIENT")
      @Name("client")
      @Scope(ScopeType.SESSION)
      public class ClientVO implements Serializable
      {
      
       /*
       * , @NamedQuery(name = "findLendersByName", query = "select distinct client
       * from ClientVO client " + "left join fetch client.roles role " + "left
       * join fetch client.entityLocations entityLocation " + "left join fetch
       * entityLocation.locationType locationType " + "left join fetch
       * entityLocation.location location " + "where (client.search like :name and
       * role.id=1) ")
       *
       *
       */
       private static final long serialVersionUID = 2285612284592204149L;
      
       private Long id;
       private String notes;
       private String clientType;
       private String memberNumber;
       private Set<ClientLocationVO> entityLocations = new HashSet<ClientLocationVO>();
       private String search;
       private List<ClientVO> clientMembers = new ArrayList<ClientVO>();
      
       /**
       * Retrieves the value of the id attribute in the class
       *
       * @return id (Long)
       */
       @Id
       @NotNull
       @Column(name = "CLIENTID")
       public Long getId()
       {
       return id;
       }
      
       @OneToMany(mappedBy = "parentClient", fetch = FetchType.LAZY)
       public List<ClientVO> getClientMembers()
       {
       return clientMembers;
       }
      
       @ManyToOne()
       @JoinColumn(name = "parentclientid")
       public ClientVO getParentClient()
       {
       return parentClient;
       }
      
       public void setClientMembers(final List<ClientVO> inChildMembers)
       {
       this.clientMembers = inChildMembers;
       }
      
       public void setParentClient(final ClientVO inParentClient)
       {
       this.parentClient = inParentClient;
       }
      
      
      
       @OneToMany
       @JoinColumns( { @JoinColumn(name = "clientid", referencedColumnName = "CLIENTID") })
       public Set<ClientLocationVO> getEntityLocations()
       {
       return entityLocations;
       }
      }
      
      @Entity
      @Table(name = "FBCLNTLOC")
      @Name("clientLocation")
      public class ClientLocationVO implements Serializable
      {
       private static final long serialVersionUID = 2285612284592204149L;
      
       private ClientLocationPk id = new ClientLocationPk();
       private ClientVO client;
       private Location location;
       private LocationType locationType;
      
       public ClientLocationVO(final LocationType inLocationType, final ClientVO inClient, final Location inLocation)
       {
       this.locationType = inLocationType;
       this.client = inClient;
       this.location = inLocation;
      
       this.id.setClientId(inClient.getId());
       this.id.setLocationId(inLocation.getId());
       this.id.setLocationTypeId(inLocationType.getId());
      
       // Guarantee referential integrity
       // client.getEntityLocations().add(this);
       inLocation.getEntityLocations().add(this);
       }
      
       @Id
       public ClientLocationPk getId()
       {
      
       return id;
       }
      
       @ManyToOne(cascade = CascadeType.ALL)
       @JoinColumn(name = "CLIENTID")
       public ClientVO getClient()
       {
       return client;
       }
      
       @ManyToOne(cascade = CascadeType.ALL)
       @JoinColumn(name = "LOCATIONID")
       public Location getLocation()
       {
       return location;
       }
      
       @ManyToOne(cascade = CascadeType.ALL)
       @JoinColumns( { @JoinColumn(name = "LOCTYPEID", insertable = false, updatable = false) })
       public LocationType getLocationType()
       {
       return locationType;
       }
      
       public void setId(final ClientLocationPk inId)
       {
       this.id = inId;
       }
      
       @Override
       public boolean equals(final Object obj)
       {
       if (this == obj)
       {
       return true;
       }
       if (obj == null)
       {
       return false;
       }
       if (getClass() != obj.getClass())
       {
       return false;
       }
      
       final ClientLocationVO other = (ClientLocationVO) obj;
      
       if (this.client == null)
       {
       if (other.client != null)
       {
       return false;
       }
       }
       else if (!this.client.equals(other.client))
       {
       return false;
       }
      
       if (this.location == null)
       {
       if (other.location != null)
       {
       return false;
       }
       }
       else if (!this.location.equals(other.location))
       {
       return false;
       }
      
       if (this.locationType == null)
       {
       if (other.locationType != null)
       {
       return false;
       }
       }
       else if (!this.locationType.equals(other.locationType))
       {
       return false;
       }
      
       return true;
       }
      
       @Override
       public int hashCode()
       {
       final int prime = 31;
       int result = 1;
      
       result = prime * result + ((this.client == null) ? 0 : this.client.hashCode());
       result = prime * result + ((this.location == null) ? 0 : this.location.hashCode());
       result = prime * result + ((this.locationType == null) ? 0 : this.locationType.hashCode());
      
       return result;
       }
      
      
      }