열거 형을 데이터베이스에 저장하는 가장 좋은 방법은 무엇입니까?
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
- 열거 형을 동작 (예 : 우선 순위)이있는 클래스로 리팩터링 한 경우 데이터베이스는 이미 올바르게 모델링합니다.
- DBA는 스키마가 정규화 되었기 때문에 만족합니다 (오타가있을 수도 있고 없을 수도있는 전체 문자열 대신 플레이어 당 단일 정수 저장).
- 데이터베이스 값 (
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);