Czy mogę ustawić JPA / hibernację, aby utrwalać Boolean
typy jako Y/N
? W bazie danych (kolumna jest zdefiniowana jako varchar2(1)
. Obecnie przechowuje je jako 0/1
. Baza danych to Oracle.
Jedyny sposób, w jaki się zorientowałem, jak to zrobić, to mieć dwie właściwości dla mojej klasy. Jeden jako wartość logiczna dla programowania API, który nie jest uwzględniony w mapowaniu. To getter i setter odwołują się do prywatnej zmiennej typu char, która jest T / N. Następnie mam inną chronioną właściwość, która jest uwzględniona w mapowaniu hibernacji, a jej metody pobierające i ustawiające odwołują się bezpośrednio do prywatnej zmiennej char.
EDYCJA: Jak już wspomniano, istnieją inne rozwiązania wbudowane bezpośrednio w Hibernate. Zostawiam tę odpowiedź, ponieważ może działać w sytuacjach, w których pracujesz ze starszym polem, które nie gra dobrze z wbudowanymi opcjami. Poza tym nie ma żadnych poważnych negatywnych konsekwencji takiego podejścia.
Hibernate ma wbudowany typ „yes_no”, który zrobi to, co chcesz. Mapuje do kolumny CHAR (1) w bazie danych.
Podstawowe mapowanie:
<property name="some_flag" type="yes_no"/>
Mapowanie adnotacji (rozszerzenia hibernacji):
@Type(type="yes_no") public boolean getFlag();
źródło
To jest czysty JPA bez użycia metod pobierających / ustawiających. Od 2013/2014 jest to najlepsza odpowiedź bez użycia adnotacji specyficznych dla Hibernate, ale pamiętaj, że to rozwiązanie to JPA 2.1 i nie było dostępne, gdy po raz pierwszy zadano pytanie:
@Entity public class Person { @Convert(converter=BooleanToStringConverter.class) private Boolean isAlive; ... }
I wtedy:
@Converter public class BooleanToStringConverter implements AttributeConverter<Boolean, String> { @Override public String convertToDatabaseColumn(Boolean value) { return (value != null && value) ? "Y" : "N"; } @Override public Boolean convertToEntityAttribute(String value) { return "Y".equals(value); } }
Edytować:
Powyższa implementacja uwzględnia wszystko inne niż znak „Y”, w tym
null
asfalse
. Czy to jest poprawne? Niektórzy tutaj uważają to za niepoprawne i uważają, żenull
baza danych powinna byćnull
w Javie.Ale jeśli powrócisz
null
w Javie, da ci to,NullPointerException
czy twoje pole jest prymitywną wartością logiczną . Innymi słowy, o ile niektóre z pól faktycznie użyć klasy Boolean najlepiej rozważyćnull
jakfalse
i użyj powyższej implementacji. Wtedy Hibernate nie będzie emitować żadnych wyjątków niezależnie od zawartości bazy danych.A jeśli chcesz akceptować
null
i emitować wyjątki, jeśli zawartość bazy danych nie jest ściśle poprawna, to myślę, że nie powinieneś akceptować żadnych znaków oprócz „Y”, „N” inull
. Zadbaj o spójność i nie akceptuj żadnych odmian, takich jak „y”, „n”, „0” i „1”, które tylko utrudnią Ci życie. To jest bardziej rygorystyczna realizacja:@Override public String convertToDatabaseColumn(Boolean value) { if (value == null) return null; else return value ? "Y" : "N"; } @Override public Boolean convertToEntityAttribute(String value) { if (value == null) return null; else if (value.equals("Y")) return true; else if (value.equals("N")) return false; else throw new IllegalStateException("Invalid boolean character: " + value); }
I jeszcze jedna opcja, jeśli chcesz zezwolić
null
w Javie, ale nie w bazie danych:@Override public String convertToDatabaseColumn(Boolean value) { if (value == null) return "-"; else return value ? "Y" : "N"; } @Override public Boolean convertToEntityAttribute(String value) { if (value.equals("-") return null; else if (value.equals("Y")) return true; else if (value.equals("N")) return false; else throw new IllegalStateException("Invalid boolean character: " + value); }
źródło
Y
,N
aT
. Nie jestem też pewien, czy w wyniku konwersji należy pomijać przypadek posiadania wartości null.Skorzystałem z koncepcji z odpowiedzi zamieszczonej przez @marcg i świetnie działa z JPA 2.1. Jego kod nie był całkiem poprawny, więc opublikowałem moją działającą implementację. Spowoduje to przekonwertowanie
Boolean
pól encji na kolumnę znaków T / N w bazie danych.Z mojej klasy encji:
@Convert(converter=BooleanToYNStringConverter.class) @Column(name="LOADED", length=1) private Boolean isLoadedSuccessfully;
Moja klasa konwertera:
/** * Converts a Boolean entity attribute to a single-character * Y/N string that will be stored in the database, and vice-versa * * @author jtough */ public class BooleanToYNStringConverter implements AttributeConverter<Boolean, String> { /** * This implementation will return "Y" if the parameter is Boolean.TRUE, * otherwise it will return "N" when the parameter is Boolean.FALSE. * A null input value will yield a null return value. * @param b Boolean */ @Override public String convertToDatabaseColumn(Boolean b) { if (b == null) { return null; } if (b.booleanValue()) { return "Y"; } return "N"; } /** * This implementation will return Boolean.TRUE if the string * is "Y" or "y", otherwise it will ignore the value and return * Boolean.FALSE (it does not actually look for "N") for any * other non-null string. A null input value will yield a null * return value. * @param s String */ @Override public Boolean convertToEntityAttribute(String s) { if (s == null) { return null; } if (s.equals("Y") || s.equals("y")) { return Boolean.TRUE; } return Boolean.FALSE; } }
Ten wariant jest również zabawny, jeśli kochasz emotikony i masz dość T / N lub T / F w swojej bazie danych. W takim przypadku kolumna bazy danych musi mieć dwa znaki zamiast jednego. Prawdopodobnie nic wielkiego.
/** * Converts a Boolean entity attribute to a happy face or sad face * that will be stored in the database, and vice-versa * * @author jtough */ public class BooleanToHappySadConverter implements AttributeConverter<Boolean, String> { public static final String HAPPY = ":)"; public static final String SAD = ":("; /** * This implementation will return ":)" if the parameter is Boolean.TRUE, * otherwise it will return ":(" when the parameter is Boolean.FALSE. * A null input value will yield a null return value. * @param b Boolean * @return String or null */ @Override public String convertToDatabaseColumn(Boolean b) { if (b == null) { return null; } if (b) { return HAPPY; } return SAD; } /** * This implementation will return Boolean.TRUE if the string * is ":)", otherwise it will ignore the value and return * Boolean.FALSE (it does not actually look for ":(") for any * other non-null string. A null input value will yield a null * return value. * @param s String * @return Boolean or null */ @Override public Boolean convertToEntityAttribute(String s) { if (s == null) { return null; } if (HAPPY.equals(s)) { return Boolean.TRUE; } return Boolean.FALSE; } }
źródło
NULL
właściwości dlaNULL
wartości bazy danych, ta odpowiedź nie zapewnia żadnej wartości dodanej do odpowiedzi @MarKG. Najlepiej ująć tę różnicę jako komentarz.Aby jeszcze lepiej odwzorować wartości logiczne na T / N, dodaj do swojej konfiguracji hibernacji:
<!-- when using type="yes_no" for booleans, the line below allow booleans in HQL expressions: --> <property name="hibernate.query.substitutions">true 'Y', false 'N'</property>
Teraz możesz używać wartości logicznych w HQL, na przykład:
"FROM " + SomeDomainClass.class.getName() + " somedomainclass " + "WHERE somedomainclass.someboolean = false"
źródło
Aby zrobić to w ogólny sposób JPA przy użyciu adnotacji pobierających, poniższy przykład działa w moim przypadku z Hibernate 3.5.4 i Oracle 11g. Zwróć uwagę, że odwzorowane metody pobierające i ustawiające (
getOpenedYnString
isetOpenedYnString
) są metodami prywatnymi. Te metody zapewniają mapowanie, ale cały programowy dostęp do klasy wykorzystuje metodygetOpenedYn
isetOpenedYn
.private String openedYn; @Transient public Boolean getOpenedYn() { return toBoolean(openedYn); } public void setOpenedYn(Boolean openedYn) { setOpenedYnString(toYesNo(openedYn)); } @Column(name = "OPENED_YN", length = 1) private String getOpenedYnString() { return openedYn; } private void setOpenedYnString(String openedYn) { this.openedYn = openedYn; }
Oto klasa util z metodami statycznymi
toYesNo
itoBoolean
:public class JpaUtil { private static final String NO = "N"; private static final String YES = "Y"; public static String toYesNo(Boolean value) { if (value == null) return null; else if (value) return YES; else return NO; } public static Boolean toBoolean(String yesNo) { if (yesNo == null) return null; else if (YES.equals(yesNo)) return true; else if (NO.equals(yesNo)) return false; else throw new RuntimeException("unexpected yes/no value:" + yesNo); } }
źródło
użycie konwerterów JPA 2.1 jest najlepszym rozwiązaniem, jednak jeśli używasz wcześniejszej wersji JPA, mogę polecić jeszcze jedno rozwiązanie (lub obejście). Utwórz wyliczenie o nazwie BooleanWrapper z 2 wartościami T i F i dodaj do niego następującą metodę, aby uzyskać opakowaną wartość
public Boolean getValue() { return this == T; }
:, zamapuj ją za pomocą @Enumerated (EnumType.STRING).źródło