UUID as a database key

Is it a good idea to use universally unique identifier (UUID) as a database key? Or is it better to use auto increment Long value?
We can easily create random UUID by using this method in Java:
UUID uuid = UUID.randomUUID();
UUID example: 41b62562-2362-11ea-978f-2e728ce88125.

UUID type is longer than Long type. UUID type has 16 bytes, Long type has 8 bytes. Using classic auto increment Long consumes less resources than UUID. However, UUID has many advantages:

  • Easy to scale. We do not need a central unit to generate UUIDs. We can combine or split components even from different environments without worry about duplication.
  • UUID can be generated in Java and we do not have to wait for the result from the database.
  • It is more secure, attackers cannot simply increment keys to get secret data. Of course we have to check right always, but this can save us in case of a developer mistake.

Tips:

  • Always store UUID in the database as UUID type, not as Varchar type.
  • It is a good idea to store also auto increment Long as well. It is useful when we want to implement pagination. It is important to have a clear sequential order defined. Otherwise, we are at risk of duplicate paging values.

Do not use UUID type in Java directly. This is a better approach:

public class TaskId {
   private final UUID id;

   private TaskId(UUID id) {
      this.id = id;
   }

   public UUID getId() {
      return id;
   }

   public static TaskId fromUUID(UUID uuid) {
      if(uuid == null) {
         return null;
      }
      return new TaskId(uuid);
   }

   public static TaskId fromString(String val) {
      if(val == null) {
         return null;
      }
      return new TaskId(UUID.fromString(val));
   }

   @Override
   public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      TaskId that = (TaskId) o;
      return Objects.equals(id, that.id);
   }

   @Override
   public int hashCode() {
      return Objects.hash(id);
   }

   @Override
   public String toString() {
      return id.toString();
   }
}


It looks like a lot of boilerplate code. But you really appreciate it in the case of a large project, where it is clear what type of value it is. Calling this method:

public void storeTask(DocumentId documentId, TaskId taskId) {...}

is more clear and type safe than

public void storeTask(UUID documentId, UUID taskId) {...}

Leave a Reply