카테고리 없음

Hibernate ID Generator

JUNG_EV 2024. 5. 7. 08:46

Hibernate ID Generator 나만의 JPA @ID 만들기

 

JPA의 Entity를 구성할때 특수한 상황일 경우 @ID의 값이 Sequence이지만 Long이 아니거나 특별한 ID를 줘야하는 경우가 있습니다.

 

커스텀 generator를 사용합니다.

 

커스텀 ID Generator 만들기

 

이 Generator는 우리 상황에 맞는 ID를 생성할 수 있도록 도와줄 것입니다.

 

import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.Configurable;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.Type;

import java.io.Serializable;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class IdGenerator implements IdentifierGenerator, Configurable {
    // 속성값 처리를 위한 Const
    public static final String METHOD = "method";
    public static final String SEQUENCE_NAME = "sequence_name";
    public static final String INCREMENT_SIZE = "increment_size";


    // 전달받은 속성값
    private String method; // id 생성 방법
    private String sequenceName; // sequenceName 추가
    private int incrementSize; // 증가 크기 변수 추가


    // 속성값 처리 메소드 override
    @Override
    public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
        method = ConfigurationHelper.getString(METHOD, params);
        sequenceName = ConfigurationHelper.getString(SEQUENCE_NAME, params); // sequenceName 설정
        incrementSize = ConfigurationHelper.getInt(INCREMENT_SIZE, params, 1); // 증가 크기 설정
    }

    // ID 생성 처리 메소드 override
    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object obj) {
        String sql = null;
        Serializable newId = null; // newId 변수 추가

        switch (method) {
            case "SEQUENCE":
                // 시퀀스의 증가 크기를 반영하여 SQL 쿼리 생성
                sql = "SELECT " + sequenceName + ".NEXTVAL FROM DUAL";
                break;
            default:
                // 다른 방식에 대한 처리
                break;
        }

        // JDBC Connection
        Connection con = null;
        try {
            con = session.getJdbcConnectionAccess().obtainConnection();
            CallableStatement callStatement = con.prepareCall(sql);
            callStatement.executeQuery();
            ResultSet rs = callStatement.getResultSet();

            if (rs.next()) {
                newId = rs.getString(1);
            }
        } catch (SQLException sqlException) {
            throw new HibernateException(sqlException);
        } finally {
            try {
                if (con != null && !con.isClosed()) { // null 체크 추가
                    con.close();
                }
            } catch (SQLException e) {
                e.getStackTrace();
            }
        }
        return newId;
    }
}

 

 

 

  1. IdentifierGenerator 인터페이스 구현: IdGenerator 클래스는 IdentifierGenerator 인터페이스를 구현합니다. 이 인터페이스에는 엔티티의 ID를 생성하기 위한 generate 메서드가 포함되어 있습니다.
  2. Configurable 인터페이스 구현: IdGenerator 클래스는 또한 Configurable 인터페이스를 구현합니다. 이는 Hibernate에게 이 클래스가 구성 가능한 것임을 알려주는 인터페이스입니다. configure 메서드에서는 속성값을 읽어와서 필요한 초기화 작업을 수행합니다.
  3. generate 메서드: generate 메서드에서는 주어진 방식에 따라 새로운 ID를 생성합니다. 현재는 "SEQUENCE" 방식만 구현되어 있으며, 시퀀스를 사용하여 ID를 생성합니다. 그런 다음 생성된 ID를 반환합니다.

 

 

JPA에 붙이기

 

이제 @Entity의 @Id에서 사용해 봅니다.

import jakarta.persistence.*;
import *.IdGenerator;
import lombok.Getter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

import java.util.Date;

@Getter
@Entity
@Table(name = "테이블_이름", schema = "스키마_이름")
public class RandomEntity {


    @Id
    @GeneratedValue(generator = "RandomEntity_IdGenerator")
    @GenericGenerator(name = "RandomEntity_IdGenerator", strategy = "*.IdGenerator", parameters = {
            @Parameter(name = IdGenerator.METHOD, value = "SEQUENCE"),
            @Parameter(name = IdGenerator.SEQUENCE_NAME, value = "시퀀스_이름"),
    })
    @Column(name = "SEQ_NO")
    private String seqNo;

    @Column(name = "USER_ID")
    private String userId;

    @Column(name = "MONEY", precision = 10, scale = 0)
    private Long moneyAmount;


    @CreationTimestamp
    @Column(name = "SYS_CREATION_DATE")
    private Date createdAt;

}

 

시퀀스의 기본 설정은 1씩 올라가기 때문에 increasement_size는 설정하지 않습니다.

 

@GeneratedValue의 generator는 고유한 이름을 주는것이 권장되며 @GenericGenerator의 name과 동일해야합니다.

strategy는 decrypted 되었지만 파일의 위치를 명시적으로 보여주고 찾기 위해서 설정합니다.