Light Blue Pointer
본문 바로가기
TIL(Develop)/Spring

📖Hibernate/JPA의 id 생성 전략들

by 개발바닥곰발바닥!!! 2024. 1. 17.

📖Identifiers in Hibernate/JPA

strategies of id generations

https://www.baeldung.com/hibernate-identifiers

AUTO Generation strategy

  • 숫자 타입
    • the primary key value들은 database level에서 unique하게 됨
    • based on a sequence or table generator
  • @Entity public class Student { @Id @GeneratedValue private long studentId; // ... }
  • UUID 타입
    • Hibernate 5부터 추가된 UUIDGenerator를 이용
    • Hibernate will generate an id of the form “8dd5f315-9788-4d00-87bb-10eed9eff566”.
  • @Entity public class Course { @Id @GeneratedValue private UUID courseId; // ... }

**IDENTITY Generation

@Entity
public class Student {

    @Id
    @GeneratedValue (strategy = GenerationType.IDENTITY)
    private long studentId;

    // ...
}
  • IdentityGenerator를 이용
  • database의 identity column을 이용해서 값을 생성함 = auto-incremented 라는 뜻
  • One thing to note is that IDENTITY generation disables batch updates.

**SEQUENCE Generation

@Entity
public class User {
    @Id
    @GeneratedValue(generator = "sequence-generator")
    @GenericGenerator(
      name = "sequence-generator",
      strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
      parameters = {
        @Parameter(name = "sequence_name", value = "user_sequence"),// sequence에 이름을 지정
        @Parameter(name = "initial_value", value = "4"), //-> 4부터 값이 시작됨
        @Parameter(name = "increment_size", value = "1")
        }
    )
    private long userId;
    
    // ...
}
  • SequenceStyleGenerator class를 이용
  • 만약 사용하는 database가 sequence기능을 지원한다면 sequence를 사용함, 지원하지 않는다면 table generation으로 전환됨
  • @GenericGenerator annotation : to customize the sequence name
  • SEQUENCE is the generation type recommended by the Hibernate documentation.
  • The generated values are unique per sequence. If we don’t specify a sequence name, Hibernate will reuse the same hibernate_sequence for different types

TABLE Generation

@Entity
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, 
      generator = "table-generator")
    @TableGenerator(name = "table-generator", 
      table = "dep_ids", 
      pkColumnName = "seq_id", 
      valueColumnName = "seq_value")
    private long depId;

    // ...
}
  • TableGenerator 를 이용 → TableGenerator가 identifier generation values를 보유하는 database table를 이용함
  • 단점 : 성능을 저하시키고 확장이 잘 되지 않는다

Custom Generator

  • IdentifierGenerator interface를 implement 하면 Custom id generator를 만들 수 있음!

예시) String prefix와 number를 포함하는 identifiers 만들기

public class MyGenerator 
  implements IdentifierGenerator, Configurable {

    private String prefix;

    @Override
    public Serializable generate(
      SharedSessionContractImplementor session, Object obj) 
      throws HibernateException {

        String query = String.format("select %s from %s", 
            session.getEntityPersister(obj.getClass().getName(), obj)
              .getIdentifierPropertyName(),
            obj.getClass().getSimpleName());

        Stream ids = session.createQuery(query).stream();

        Long max = ids.map(o -> o.replace(prefix + "-", ""))
          .mapToLong(Long::parseLong)
          .max()
          .orElse(0L);

        return prefix + "-" + (max + 1);
    }

    @Override
    public void configure(Type type, Properties properties, 
      ServiceRegistry serviceRegistry) throws MappingException {
        prefix = properties.getProperty("prefix");
    }
}

override the generate() method from the IdentifierGenerator interface.

override Configurable interface 의 configure() method

  1. prefix-XX에서 존재하는 primary key중에 가장 큰 값을 찾아옴
  2. 가장 큰 primary key값에 1을 더한 후 prefix property 를 붙여줌
  3. Configurable interface 의 configure() method를 사용해서 prefix property value를 설정해줌
  4. @GenericGenerator annotation 를 이용해서 custom generator를 entity에 달아줌
@Entity
public class Product {

    @Id
    @GeneratedValue(generator = "prod-generator")
    @GenericGenerator(name = "prod-generator", 
      parameters = @Parameter(name = "prefix", value = "prod"), 
      strategy = "com.baeldung.hibernate.pojo.generator.MyGenerator")
    private String prodId;

    // ...
}

이 예제에서는 prefix parameter를 “prod”로 설정함

테스트코드가 있음

@Test
public void whenSaveCustomGeneratedId_thenOk() {
    Product product = new Product();
    session.save(product);
    Product product2 = new Product();
    session.save(product2);

    assertThat(product2.getProdId()).isEqualTo("prod-2");
}

Here the first value generated using the “prod” prefix was “prod-1”, followed by “prod-2”.

 

→ 이후는 전에 해봤던 복합키!