user19254373
user19254373

Reputation:

Which choice is better for generating equals() and hashCode() methods in Hibernate?

I create equals() and hashCode() methods in each Hibernate entity (if there is no inheritance) in my Java app. However, I am a little bit confused and wanted to be sure if I define it properly. Could you please clarify the following issues?

  1. In IntelliJ, when I use the "Generate" feature (via Alt + Insert), there are some templates like IntelliJ Default, Java 7+, etc. Which template should I use?

  2. When using the "Generate" feature, which field should I include in my equals() and hashCode() methods of the following entity?

    @Entity
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        private String name;
    
        @Column(unique = true)
        private String email;
    
        // getter, setter, constructor...
    }
    
  3. Should I use annotation for simple and easy usage like @EqualsAndHashCode? Or is it not flexible and should I prefer implementing explicitly?

Upvotes: 0

Views: 1256

Answers (2)

CaptainPyscho
CaptainPyscho

Reputation: 336

Use the option that generates fewer code and no need 3rd party libraries. I prefer Java7+. Include just the primary key field because the important thing is to verify if 2 differents instances are representing the same row in database. There's no need to verify if all field values are the same.

You can read more about here

Upvotes: -1

rzwitserloot
rzwitserloot

Reputation: 103254

One rather crucial issue to consider is what a User object represents.

It represents a row in the database

That means if the unid is unset (i.e. you haven't save()d it yet) then no 2 user objects can be identical to each other. After all, if you make 2 instances of User, set every value (other than id which you don't mess with) to the same thing, and then save() both, you have 2 rows: Thus proving they aren't identical. However, to stick with java rules, they will be identical if there is reference identity (i.e. this == other).

When unid is set then the only thing that decides whether 2 User objects are identical, is if their unid is identical. All other properties are irrelevant - after all, if you query the same DB for the same row (getting a separate User instance for each, that have identical values), and then you setEmail on one of them, then the email field is no longer identical, but it doesn't matter: They refer to the same row, save one and the other changes along, thus, they are identical.

Conclusion: Write your own equals method that looks like:

if (other == null) return false;
if (other.getClass() != User.class) return false;
if (this == other) return true;
if (this.id == null || other.id == null) return false;
return this.id.equals(other.id);

And hashCode can just be id.hashCode(), returning 0 or 31 if you prefer (or any other prime, won't make much of a difference in practice) if id is null.

It represents a User

In that case, the fact that you can save() them, and that they also have an id representing absolutely nothing relevant about the user, just representing an 'implementation detail' of the backing storage system, you would compare everything except id - Any 2 User object whose name and email are equal, are equal, even if the objects happen to represent 2 separate rows (somehow - you have a uniqueness constraint on email. But you can still make that happen - make a new instance that you haven't saved yet, the SQLException won't occur until you save it).

But those are each others exact opposite!

Yeah. They are. Annoying, isn't it? One checks only id, the other checks everything except id.

Hibernate/JPA is confused about what it thinks it wants, peddling itself as both a database abstraction (the first 'take') as well as an object persistence framework (which steers much closer to the second case), and thus I still haven't actually seen which of these 2 views is 'preferred' by hibernate.

Hence, uh.. pick one, I guess. I think the first one is far more 'correct' and far more likely to do what you want (keep in mind that hitting every property might cause quite the cascade in selects to fetch all those values, especially if you have interconnected stuff, i.e. references to other tables, that's one of a few reasons why the first view is 'better'), however, in my experience, it's less commonly deployed.

If you want to choose the second option, you can just use lombok's stuff. Make sure to mark id with @EqualsAndHashCode.Exclude. For the first - easier to just write it yourself, see snippet above.

Upvotes: 6

Related Questions