【Spring Data JPA】登録時に独自シーケンスでカスタムIDを採番

# はじめに

「Spring Data JPA」でデータを登録するときに、
シーケンスで採番したものに接頭辞（プレフィックス）つけた時のコードをまとめています。

　

# 環境

Java 8
PostgreSQL 10.5

　

# やったこと

## 独自Generatorを実装する。（接頭辞を付ける人）

SequenceStyleGeneratorを継承します。
シーケンスで採番をするのは内部（スーパークラス）の処理をそのまま使います。
独自Generatorは採番されたものに prefix を付ける役割です。


## EntityにGeneratorの指定とパラメータを記載する

Entityでシーケンスの指定や接頭辞、フォーマットの指定をします。
シーケンスを指定しないと ```hibernate_sequence``` を使おうとします。
（ここでちょっとハマった）

# ソース

実装したソースです。

## 独自Generator

```java:
import java.io.Serializable;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.LongType;
import org.hibernate.type.Type;

public class PrefixedIdGenerator extends SequenceStyleGenerator {

    public static final String VALUE_PREFIX_PARAMETER = "valuePrefix";
    public static final String VALUE_PREFIX_DEFAULT = "";
    private String valuePrefix;


    public static final String NUMBER_FORMAT_PARAMETER = "numberFormat";
    public static final String NUMBER_FORMAT_DEFAULT = "%d";

    private String numberFormat;

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
        return valuePrefix + String.format(numberFormat, super.generate(session, object));
    }

    @Override
    public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
        super.configure(LongType.INSTANCE, params, serviceRegistry);
        valuePrefix = ConfigurationHelper.getString(VALUE_PREFIX_PARAMETER, params, VALUE_PREFIX_DEFAULT);
        numberFormat = ConfigurationHelper.getString(NUMBER_FORMAT_PARAMETER, params, NUMBER_FORMAT_DEFAULT);
    }
}
```

## Entity

```java:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;

import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

@Entity
@Data
@Table(name = "trial")
public class Trial {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_trial")
    @GenericGenerator(
            name = "seq_trial",
            strategy = "PrefixedIdGenerator",
            parameters = {
                @Parameter(name = PrefixedIdGenerator.SEQUENCE_PARAM, value = "seq_trial"),
                @Parameter(name = PrefixedIdGenerator.VALUE_PREFIX_PARAMETER, value = "B_"),
                @Parameter(name = PrefixedIdGenerator.NUMBER_FORMAT_PARAMETER, value = "%05d") })
    @Column(name = "trial_id")
    private String trialId;

    @Column(name = "trial_name")
    private String trialName;
}
```

## Service

```java:
@Service
public class TrialService {

    @Autowired
    private TrialRepository repository;

    @Transactional
    public void insert() {

        Trial entity = new Trial();
        entity.setTrialName("trial_xxxxxxxx");

        repository.save(entity);
    }
}
```

# 実行時ログ

```
2019/05/24 17:54:18.505 [http-nio-8099-exec-1] DEBUG SqlStatementLogger 92 : 
    select
        nextval ('seq_test')
2019/05/24 17:54:22.313 [http-nio-8099-exec-1] DEBUG SqlStatementLogger 92 : 
    insert 
    into
        trial
        (trial_name, trial_id) 
    values
        (?, ?)
2019/05/24 17:54:22.318 [http-nio-8099-exec-1] TRACE BasicBinder 65 : binding parameter [1] as [VARCHAR] - [trial_xxxxxxxx]
2019/05/24 17:54:22.319 [http-nio-8099-exec-1] TRACE BasicBinder 65 : binding parameter [2] as [VARCHAR] - [B_00001]
```

# 参考にしたサイト
[How to Implement a Custom, Sequence-Based ID Generator](https://thoughts-on-java.org/custom-sequence-based-idgenerator/)

　

以上