-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 
Author Message
 Post subject: MappedSuperClass using id to calculate equals/hashCode
PostPosted: Mon Oct 03, 2016 3:59 am 
Newbie

Joined: Mon Oct 03, 2016 3:11 am
Posts: 2
Hello,
I have simple test using a AbstractClass as MappedSuperClass and using the id to calculate the hashCode and using the equals method (Hibernate 5.0.7 / JPA):

Code:
@MappedSuperclass
public abstract class AbstractEntity implements Serializable {

     private static final long serialVersionUID = -921930556963950805L;

    @Id
    @Column(name = "C_ID", unique = true, updatable = false, nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected Long id;

    public Long getId() {
        return this.id;
    }
    public void setId(final Long id) {
        this.id = id;
    }
    /**
     * Two entities are considered equal if they are of the same class and have the same ID.
     * An entity that has no ID (i.e. it is not persistent yet) is only equal to itself.
     */
    @Override
    public boolean equals(Object obj) {
           if (this == obj) {
                 return true;
           }
           if (obj == null) {
                 return false;
           }
           if (!(obj instanceof AbstractEntity)) {
                 return false;
           }
           AbstractEntity other = (AbstractEntity) obj;
           if (id == null) {
                 if (other.id != null) {
                        return false;
                 }
           } else if (!id.equals(other.id)) {
                 return false;
           }
           return true;
    }
    /**
     * Use the id to calculate the hashCode.
     */
    @Override
    public int hashCode() {
           final int prime = 31;
           int result = 1;
           result = prime * result + ((id == null) ? 0 : id.hashCode());
           return result;
    }
}


And implementing two classes that using the abstract class:

Code:
@Entity
@Table(name = "T_ADDRESS")
public class Address extends AbstractEntity implements Serializable {
      private static final long serialVersionUID = -2096844448338376419L;

      /**
       * The associated <code>User</code> (handles a bi-directional relationship).
       */
      @ManyToOne
      @JoinColumn(name = "JOIN_COL_ADDRESS_USER")
      private User user;

      public Address() {

      }
      public User getUser() {
             return this.user;
      }
      /**
       * The relationship is bidirectional,
       * the relationship has to be removed manually on the other side.
       */
      public void addUser(@NotNull final User user) {
             this.user = user;
             user.internalAddAddress(this);
      }
      /**
       * The relationship is bidirectional,
       * the relationship has to be removed manually on the other side.
       */
      public void removeUser() {
             if (this.user != null) {
                   this.user.internalRemoveAddress(this);
                   this.user = null;
             }
      }     
}


and

Code:
@Entity
@Table(name = "T_USER")
public class User extends AbstractEntity implements Serializable {
      private static final long serialVersionUID = 4283786193325586932L;

      /**
       * The <code>Address</code>es of this user (handles bi-directional
       * relationship).
       */
      @OneToMany(mappedBy = "user", cascade = { CascadeType.ALL }, orphanRemoval = true)
      private final Set<Address> addresses = new LinkedHashSet<>();

      public User() {

      }
      // _____________________________________________________________
      // Address handling.
      // -------------------------------------------------------------
     
      public Set<Address> getAddresses() {
             return Collections.unmodifiableSet(this.addresses);
      }
      public void addAddress(final Address address) {
             address.addUser(this);
      }
      public void removeAddress(final Address address) {
             address.removeUser();
      }
      /**
       * The relationship is
       * bidirectional, the relationship has to be added manually on the other
       * side.
       */
      protected void internalAddAddress(final Address address) {
             this.addresses.add(address);
      }
      /**
       * The relationship is
       * bidirectional, the relationship has to be removed manually on the other
       * side.
       */
      public void internalRemoveAddress(final Address address) {
             [color=#FF0000]this.addresses.remove(address);[/color]
      }
}


The user handles a OneToMany Relationship to the Address.
Adding a Address to Users collection works.

User user = new User();
Address address = new Address();
user.addAddress(address);

entityManager.persist(address);
entityManager.persist(user);

But removing a address from the addresses collection does not work.

The method internalRemoveAddress is invoked and the address should be invoked from the collection, but it does not work.
The two affected elements (in collection and the parameter) are equal using 'collectionAddressElement.equals(address)'
But if I change the attribute id in the equals and hashcode method with a nother value like a uuid the remove works fine.

Where is my mistake?


Top
 Profile  
 
 Post subject: Re: MappedSuperClass using id to calculate equals/hashCode
PostPosted: Mon Oct 03, 2016 4:30 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1628
Location: Romania
The mistake is that you are using an auto-generated id that changes its hashCode value after being persisted.

To fix this issue, check out this blog post which explains in great details how you should implement equals and hashCode when using the entity identifier. Therefore, the equals can use the entity identifier, but the hashCode should remain constant:

Code:
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof AbstractEntity)) return false;
    AbstractEntity that = (AbstractEntity) o;
    return Objects.equals(getId(), that.getId());
}

@Override
public int hashCode() {
    return 31;
}


Top
 Profile  
 
 Post subject: Re: MappedSuperClass using id to calculate equals/hashCode
PostPosted: Mon Oct 03, 2016 5:02 am 
Newbie

Joined: Mon Oct 03, 2016 3:11 am
Posts: 2
Thanks for your fast reply, thats it.
The link helps for explanation.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.