问题
I hit a problem when writing tests for a database application using JPA2 and EclipseLink:
I add some entity to a database, retrieve it later and want to compare it to an instance which has the values I expect to confirm that the addition worked as I intended.
First I wrote something like
assertEquals(expResult, dbResult);
which failed, because I can't really know the value of id
field, which is generated by the database and therefore dbResult
differs from expResult
which I created with new
and populated manually.
I see two options:
Either I remove the
id
field fromequals
andhashCode
so that the comparison is only based on the "real values". I don't know if this causes problems in the database or elsewhere, though.Or I write my tests to explicitly check every field except
id
manually.
What should I do?
回答1:
You might find a lot of controversy about this one. My stance is that you absolutely don't use a database primary key for anything in your application. It should be completely invisible. Identify your objects in your application by some other property or combination of properties.
On the "testing persistence operations" front, what you really want is probably to check that the fields were saved and loaded correctly and maybe that the primary key got assigned some value when you saved it. This probably isn't a job for the equals method at all.
回答2:
Relying on database generated Ids in your equals
and hashCode
implementation is not advisable. You ought to rely on the truly unique/semi-unique attributes of your classes in checking for equality, and in generating the hashcode values. The Hibernate documentation has an extensive page that discusses this, and the facts therein are applicable to more or less every JPA provider.
The underlying reason for using business keys over database generated values in your equals
and hashCode
implementation is that the JPA provider must actually issue a SELECT
after persisting the entity in the database. If you compare objects using the database generated Ids, then you will end up having an equality test that fails in the following scenarios:
- If
E1
andE2
are entities of class E (that verifies equality using database generated Ids), then ifE1
andE2
will be equal if they haven't been stored in the database yet. This is not what you want, especially if want to storeE1
andE2
in someSet
before persistence. This is worse if the attributes ofE1
andE2
possess different values; theequals
implementation would prevent two significantly different entities from being added to aSet
, and thehashCode
implementation will give you aO(n)
lookup time when entities are looked up from aHashMap
using the primary key. - If
E1
is a managed entity that has been persisted, andE2
is an entity that has not been persisted, then the equality test would deem thatE1
!=E2
in the scenario where all the attribute values ofE1
andE2
(except for the Ids) are similar. Again, this is probably not what you want, especially if you want to avoid duplicate entities in the database that differ only in their database generated Ids.
The equals
and hashCode
implementations therefore ought to use business keys, in order to exhibit consistent behavior for both persisted and unpersisted entities.
回答3:
From the book Hibernate in Action, its recommended to defined a business key and test equality on that. A business key is "a property, or some combination of properties, that is unique for each instance with the same database identity." In other areas it says to not use the id as one of those properties, and don't use values in collections.
回答4:
I would write my test to explicitly check for fields. To make this easy, before performing the assertEqual test, I will set the id of both the expected and actual result to the same predefined value and then use the normal equals method.
Removing ID from equals is not justifiable, just because testing is slightly difficult. You are foregoing serious performance benefits and also code integrity.
来源:https://stackoverflow.com/questions/7340824/should-the-id-field-of-a-jpa-entity-be-considered-in-equals-and-hashcode