Reputation:
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?
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?
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...
}
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
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
Reputation: 103254
One rather crucial issue to consider is what a User
object represents.
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.
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).
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