이전 포스팅에서 JPA의 ID 생성전략에 대해 작성했는데, 그 외에도 애플리케이션내부에서 또는 데이터베이스에 사전에 직접 정의해 둔 Sequence를 통해 ID를 생성하는 방식도 있다.
이렇게 JPA에서 제공하는 GenerationType이 아닌 직접 생성한 ID Generator를 사용하고 싶을 때 사용하는 것이 @GenericGenerator 어노테이션이다.
@Entity
@Table(name="test_table")
public class TestEntity {
// Generator에 넘겨줄 파라미터값
private static final String ID_GEN_PARAM = "TEST";
@Id
@GenericGenerator(
name = "idGenerator",
type = CustomSequenceGenrator.class
parameters = {
@Parameter(
name = "ID_GEN_PARAM",
value = ID_GEN_PARAM
)
}
)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idGenerator")
@Column(name = "TEST_ID")
private String id;
}
만약 미리 정의해둔 ID 생성 시퀀스가 파라미터를 필요로 하지 않는다면 @GenericGenerator의 parameters 속성은 제외해도 된다.
생성된 값을 id 필드에 매핑해줘야 하기 때문에 @GeneratedValue 어노테이션도 함께 사용해줘야 한다. 이 때 @GeneratedValue의 generator 속성은 @GenericGenerator의 name 속성과 맞춰줘야 한다.
이제 실제 ID 생성을 담당하는 CustomSequenceGenrator.class를 생성해야한다. 이름은 아무렇게나 지어도 상관없는데 중요한건@GenericGenerator에게 type 속성으로 이 클래스를 전달하기만 하면 된다.
hibernate에서 제공하는 IdentifierGenerator 인터페이스를 상속받아 implement 하면된다.
@Slf4j
public class CustomSequenceGenrator implements IdentifierGenerator {
// 파라미터를 사용하지 않으면 필요 없다.
private String ID_GEN_PARAM = "";
@Override
public Serializable generate(
SharedSessionContractImplementor session,
Object object
) throws HibernateException {
Connection connection = null;
try {
ConnectionProvider connectionProvider = session
.getFactory()
.getServiceRegistry()
.getService(ConnectionProvider.class);
connection = connectionProvider.getConnection();
// 파라미터를 사용하지 않는다면 SELECT nextval() 로 해도 된다.
PreparedStatement preparedStatement = connection.prepareStatement(
"SELECT nextval('" + ID_GEN_PARAM + "')");
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
String generatedId = resultSet.getString(1);
return generatedId;
}
} catch (SQLException e) {
throw new HibernateException("Unable to generate sequence", e);
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
log.debug("Unexpected error occurs while closing the connection");
}
}
}
return null;
}
// 파라미터를 사용하지 않는다면 이 메서드는 오버라이드하지 않아도 무관하다.
@Override
public void configure(
Type type,
Properties params,
ServiceRegistry serviceRegistry
) throws HibernateException {
// @GenericGenerator의 parameters 속성으로 전달해준 @Parameter의 name 속성
String idGenParam = params.getProperty("ID_GEN_PARAM");
if (idGenParam != null) {
this.ID_GEN_PARAM = idGenParam;
}
}
}
idGenerator의 생성이 완료되었다. 이제 Entity의 생성 요청(INSERT 쿼리)이 발생하면 자동으로 id 생성 시퀀스를 작동시키는 쿼리 요청을 보내고 반환된 값을 Entity의 id로 설정하여 INSERT 될 것이다.
만약 데이터베이스의 Sequence를 사용하는 것이 아닌 애플리케이션 내에서 id를 생성하려고 한다면 generate 메서드에서 해당 동작을 수행 후 해당 값을 return 해주면 된다.
[Database][MySQL] ID 생성 procedure 및 function 생성 - 유사 Sequence 기능