Blog

JPA implementation patterns: Using UUIDs as primary keys

03 Jun, 2009

Continuing Vincent Partington‘s blog series about JPA implementation patterns, I would like to add the following
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")

For a list of all the JPA implementation pattern blogs, please refer to the JPA implementation patterns wrap-up.

guest
14 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
trackback

[…] JPA implementation patterns: Using UUIDs as primary keys (Albert Sikkema)Interessanter Ansatz mit Pro und Cons anstatt @GeneratedValue eine UUID in einer abstracten Oberklasse selbst zu generieren [Link] […]

Kenneth
12 years ago

I might be missing something here but how is a private member “private String id” going to be inherited by any other classes?
I also assume you left the getters and setters out for the sake of brievity? Not knocking anything, your article helped me significantly 🙂

Jens Göring
12 years ago

UUID.randomUUID() is not Universal Unique because it is based solely on random numbers (http://en.wikipedia.org/wiki/Uuid#Version_4_.28random.29). It might be improbable to get the same number twice locally, but you should not depend on *universal* uniqueness.
If you need universal uniqueness, you have to rely on a third party UUID library like http://johannburkard.de/software/uuid/ or http://jug.safehaus.org/, which evaluate the network adapter’s MAC address to build a Version 1 UUID using platform dependend code.
>UUIDs are not guessable
If you use UUID Version 1 as mentioned above, this might not hold, see http://en.wikipedia.org/wiki/Uuid#Version_1_.28MAC_address.29
I’ll like to add another Con:
By using UUID, you introduce another dependency to your Domain class – this might be a problem if you for example want to transmit instances of these classes via Gilead to GWT client side code.
I had this problem once and solved it by setting the UUID in the DAO’s insert/update method if it is null.

Peter Schuler
Peter Schuler
12 years ago

Ik think UUID’s certainly have their function. There are however big disadvantages in using them. A big disadvantage is database readabillity. Debugging your database is quit hard when you have to follow primary/foreign key’s based on UUID.
For an interesting discussing about the opinion of the Hibernate developers see this post on the hibernate forum: https://forum.hibernate.org/viewtopic.php?t=967211.

trackback

[…] Using UUIDs as primary keys (guest blog by Albert Sikkema) […]

Fernando Machado
12 years ago

You can use UUIDs with EclipseLink as well.
http://wiki.eclipse.org/EclipseLink/Examples/JPA/CustomSequencing

Michael Dowling
12 years ago

Thanks for the article. I am a big believer in using UUIDs as primary keys for domain objects in the DB. Having used them quite a bit in my projects, I wanted to debate a couple of the cons:
1. Performance can be an issue: Yes, it can, but grabbing a sequence from the DB creates a bigger issue (2 calls per new insert, depending on the RDBMS platform). This can be easily remedied by writing a simple UUID pool object which generates the UUIDs in batch, and doles them out on request. When the pool runs low/out, regenerate.
2. Ordering/Sorting. Indeed, you really cannot order by the primary key in the case of a UUID. But then again, why does one usually order by primary key? Sequential primary keys tend to indicate the order of entity creation. Good DB design principles include having a created_date and a modify_date column as part of the entity (depending on your needs and requirements, of course, but I’d say this is mostly true). With this in mind, you CAN order by the create date.
3. cannot test if a record has already been persisted by checking if the Id field has been set. Uhm, you can write a @GenericGenerator to generate the uuid if you’re using a JPA provider other than hibernate, and it wouldn’t be hard at all. The UUID is only set upon insert call to the DB, so you can do this check. But the OP is right, I question a design that checks for the existence of a PK being set.
My $0.02. Reposting to my blog
-Michael

trackback

[…] using UUIDs as primary keys Tuesday, July 21st, 2009 | Java | michael So, I read someone’s blog post about the pros and cons of using UUIDs as primary keys.  I am a big believer in using UUIDs as […]

Chris Burnley
12 years ago

Should you put this in the constructor ? Isn’t the default constructor going to get executed indirectly via the JPA implementation when the entity is loaded from the database ?

Ralph
Ralph
12 years ago

Hi
Instead of assign the uid inside the constructor you could use an EntityListener. This way the uid is only generated before persist or when somebody calls hashCode() or equals().
@MappedSuperclass
@EntityListeners({AbstractEntity.AbstractEntityListener.class})
public abstract class AbstractEntity implements Serializable {
@Id
@Column(length=36)
private String uid;
@Override
public boolean equals(Object o) {
return (o == this || (o instanceof AbstractEntity && uid().equals(((AbstractEntity)o).uid())));
}
@Override
public int hashCode() {
return uid().hashCode();
}
String uid() {
if (uid == null) {
uid = new UUID().toString();
}
return uid;
}
public static class AbstractEntityListener {
@PrePersist
public void onPrePersist(AbstractEntity abstractEntity) {
abstractEntity.uid();
}
}
}

cdiesse
cdiesse
12 years ago

My main concern about defining Ids in MappedSuperClass, is to define the id but have opportunities to override the GeneratedValue strategy in derived classes.
This is a missing feature in JPA 1.0 and JPA 2.0. Maybe this can be overcome with EntityListeners …
Anythought ?

luk@synergetics.be
10 years ago

I agree with the remark of Ralph and Chris.. should the UUID assignment go in the constructor?

Jens Rantil
Jens Rantil
7 years ago

No, it shouldn’t. I’ve run into entropy starvation on systems because of this.

Explore related posts