UUID Generator (v4, v7, v1)

What is a UUID?

A UUID (Universally Unique Identifier) β€” also called a GUID (Globally Unique Identifier) on Microsoft platforms β€” is a 128-bit value used to uniquely identify something without coordination. The odds of two UUIDs clashing are astronomically low: generating a billion v4 UUIDs per second for a hundred years has roughly the same chance of a collision as an asteroid hitting your server.

A UUID is displayed as 32 hexadecimal digits split by hyphens into five groups: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx, where M indicates the version and N encodes the variant.

UUID versions in practice

The RFC defines seven versions. Only three matter in everyday code:

v4 β€” random (the classic default)

  • 122 bits of cryptographic randomness + 6 bits of version/variant markers
  • No timestamp, no hardware info, fully unpredictable
  • Available in Java since 1.5 via UUID.randomUUID()
  • Weakness: inserting random UUIDs into a database index causes fragmentation

v7 β€” time-ordered (recommended for new projects)

  • 48-bit Unix millisecond timestamp + 74 bits of randomness
  • Sortable by creation time, like auto-incrementing integers
  • Standardized in RFC 9562 (May 2024)
  • Play nicely with B-tree database indexes β€” no fragmentation
  • No Java built-in yet β€” use the uuid-creator library

v1 β€” legacy time + MAC address

  • 60-bit timestamp + 14-bit clock sequence + 48-bit "node" (historically the MAC address)
  • Discouraged today because the node used to leak the machine's MAC address
  • Use only for compatibility with older systems

v4 vs v7 β€” why database engineers prefer v7

Imagine 1 million INSERTs with v4 UUID primary keys. Because each UUID is fully random, every INSERT lands in a different B-tree leaf page. The index becomes fragmented β€” pages fill to 50%, the engine splits them, disk I/O explodes.

With v7, the time prefix means every INSERT goes at or near the end of the index (just like an auto-increment integer). The B-tree stays compact, the OS page cache stays hot, and INSERT throughput is 3–10Γ— higher on common workloads.

If you're starting a new service, default to v7. The only reason to pick v4 is when you explicitly want the creation time to not be observable from the ID.

UUIDs in Java

v4 β€” built into java.util

import java.util.UUID;

UUID id = UUID.randomUUID();
String s = id.toString();         // "a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6"
String noHyphens = id.toString().replace("-", "");

// Parse
UUID parsed = UUID.fromString(s);

v7 β€” with uuid-creator library

The JDK has no v7 support yet. Use uuid-creator:

<dependency>
  <groupId>com.github.f4b6a3</groupId>
  <artifactId>uuid-creator</artifactId>
  <version>6.0.0</version>
</dependency>
import com.github.f4b6a3.uuid.UuidCreator;

UUID v7 = UuidCreator.getTimeOrderedEpoch();       // v7
UUID v4 = UuidCreator.getRandomBased();            // v4
UUID v5 = UuidCreator.getNameBasedSha1(namespace, "unique-name");

v5 β€” name-based (SHA-1 hash of a namespace + name)

Generate a deterministic UUID from a string. Useful when you need the same input to always produce the same UUID (e.g. imported external IDs).

When to use UUIDs (and when not to)

Use a UUID when:

  • You need IDs that can be generated without consulting a central authority
  • Multiple databases/services must mint IDs that won't collide when merged
  • You don't want sequential IDs to leak record counts or creation order
  • A URL-safe, unguessable token is needed (v4 / v7)

Avoid UUIDs when:

  • Your IDs must be short and human-friendly (use nanoid or short-UUID libraries)
  • You need pure sequential ordering with no randomness (use auto-increment)
  • Storage cost matters a lot β€” UUID is 16 bytes vs 8 for BIGINT, and 36 chars as text

Storing UUIDs in a database

Three common approaches:

  • Native UUID type β€” PostgreSQL has UUID (16 bytes). MySQL 8 has UUID_TO_BIN() / BIN_TO_UUID(). Use this when available.
  • BINARY(16) β€” universally supported, compact. Convert to/from UUID in the app layer.
  • VARCHAR(36) β€” simplest, but 2Γ— the storage and slower comparisons. Avoid when you'll store millions.

JPA mapping

@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)  // JPA 3.1+
    @Column(columnDefinition = "uuid")
    private UUID id;
    // …
}

On Hibernate 6+, GenerationType.UUID generates v4 by default. For v7 you'll need a custom ID generator.

Common mistakes

  • Using Math.random()-based UUIDs. Math.random is not cryptographically secure. Always use crypto.getRandomValues() in JS or SecureRandom in Java.
  • Storing UUIDs as VARCHAR(36) on millions of rows. That's 4Γ— the space vs BINARY(16), and 4Γ— the RAM for the index.
  • Exposing v1 UUIDs publicly β€” they leak the server's MAC and approximate time of creation.
  • Assuming v4 UUIDs are ordered. They're not. Tools that sort by UUID will get random ordering.
  • Parsing v7 with UUID.fromString. The JDK method accepts any valid UUID, but treats it as opaque β€” you can't extract the timestamp without extra code.