JPA Implementation Patterns: Using UUIDs as Primary Keys
The default way in JPA for primary keys is to use the @GeneratedValue annotation with the strategy attribute set to one of AUTO, IDENTITY, SEQUENCE, or TABLE. You pick the most appropriate strategy for your situation and that's it.
But you can also choose to generate the primary key yourself.
Using UUIDs
for this is ideal and has some great benefits. In our current project
we've used this strategy by creating an abstract base class our
entities to inherit from which takes care of dealing with primary keys.
@MappedSuperclass
public abstract class AbstractBaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private String id;
public AbstractBaseEntity() {
this.id = UUID.randomUUID().toString();
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof AbstractBaseEntity)) {
return false;
}
AbstractBaseEntity other = (AbstractBaseEntity) obj;
return getId().equals(other.getId());
}
}
Using UUIDs versus sequences in general has been widely discussed on the web, so I won't go into too much detail here. But here are some pro's and con's:
Pros
- Write this base class once and every entity gets an Id for free. If you implement equals and hashcode as above you also throw that one in as a bonus.
- UUID are Universal Unique(what's in a name). This means that you get great flexibility if you need to copy/merge records from location a to b without having to re-generate keys. (or use complex sequence strategies).
- UUIDs are not guessable. This means it can be safer to expose them to the outside world in let's say urls. If this would be good practice is another thing.
Cons
- Performance can be an issue. See http://johannburkard.de/blog/programming/java/Java-UUID-generators-compared.html, Some databases don't perform well with UUIDs (at least when they are stored as strings) for indexes.
- Ordering, Sorting. Speaks for itself.
- JPA specific: you cannot test if a record has already been persisted by checking if the Id field has been set. One might argue if you need such checks anyway.
Conclusion
UUIDs are easy to use, but wouldn't it be nice if the JPA spec would open up to include UUID as a strategy? Some JPA implementations, like Hibernate, already have support for this:
@Id @GeneratedValue(generator="system-uuid")
@GenericGenerator(name="system-uuid",
strategy = "uuid")
From http://blog.xebia.com/
Vincent Partington has more than 10 years of enterprise Java experience. He has written and presented on topics as diverse as performance, security, RIA, and persistence technologies. Vincent Partington is the Chief Technical Officer of XebiaLabs where he is responsible for their flagship deployment automation product Deployit. Vincent is a DZone MVB and is not an employee of DZone and has posted 14 posts at DZone.
- Login or register to post comments
- 3478 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)










Comments
eriksensei replied on Tue, 2009/09/01 - 7:48am
I think this is information is very useful, if a little low on details. For instance, one of the central problems when using JPA, Hibernate and what have you is that typically, entities which have not yet been persisted have null id's. If you look at, say, the JPA entities that NetBeans generates for you, you will find that this id is used to define the entity's equals() and hashCode() methods.
The equals() method is in turn used by Java's Collections, e.g. to determine whether your entity is in a collection or not, or to delete entities from a collection. Putting entities with null id's into a collection can lead to serious bugs, due to equals() reporting that two entities are equivalent, when in fact they're not. Nastiness ensues; for exampe, you may be deleting the wrong entity from a collection. In addition, changing an object's hashCode() during the lifetime of an object violates Java's contract for hashCode().
Using UUID's instead allows you to avoid these problems. This is one of the many snags you encounter when using certain ORM's; they're supposed to make things easier, but you need to be well aware of what goes on under the hood.
Denis123 replied on Tue, 2009/09/01 - 2:15pm
alexander.a replied on Tue, 2009/09/01 - 2:34pm
Rainer P. replied on Tue, 2009/09/01 - 3:54pm
Nice article!
One question: Why do you think that only uuids can have a common base class? IMHO you can also have base classes with an long id - what's the difference?
To the performance problem: The most evil thing about uuid alues is the space they need. I had a project with uuids persisted in a varchar(36). Consider not only the private keys but also the foreign keys: In fact, more then 60 % of the database space was used by this keys and the huge indexes built on them!
But in general there are really some good points in using uuids, especially in all of the (many) use cases when you want to move data from one database to other.
I don't see the sorting / ordering problem, sorry. You surely should never rely on sequence-generated ids for sorting; it is just wrong. Or do I have missed the problem here?
@eriksensei and the equal problem with not-yet-persisted entities: Just throw an exception in such cases and you will have no more problem - it is just a (self-defined) error in such cases then to compare "not yet existing" entities - a point which can be tested and avoided easily.
Or follow the tip from G. King to use natural keys instead.
And never forget the compareTo method! TreeMaps e.g. will thank you ;-)
IMHO every entity must have a _good_ compareTo method implementation which really only returns 0 when it _is_ the same entity.
Same helps with the sorting / ordering mentioned in the article.
eriksensei replied on Wed, 2009/09/02 - 8:46am
in response to: rpr
Andy Jefferson replied on Sun, 2009/09/06 - 1:17pm
> UUIDs are easy to use, but wouldn't it be nice if the JPA spec would open up to include UUID as a strategy?
JDO already includes such things in its spec and has for 3+ years.
--Andy (DataNucleus)