[java] 데이터베이스에 열거 형을 저장하는 방법

열거 형을 데이터베이스에 저장하는 가장 좋은 방법은 무엇입니까?

Java가 제공 name()하고 valueOf()열거 형 값을 문자열로 변환하는 방법을 알고 있습니다. 그러나 이러한 값을 저장하는 다른 (유연한) 옵션이 있습니까?

열거 형을 고유 한 숫자로 만드는 현명한 방법이 ordinal()있습니까 (사용하기에 안전하지 않음)?

최신 정보:

모든 멋지고 빠른 답변에 감사드립니다! 내가 생각했던대로였다.

그러나 ‘툴킷’에 대한 참고 사항; 그것은 한 가지 방법입니다. 문제는 내가 만든 각 Enum 유형에 동일한 메서드를 추가해야한다는 것입니다. 그것은 많은 중복 코드이며 현재 Java는 이에 대한 솔루션을 지원하지 않습니다 (Java 열거 형은 다른 클래스를 확장 할 수 없음).



답변

우리는 결코 더 이상 숫자 순서 값으로 열거를 저장하지; 디버깅과 지원을 너무 어렵게 만듭니다. 문자열로 변환 된 실제 열거 값을 저장합니다.

public enum Suit { Spade, Heart, Diamond, Club }

Suit theSuit = Suit.Heart;

szQuery = "INSERT INTO Customers (Name, Suit) " +
          "VALUES ('Ian Boyd', %s)".format(theSuit.name());

다음으로 다시 읽어보십시오.

Suit theSuit = Suit.valueOf(reader["Suit"]);

문제는 과거에 Enterprise Manager를 쳐다보고 다음을 해독하려고했던 것입니다.

Name                Suit
==================  ==========
Shelby Jackson      2
Ian Boyd            1

구절

Name                Suit
==================  ==========
Shelby Jackson      Diamond
Ian Boyd            Heart

후자는 훨씬 쉽습니다. 전자는 소스 코드에서 열거 형 멤버에 할당 된 숫자 값을 찾아야했습니다.

예, 더 많은 공간이 필요하지만 열거 형 멤버 이름이 짧고 하드 드라이브가 저렴하며 문제가있을 때 도움을주는 것이 훨씬 더 가치가 있습니다.

또한 숫자 값을 사용하면 그 값에 묶여 있습니다. 이전 숫자 값을 강제하지 않고 멤버를 멋지게 삽입하거나 재 배열 할 수 없습니다. 예를 들어 Suit 열거를 다음과 같이 변경합니다.

public enum Suit { Unknown, Heart, Club, Diamond, Spade }

될 것입니다 :

public enum Suit {
      Unknown = 4,
      Heart = 1,
      Club = 3,
      Diamond = 2,
      Spade = 0 }

데이터베이스에 저장된 레거시 숫자 값을 유지하기 위해.

데이터베이스에서 정렬하는 방법

질문이 나옵니다. 값을 주문하고 싶다고 가정 해 보겠습니다. 어떤 사람들은 열거 형의 서수 값으로 정렬하기를 원할 수 있습니다. 물론 열거 형의 숫자 ​​값으로 카드를 주문하는 것은 의미가 없습니다.

SELECT Suit FROM Cards
ORDER BY SuitID; --where SuitID is integer value(4,1,3,2,0)

Suit
------
Spade
Heart
Diamond
Club
Unknown

그것은 우리가 원하는 순서가 아닙니다-우리는 그것들을 열거 순서로 원합니다 :

SELECT Suit FROM Cards
ORDER BY CASE SuitID OF
    WHEN 4 THEN 0 --Unknown first
    WHEN 1 THEN 1 --Heart
    WHEN 3 THEN 2 --Club
    WHEN 2 THEN 3 --Diamond
    WHEN 0 THEN 4 --Spade
    ELSE 999 END

문자열을 저장하는 경우 정수 값을 저장하는 경우 필요한 동일한 작업이 필요합니다.

SELECT Suit FROM Cards
ORDER BY Suit; --where Suit is an enum name

Suit
-------
Club
Diamond
Heart
Spade
Unknown

그러나 그것은 우리가 원하는 순서가 아닙니다. 우리는 그것들을 열거 순서로 원합니다.

SELECT Suit FROM Cards
ORDER BY CASE Suit OF
    WHEN 'Unknown' THEN 0
    WHEN 'Heart'   THEN 1
    WHEN 'Club'    THEN 2
    WHEN 'Diamond' THEN 3
    WHEN 'Space'   THEN 4
    ELSE 999 END

내 의견은 이러한 종류의 순위가 사용자 인터페이스에 속한다는 것입니다. 열거 형 값을 기준으로 항목을 정렬하는 경우 : 뭔가 잘못하고있는 것입니다.

하지만 정말로 그렇게하고 싶다면 Suits차원 테이블을 만들 것입니다 .

| Suit       | SuitID       | Rank          | Color  |
|------------|--------------|---------------|--------|
| Unknown    | 4            | 0             | NULL   |
| Heart      | 1            | 1             | Red    |
| Club       | 3            | 2             | Black  |
| Diamond    | 2            | 3             | Red    |
| Spade      | 0            | 4             | Black  |

이렇게하면 Kissing Kings New Deck Order 를 사용하도록 카드를 변경하려는 경우 모든 데이터를 버리지 않고 표시 목적으로 변경할 수 있습니다.

| Suit       | SuitID       | Rank          | Color  | CardOrder |
|------------|--------------|---------------|--------|-----------|
| Unknown    | 4            | 0             | NULL   | NULL      |
| Spade      | 0            | 1             | Black  | 1         |
| Diamond    | 2            | 2             | Red    | 1         |
| Club       | 3            | 3             | Black  | -1        |
| Heart      | 1            | 4             | Red    | -1        |

