Spring
Project
Thymeleaf 쇼핑몰(중단)

shoppingmall 프로젝트

shoppingmall 프로젝트를 진행한 기록을 모아두는 공간입니다.

Error

Entity EnumType Error

Item Table에서 item_status를 아래와 같이 생성하고

  item_status VARCHAR(20),

Entity 코드는 다음과 같이 구성되어 있었는데,

  @Enumerated(EnumType.STRING)
  private ItemStatus itemStatus;

found [varchar (Types#VARCHAR)], but expecting [enum ('sell','sold_out')

와 같은 에러가 발생했다. 스프링 부트 2.x 버전에서는 위 코드로 정상적으로 동작했는데 3점대 버전에서 에러가 발생하는건지 정확한 원인은 찾지 못했다.
다만 아래와 같이 columnDefinition을 추가해서 database scheme의 데이터 타입을 직접 지정해줘서 에러를 해결할 수 있었다.

  @Enumerated(EnumType.STRING)
  @Column(length = 20, columnDefinition = "varchar(20)")
  private ItemStatus itemStatus;

Item Seq Not Found

Entity를 생성할 때 GenerationType을 AUTO로 설정하게 되면

@Getter
@ToString
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "item")
public class ItemEntity {
  @Id
  @Column
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  ... 중략 ...
}

Schema-validation: missing table [item_seq]

이런 에러가 발생했다.
GenerationType을 IDENTITY로 변경하니 정상적으로 동작했다.

@Getter
@ToString
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "item")
public class ItemEntity {
  @Id
  @Column
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  ... 중략 ...
}

GenerationType

GenerationType.IDENTITY:

이 전략은 데이터베이스의 자동 증가(auto-increment) 컬럼을 사용하여 식별자를 생성합니다. 즉, 식별자의 생성은 데이터베이스에 위임되며, 새로운 엔티티가 데이터베이스에 저장될 때마다 데이터베이스가 식별자를 자동으로 생성하고 할당합니다. 이 전략은 MySQL과 같이 자동 증가 컬럼을 지원하는 데이터베이스에서 주로 사용됩니다.

GenerationType.AUTO:

이 전략은 JPA 구현체가 가장 적절한 식별자 생성 전략을 선택하도록 합니다. 선택된 전략은 JPA 구현체와 데이터베이스에 따라 다르며, GenerationType.SEQUENCE, GenerationType.IDENTITY, GenerationType.TABLE 중 하나가 될 수 있습니다. 예를 들어, Hibernate는 기본적으로 GenerationType.SEQUENCE 전략을 선택하지만, 데이터베이스가 자동 증가 컬럼을 지원하지 않는 경우 GenerationType.TABLE 전략을 선택할 수 있습니다.

JPA는 시퀀스를 사용하여 식별자를 생성하고 할당합니다. 이 전략은 주로 Oracle, PostgreSQL 등 시퀀스를 지원하는 데이터베이스에서 사용됩니다. 그러나 MySQL은 시퀀스를 지원하지 않으므로 MySQL Database를 사용하는데 GenerationType.SEQUENCE 전략을 사용하면 안된다.

Test 작성시 여러 데이터가 동시에 조회되는 문제

다음과 같은 Repository를 테스트할려고 하는데 문제가 발생했다.

public interface ItemRepository extends JpaRepository<ItemEntity, Long> {
  Optional<ItemEntity> findByItemName(String itemName);
}

intellij에서는 Query did not return a unique result: 6 results were returned라는 경고가 뜨고 Test 실행결과는 실패가 떳다.
👇는 테스트 코드

@Test
  @DisplayName("상품 조회")
  public void findByItemName() {
    // given
    ItemEntity item =  customItem();
 
    // when
    ItemEntity findResult = itemRepository.findByItermName(item.getItemName()).orElseThrow(() -> new IllegalArgumentException("해당 상품이 없습니다."));
 
    System.out.println("Debug Point");
 
    // then
    assertNotNull(findResult.getId());
    assertEquals(item.getItemName(), findResult.getItemName());
    assertEquals(item.getPrice(), findResult.getPrice());
    assertEquals(item.getItemDetail(), findResult.getItemDetail());
    assertEquals(item.getItemStatus(), findResult.getItemStatus());
    assertEquals(item.getStockNumber(), findResult.getStockNumber());
  }
 
  public ItemEntity customItem() {
        return  ItemEntity.builder()
        .itemName("테스트 상품")
        .price(10000)
        .itemDetail("테스트 상품 상세 설명")
        .itemStatus(ItemStatus.SELL)
        .stockNumber(100)
        .registeredAt(LocalDateTime.now())
        .updatedAt(LocalDateTime.now())
        .build();
  }

해결책 1

application-test.yaml을 test/resources에 생성하지 않고 main/resources에 생성해서 h2database가 아닌 mysql을 사용하고 있어서 데이터가 초기화되지 않는다는 문제점이 있었다.

ℹ️

Test class 상단에다가 @TestPropertySource(locations = "classpath:application-test.yaml")를 추가해줘야 한다.

해결책 2

jpa 쿼리중에 상위 N개를 가져오고 싶으면 TopN을 사용하면 된다. 예를 들어서 상위 Name으로 검색해서 상위 1개의 데이터를 가져오고 싶으면 다음과 같은 식이다.

  Optional<ItemEntity> findTop1ByItermName(String itemName);