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.
Odpowiedzi:
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();
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
as false
. Czy to jest poprawne? Niektórzy tutaj uważają to za niepoprawne i uważają, że null
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
jak false
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” i null
. 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);
}
Y
, N
a T
. 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;
}
}
NULL
właściwości dla NULL
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"
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
i setOpenedYnString
) są metodami prywatnymi. Te metody zapewniają mapowanie, ale cały programowy dostęp do klasy wykorzystuje metody getOpenedYn
i setOpenedYn
.
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
i toBoolean
:
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);
}
}
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).