Spring Data JPAでIDとしてUUIDv7を利用する
この記事は jig.jp Advent Calender 2024 の13日目の記事です。
初めまして、じんたんと申します。
最近、家の電気毛布が2枚連続で壊れてしまいとても悲しい気持ちです。どうぞよろしくお願いします。
Spring Data JPA のデフォルト実装である HibernateとMySQLを利用した記事となっております。
UUIDv7を使いたい
- 連番IDと違いIDを推測されにくい
- 時系列ソートが出来るのでUUIDv4よりもインデックス効率がいい
- 時系列部分と乱数部分があるので衝突しにくい
などの理由でUUIDv7を利用することになりました。
既存のUUID生成アノテーション
Hibernateがデフォルトで提供するUUID生成用アノテーションには以下のものがあります。
JPA の@GeneratedValue
@GeneratedValue(strategy = GenerationType.UUID) // UUIDv4を生成
Hibernate の独自アノテーション@UuidGenerator
@UuidGenerator(style = Style.TIME) // UUIDv1 を生成
@UuidGenerator(style = Style.RANDOM) // UUIDv4 を生成
これらのアノテーションではUUIDv7 を生成することはできませんでした。
UUIDv7を生成するカスタムアノテーションを作成する
UUIDv7を生成するカスタムアノテーションを作成していきましょう。
依存関係の追加
まずはUUIDv7を生成するためにjava-uuid-generator
ライブラリを使用します。
Mavenを使用している場合、pom.xml
に以下を追加してください。
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>5.1.0</version>
</dependency>
UUIDv7生成クラスを作成
以下のようにHibernateのIdentifierGenerator
を実装し、UUIDv7を生成するクラスを作成します。
import java.io.Serializable;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.TimeBasedEpochGenerator;
public class UuidV7Generator implements IdentifierGenerator {
private static final TimeBasedEpochGenerator GENERATOR;
static {
GENERATOR = Generators.timeBasedEpochGenerator();
}
@Override
public Serializable generate(SharedSessionContractImplementor session, Object obj) {
return GENERATOR.generate();
}
}
カスタムアノテーションを作成
エンティティで簡潔に利用できるようにするため、カスタムアノテーションを作成します。
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.hibernate.annotations.IdGeneratorType;
@IdGeneratorType(UuidV7Generator.class)
@Retention(RUNTIME)
@Target({FIELD, METHOD})
public @interface GenerateUuidV7 {
}
エンティティでの利用
エンティティのIDフィールドにアノテーションを適用します。
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import java.util.UUID;
@Entity
public class MyEntity {
@Id
@Column(columnDefinition = "BINARY(16)")
@GenerateUuidV7
private UUID id;
// 他のフィールドやメソッド
}
MySQLはネイティブでUUID型をサポートしていないので今回はBINARY(16) でUUIDを保存しました。Hibernateで発行されるクエリで自動的にUUIDとBINARYの相互変換をしてくれたのでよかったです。
動作確認
エンティティを保存する際にUUIDv7が自動生成されます。
MyEntity entity = new MyEntity();
entityRepository.save(entity);
System.out.println("Generated ID: " + entity.getId());
生成されたUUIDはUUIDv7の形式に準拠し、順序付きの一意な値となります。
感想
UUIDv7を生成するライブラリはjava-uuid-generator
以外にも沢山あるので別のものを利用しても大丈夫だと思います。
UUIDv7でもjava.util.UUID
はそのまま使えたので優秀だなと思いました。
Discussion