9 Replies Latest reply on Sep 14, 2010 10:13 AM by Derk Dukker

    HashSet remove() problem...

    Derk Dukker Novice

      Hi all,


      I have a problem with the hashset. I try to remove an object from it but it resolves into false. Let me explain by code:




      public String removeTeam(Team team) {
                log.debug("remove team");
                log.debug("team ID: " + team.getId());
                log.debug("team.toString() " + team.toString());
                
                Planning planning = planningHome.getInstance();
                log.debug("planning ID: " + planning.getId());
                log.debug("planning amount of teams: " + planning.getTeams().size());
                for(Team _team : planning.getTeams() ){
                     log.debug("planning team ID " + _team.getId() );
                     log.debug("planning team.toString() " + _team.toString() );
                     log.debug("team vs planning team equals? " + team.equals(_team));
                }
                log.debug("removed or what? " + planning.getTeams().remove(team));
                log.debug("planning amount of teams after removal: " + planning.getTeams().size());
                planningHome.update();
                
                return "removeTeam";
           }



      The output:


      19:09:51,939 DEBUG [ScheduleAction] remove team


      19:09:51,940 DEBUG [ScheduleAction] team ID: 1


      19:09:51,940 DEBUG [ScheduleAction] team.toString() eu.emolife.commitment.entity.project.Team@1


      19:09:51,940 DEBUG [PlanningHome] getInstance is called, trying to return a planning


      19:09:51,940 DEBUG [ScheduleAction] planning ID: 1


      19:09:51,940 DEBUG [ScheduleAction] planning amount of teams: 1


      19:09:51,940 DEBUG [ScheduleAction] planning team ID 1


      19:09:51,940 DEBUG [ScheduleAction] planning team.toString() eu.emolife.commitment.entity.project.Team@1


      19:09:51,941 DEBUG [ScheduleAction] team vs planning team equals? true


      19:09:51,941 DEBUG [ScheduleAction] removed or what? false


      19:09:51,941 DEBUG [ScheduleAction] planning amount of teams after removal: 1


      19:09:51,943 DEBUG [PlanningHome] updated entity eu.emolife.commitment.entity.department.Planning 1


      here the Planning class code:




      @Entity
      public class Planning implements Serializable {
      
           /**
            * 
            */
           private static final long serialVersionUID = 7257535247848794177L;
      
           private Long id;
           private Long version;
           private Date date;
           private ShiftDay shiftDay;
           private Set<Team> teams = new HashSet<Team>(0);
           private Project project;
      
           @Id
           @GeneratedValue(strategy = GenerationType.AUTO)
           public Long getId() {
                return id;
           }
      
           public void setId(Long id) {
                this.id = id;
           }
      
           @Version
           @Column(name = "obj_version", nullable = false)
           public Long getVersion() {
                return version;
           }
      
           public void setVersion(Long version) {
                this.version = version;
           }
      
           public Date getDate() {
                return date;
           }
      
           public void setDate(Date date) {
                this.date = date;
           }
      
           @OneToOne
           public ShiftDay getShiftDay() {
                return shiftDay;
           }
      
           public void setShiftDay(ShiftDay shiftDay) {
                this.shiftDay = shiftDay;
           }
      
           @OneToMany(cascade = CascadeType.ALL)
           public Set<Team> getTeams() {
                return teams;
           }
      
           public void setTeams(Set<Team> teams) {
                this.teams = teams;
           }
      
           @ManyToOne
           public Project getProject() {
                return project;
           }
      
           public void setProject(Project project) {
                this.project = project;
           }
           
           @Override
           public boolean equals(Object obj) {
                if (this == obj) return true;
               if (obj == null || !(obj instanceof Planning))
                   return false;
      
               Planning other = (Planning)obj;
      
               if (id == other.getId()) return true;
               if (id == null) return false;
      
               // equivalence by id
               return id.equals(other.getId());
           }
           
           public int hashCode() {
               if (id != null) {
                   return id.hashCode();
               } else {
                   return super.hashCode();
               }
           }





      here the Team class code:




      @Entity
      public class Team implements Serializable {
      
           /**
            * 
            */
           private static final long serialVersionUID = -1785144411928625999L;
      
           private Long id;
           private Long obj_version;
           private String name;
           private Date date;
           private Set<ProjectTeamEmployee> projectTeamEmployees = new HashSet<ProjectTeamEmployee>(0);
           private ScoreCard scoreCard;
      
           @Id
           @GeneratedValue(strategy = GenerationType.AUTO)
           public Long getId() {
                return id;
           }
      
           public void setId(Long id) {
                this.id = id;
           }
      
           @Version
           public Long getObj_version() {
                return obj_version;
           }
      
           public void setObj_version(Long objVersion) {
                obj_version = objVersion;
           }
      
           public String getName() {
                return name;
           }
      
           public void setName(String name) {
                this.name = name;
           }
      
           @Temporal(TemporalType.TIMESTAMP)
           public Date getDate() {
                return date;
           }
      
           public void setDate(Date date) {
                this.date = date;
           }
      
           @OneToMany(cascade = CascadeType.ALL)
           public Set<ProjectTeamEmployee> getProjectTeamEmployees() {
                return projectTeamEmployees;
           }
      
           public void setProjectTeamEmployees(Set<ProjectTeamEmployee> projectTeamEmployees) {
                this.projectTeamEmployees = projectTeamEmployees;
           }
      
           @OneToOne(cascade = CascadeType.ALL)
           public ScoreCard getScoreCard() {
                return scoreCard;
           }
      
           public void setScoreCard(ScoreCard scoreCard) {
                this.scoreCard = scoreCard;
           }
           
           @Override
           public boolean equals(Object obj) {
                if (this == obj) return true;
               if (obj == null || !(obj instanceof Team))
                   return false;
      
               Team other = (Team)obj;
      
               if (id == other.getId()) return true;
               if (id == null) return false;
      
               // equivalence by id
               return id.equals(other.getId());
           }
           
           public int hashCode() {
               if (id != null) {
                   return id.hashCode();
               } else {
                   return super.hashCode();
               }
           }





      I can't see what I'm doing wrong...



        • 1. Re: HashSet remove() problem...
          Tim Evers Master

          Can you try something for me I want to check if my theory is right.


          Make your hashCode() methods just return 0 and see if you still have the problem.

          • 2. Re: HashSet remove() problem...
            Tim Evers Master

            By the way if doing what I suggested fixes your problem then take a look at this link http://www.vipan.com/htdocs/hashcode_help.html


            You really shouldn't implement hashcode where it uses a value that can change. Essentially if the object is a mutable object then you just have to return 0 (or any other number for that matter). Sure this means your hash just turns into a list but at least it works. :)

            • 3. Re: HashSet remove() problem...
              Leo van den berg Master

              Hi,


              Are the Team instances persisted in  the DB or not yet saved. I assume you are using a extended EntityHome for planningHome (?) The Home component doesn't handle very well associations, because it uses the finf-method which leaves all associated objects (SET)  in a not-yet-loaded-but-I-have-a-proxy-for-you state, but reads the ID's. This is NOT a bug, but a great Hibernate feauture.


              You can solve this the dirty way by reading some attributes of the Team-object. Seam will take care of loading the object for you, although this is a VERY inefficient way A far better approach is overriding the getInstance method and loading the object yourself or making a specific components which takes care of loading/saving etc and where you define the Hql yourself (that's what I did).


              Leo

              • 4. Re: HashSet remove() problem...
                Derk Dukker Novice

                Hi all,


                Thank you for your responses. I have set the hashcode to 0 and that indeed did the trick.


                Leo (Dutch?) you are right. I use an extended EntityHome. Could it also be that switching from Long id to long id would solve the case. Because Long is an object en long a primitive? Next thing is that the hashcode needs to return an int instead of Long/long :(


                also see this link: http://community.jboss.org/wiki/EqualsandHashCode


                Does anybody have any idea what the best implementation of hashcode and equals is or does anybody knows a better solution to this problem?


                • 6. Re: HashSet remove() problem...
                  Leo van den berg Master

                  Hi,


                  HashCode calculation is not really complex and every IDE has tools to do that for you.The Java book Effective java explains the background of equals and hasCode very well. It basically states that you should use all significant field to calculate the hashCode and finally multiply this with the prime 31.


                  The Long and long problems doesn't matter because of the autoBoxing and casting of Java (since 1.5).


                  And yes, I am Dutch.


                  Leo van den Berg

                  • 7. Re: HashSet remove() problem...
                    Tim Evers Master

                    I think the most important thing to remember is that you cannot use any field in your hashcode calculation if that field's value can change.


                    I know in the perfect world the id attribute shouldn't change and seems like a logical field to use. But, ids don't get set at instance creation time so theoretically they are modifiable.


                    I don't know the solution to your problem, I just know why the problem exits. :) I know, I'm not much help. But the idea given in the link provided by Derk Dukker is probably one of the best solutions you can go for.


                    Make all your entities subclass an abstract class that generates a business ID for every entity. (Yes this means a second identifying column in your db but it probably is worth it). Then once an instance is instantiated your hashcode can use this businessID to calculate the hashcode. This will always work. (Probably makes your equals method a tad better too).

                    • 8. Re: HashSet remove() problem...
                      Derk Dukker Novice

                      Hi Leo,


                      Thank you for your explanation! I did generate the equals and hashcode with eclipse and removed everything except the date and name of the team. Now it works again!


                      I'm also Dutch :)


                      Regards,


                      Derk

                      • 9. Re: HashSet remove() problem...
                        Derk Dukker Novice

                        Hi all,


                        Thank you for all the responses. I haven't chosen for an abstract class but instead override the equals and hashcode with business keys instead of primary keys (id's).


                        Regards,


                        Derk