이제 내부 프로그래밍 세부 정보 (열거 형 이름, 열거 형 값)를 사용자를위한 표시 설정으로 구분합니다.

SELECT Cards.Suit
FROM Cards
   INNER JOIN Suits ON Cards.Suit = Suits.Suit
ORDER BY Suits.Rank,
   Card.Rank*Suits.CardOrder


답변

피해야 할 특정 성능 이유가없는 한 열거 형에 별도의 테이블을 사용하는 것이 좋습니다. 추가 조회로 인해 실제로 죽지 않는 한 외래 키 무결성을 사용하십시오.

정장 테이블 :

suit_id suit_name
1       Clubs
2       Hearts
3       Spades
4       Diamonds

플레이어 테이블

player_name suit_id
Ian Boyd           4
Shelby Lake        2
  1. 열거 형을 동작 (예 : 우선 순위)이있는 클래스로 리팩터링 한 경우 데이터베이스는 이미 올바르게 모델링합니다.
  2. DBA는 스키마가 정규화 되었기 때문에 만족합니다 (오타가있을 수도 있고 없을 수도있는 전체 문자열 대신 플레이어 당 단일 정수 저장).
  3. 데이터베이스 값 ( suit_id)은 열거 값과 독립적이므로 다른 언어의 데이터에 대해서도 작업 할 수 있습니다.

답변

여기서 유일하게 안전한 메커니즘은 String name()값 을 사용하는 것 입니다. DB를 쓸 때, 당신은 할 수 값을 삽입 할 SPROC를 사용하여 읽을 때, 뷰를 사용합니다. 이러한 방식으로 열거 형이 변경되면 sproc / view에 간접적 인 수준이있어이를 DB에 “적용”하지 않고 열거 형 값으로 데이터를 표시 할 수 있습니다.


답변

당신이 말했듯이 서수는 약간 위험합니다. 예를 들어 :

public enum Boolean {
    TRUE, FALSE
}

public class BooleanTest {
    @Test
    public void testEnum() {
        assertEquals(0, Boolean.TRUE.ordinal());
        assertEquals(1, Boolean.FALSE.ordinal());
    }
}

이것을 서수로 저장 한 경우 다음과 같은 행이있을 수 있습니다.

> SELECT STATEMENT, TRUTH FROM CALL_MY_BLUFF

"Alice is a boy"      1
"Graham is a boy"     0

하지만 부울을 업데이트하면 어떻게 될까요?

public enum Boolean {
    TRUE, FILE_NOT_FOUND, FALSE
}

이것은 모든 거짓말이 ‘파일을 찾을 수 없음’으로 잘못 해석된다는 것을 의미합니다.

문자열 표현을 사용하는 것이 더 좋습니다.


답변

대규모 데이터베이스의 경우 숫자 표현의 크기와 속도 이점을 잃는 것을 꺼립니다. 나는 종종 Enum을 나타내는 데이터베이스 테이블로 끝납니다.

외래 키를 선언하여 데이터베이스 일관성을 적용 할 수 있습니다. 그러나 경우에 따라 모든 트랜잭션에 비용을 부과하는 외래 키 제약 조건으로 선언하지 않는 것이 더 나을 수 있습니다. 다음을 사용하여 선택한 시간에 주기적으로 확인하여 일관성을 보장 할 수 있습니다.

SELECT reftable.* FROM reftable
  LEFT JOIN enumtable ON reftable.enum_ref_id = enumtable.enum_id
WHERE enumtable.enum_id IS NULL;

이 솔루션의 나머지 절반은 Java enum과 데이터베이스 enum 테이블의 내용이 동일한 지 확인하는 테스트 코드를 작성하는 것입니다. 그것은 독자를위한 연습 문제로 남겨졌습니다.


답변

열거 형 이름 자체 만 저장합니다. 더 읽기 쉽습니다.

제한된 값 집합이있는 열거 형에 대한 특정 값을 저장하는 데 어려움을 겪었습니다. 예를 들어 문자를 사용하여 나타내는 제한된 상태 집합이있는 열거 형 (숫자 값보다 더 의미 있음) :

public enum EmailStatus {
    EMAIL_NEW('N'), EMAIL_SENT('S'), EMAIL_FAILED('F'), EMAIL_SKIPPED('K'), UNDEFINED('-');

    private char dbChar = '-';

    EmailStatus(char statusChar) {
        this.dbChar = statusChar;
    }

    public char statusChar() {
        return dbChar;
    }

    public static EmailStatus getFromStatusChar(char statusChar) {
        switch (statusChar) {
        case 'N':
            return EMAIL_NEW;
        case 'S':
            return EMAIL_SENT;
        case 'F':
            return EMAIL_FAILED;
        case 'K':
            return EMAIL_SKIPPED;
        default:
            return UNDEFINED;
        }
    }
}

값이 많을 때 getFromXYZ 메서드를 작게 유지하려면 열거 형 내에 Map이 있어야합니다.


답변

열거 형을 데이터베이스에 문자열로 저장하는 경우 유틸리티 메서드를 생성하여 열거 형을 (역) 직렬화 할 수 있습니다.

   public static String getSerializedForm(Enum<?> enumVal) {
        String name = enumVal.name();
        // possibly quote value?
        return name;
    }

    public static <E extends Enum<E>> E deserialize(Class<E> enumType, String dbVal) {
        // possibly handle unknown values, below throws IllegalArgEx
        return Enum.valueOf(enumType, dbVal.trim());
    }

    // Sample use:
    String dbVal = getSerializedForm(Suit.SPADE);
    // save dbVal to db in larger insert/update ...
    Suit suit = deserialize(Suit.class, dbVal);