IT박스

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

itboxs 2020. 7. 27. 07:40
반응형

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


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

나는 자바가 제공하는 알 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. Your database values (suit_id) are independent from your enumeration value, which helps you work on the data from other languages as well.

I would argue that the only safe mechanism here is to use the String name() value. When writing to the DB, you could use a sproc to insert the value and when reading, use a View. In this manner, if the enums change, there is a level of indirection in the sproc/view to be able to present the data as the enum value without "imposing" this on the DB.


As you say, ordinal is a bit risky. Consider for example:

public enum Boolean {
    TRUE, FALSE
}

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

If you stored this as ordinals, you might have rows like:

> SELECT STATEMENT, TRUTH FROM CALL_MY_BLUFF

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

But what happens if you updated Boolean?

public enum Boolean {
    TRUE, FILE_NOT_FOUND, FALSE
}

This means all your lies will become misinterpreted as 'file-not-found'

Better to just use a string representation


For a large database, I am reluctant to lose the size and speed advantages of the numeric representation. I often end up with a database table representing the Enum.

You can enforce database consistency by declaring a foreign key -- although in some cases it might be better to not declare that as a foreign key constraint, which imposes a cost on every transaction. You can ensure consistency by periodically doing a check, at times of your choosing, with:

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

The other half of this solution is to write some test code that checks that the Java enum and the database enum table have the same contents. That's left as an exercise for the reader.


We just store the enum name itself - it's more readable.

We did mess around with storing specific values for enums where there are a limited set of values, e.g., this enum that has a limited set of statuses that we use a char to represent (more meaningful than a numeric value):

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;
        }
    }
}

and when you have a lot of values you need to have a Map inside your enum to keep that getFromXYZ method small.


If saving enums as strings in the database, you can create utility methods to (de)serialize any enum:

   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);

All my experience tells me that safest way of persisting enums anywhere is to use additional code value or id (some kind of evolution of @jeebee answer). This could be a nice example of an idea:

enum Race {
    HUMAN ("human"),
    ELF ("elf"),
    DWARF ("dwarf");

    private final String code;

    private Race(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }
}

Now you can go with any persistence referencing your enum constants by it's code. Even if you'll decide to change some of constant names, you always can save code value (e.g. DWARF("dwarf") to GNOME("dwarf"))

Ok, dive some more deeper with this conception. Here is some utility method, that helps you find any enum value, but first lets extend our approach.

interface CodeValue {
    String getCode();
}

And let our enum implement it:

enum Race implement CodeValue {...}

This is the time for magic search method:

static <T extends Enum & CodeValue> T resolveByCode(Class<T> enumClass, String code) {
    T[] enumConstants = enumClass.getEnumConstants();
    for (T entry : enumConstants) {
        if (entry.getCode().equals(code)) return entry;
    }
    // In case we failed to find it, return null.
    // I'd recommend you make some log record here to get notified about wrong logic, perhaps.
    return null;
}

And use it like a charm: Race race = resolveByCode(Race.class, "elf")


I have faced the same issue where my objective is to persist Enum String value into database instead of Ordinal value.

To over come this issue, I have used @Enumerated(EnumType.STRING) and my objective got resolved.

For Example, you have an Enum Class:

public enum FurthitMethod {

    Apple,
    Orange,
    Lemon
}

In the entity class, define @Enumerated(EnumType.STRING):

@Enumerated(EnumType.STRING)
@Column(name = "Fruits")
public FurthitMethod getFuritMethod() {
    return fruitMethod;
}

public void setFruitMethod(FurthitMethod authenticationMethod) {
    this.fruitMethod= fruitMethod;
}

While you try to set your value to Database, String value will be persisted into Database as "APPLE", "ORANGE" or "LEMON".


Multiple values with OR relation for one, enum field. The concept for .NET with storing enum types in database like a byte or an int and using FlagsAttribute in your code.

http://blogs.msdn.com/b/efdesign/archive/2011/06/29/enumeration-support-in-entity-framework.aspx

참고URL : https://stackoverflow.com/questions/229856/ways-to-save-enums-in-database

반응형