JPA implementation patterns: Using UUIDs as primary keys

Albert Sikkema

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.

Comments (13)

  1. [...] 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] [...]

  2. Kenneth - Reply

    June 11, 2009 at 8:16 pm

    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 :)

  3. Jens Göring - Reply

    July 15, 2009 at 2:41 pm

    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.

  4. Peter Schuler - Reply

    July 15, 2009 at 7:58 pm

    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.

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

  6. Fernando Machado - Reply

    July 21, 2009 at 4:21 am

    You can use UUIDs with EclipseLink as well.

    http://wiki.eclipse.org/EclipseLink/Examples/JPA/CustomSequencing

  7. Michael Dowling - Reply

    July 22, 2009 at 6:02 am

    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

  8. [...] 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 [...]

  9. Chris Burnley - Reply

    July 23, 2009 at 6:15 am

    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 ?

  10. Albert Sikkema - Reply

    August 4, 2009 at 9:28 pm

    All,

    Really nice to see so many responses here. One of the things which triggered me to write this blog was that I always went for the 'easy' way in terms of selecting a primary key strategy without giving it much thought. I'm no UUID expert in any way although I found it very useful and I'll use it again more often.

    @Micheal: Do you have an example of your pool implementation?

    @Jens: We felt comfortable to use the java.util.UUID implementation for our specific use-case, but I agree with you that for true universal uniqueness you should take a different implementation. Thanks for the links!

  11. Ralph - Reply

    September 2, 2009 at 1:44 pm

    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();
    }
    }
    }

  12. cdiesse - Reply

    December 11, 2009 at 5:47 pm

    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 ?

  13. luk@synergetics.be - Reply

    July 20, 2011 at 5:56 pm

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

Add a Comment