Kann ich JPA / Hibernate so einrichten, dass Boolean
Typen wie beibehalten werden Y/N
? In der Datenbank (die Spalte ist definiert als varchar2(1)
. Sie speichert sie derzeit als 0/1
. Die Datenbank ist Oracle.
Antworten:
Der einzige Weg, wie ich das herausfinden kann, besteht darin, zwei Eigenschaften für meine Klasse zu haben. Einer als Boolescher Wert für die Programmier-API, der nicht im Mapping enthalten ist. Getter und Setter verweisen auf eine private Zeichenvariable, die J / N ist. Ich habe dann eine andere geschützte Eigenschaft, die in der Zuordnung des Ruhezustands enthalten ist, und ihre Getter und Setter verweisen direkt auf die private Zeichenvariable.
BEARBEITEN: Wie bereits erwähnt, gibt es andere Lösungen, die direkt in den Ruhezustand integriert sind. Ich lasse diese Antwort, weil sie in Situationen funktionieren kann, in denen Sie mit einem Legacy-Feld arbeiten, das mit den integrierten Optionen nicht gut funktioniert. Darüber hinaus gibt es keine ernsthaften negativen Konsequenzen für diesen Ansatz.
Der Ruhezustand verfügt über einen integrierten Typ "yes_no", der das tut, was Sie wollen. Es wird einer CHAR (1) -Spalte in der Datenbank zugeordnet.
Grundlegende Zuordnung: <property name="some_flag" type="yes_no"/>
Annotation Mapping (Hibernate-Erweiterungen):
@Type(type="yes_no")
public boolean getFlag();
Dies ist reine JPA ohne Verwendung von Gettern / Setzern. Ab 2013/2014 ist dies die beste Antwort ohne Verwendung von Hibernate-spezifischen Anmerkungen. Beachten Sie jedoch, dass diese Lösung JPA 2.1 ist und nicht verfügbar war, als die Frage zum ersten Mal gestellt wurde:
@Entity
public class Person {
@Convert(converter=BooleanToStringConverter.class)
private Boolean isAlive;
...
}
Und dann:
@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);
}
}
Bearbeiten:
Die obige Implementierung berücksichtigt alles, was sich vom Zeichen "Y" unterscheidet, einschließlich null
, als false
. Ist das korrekt? Einige Leute hier halten dies für falsch und glauben, dass sich null
die Datenbank null
in Java befinden sollte.
Wenn Sie jedoch null
in Java zurückkehren, erhalten Sie einen, NullPointerException
wenn Ihr Feld ein primitiver Boolescher Wert ist . Mit anderen Worten, es sei denn , einige Ihrer Felder tatsächlich die Verwendung der Klasse Boolean ist es am besten zu prüfen , null
wie false
und die obige Implementierung verwenden. Dann gibt Hibernate keine Ausnahmen aus, unabhängig vom Inhalt der Datenbank.
Und wenn Sie null
Ausnahmen akzeptieren und ausgeben möchten, wenn der Inhalt der Datenbank nicht streng korrekt ist, sollten Sie außer "Y", "N" und " keine" Zeichen akzeptieren null
. Machen Sie es konsistent und akzeptieren Sie keine Variationen wie "y", "n", "0" und "1", die Ihr Leben erst später schwieriger machen. Dies ist eine strengere Implementierung:
@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);
}
Und noch eine Option, wenn Sie null
in Java, aber nicht in der Datenbank zulassen möchten :
@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
und T
. Ich bin mir auch nicht sicher, ob man den Fall eines Nullwerts infolge einer Konvertierung weglassen sollte.
Ich habe das Konzept aus der Antwort von @marcg verwendet und es funktioniert hervorragend mit JPA 2.1. Sein Code war nicht ganz richtig, also habe ich meine Arbeitsimplementierung veröffentlicht. Dadurch werden Boolean
Entitätsfelder in eine J / N-Zeichenspalte in der Datenbank konvertiert .
Aus meiner Entitätsklasse:
@Convert(converter=BooleanToYNStringConverter.class)
@Column(name="LOADED", length=1)
private Boolean isLoadedSuccessfully;
Meine Konverterklasse:
/**
* 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;
}
}
Diese Variante macht auch Spaß, wenn Sie Emoticons lieben und nur J / N oder T / F in Ihrer Datenbank satt haben. In diesem Fall muss Ihre Datenbankspalte aus zwei Zeichen anstelle von einem bestehen. Wahrscheinlich keine große Sache.
/**
* 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
Eigenschaft für den NULL
Datenbankwert bietet diese Antwort keinen Mehrwert gegenüber der @ MarKG-Antwort. Am besten erfassen Sie diesen Unterschied als Kommentar.
Fügen Sie Ihrer Ruhezustandskonfiguration Folgendes hinzu, um eine noch bessere boolesche Zuordnung zu J / N zu erzielen:
<!-- 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>
Jetzt können Sie Boolesche Werte in HQL verwenden, zum Beispiel:
"FROM " + SomeDomainClass.class.getName() + " somedomainclass " +
"WHERE somedomainclass.someboolean = false"
Das folgende Beispiel funktioniert für mich mit Hibernate 3.5.4 und Oracle 11g, um dies auf generische JPA-Weise mithilfe von Getter-Annotationen zu tun. Beachten Sie, dass der zugeordnete Getter und der Setter ( getOpenedYnString
und setOpenedYnString
) private Methoden sind. Diese Methoden stellen die Zuordnung bereit, aber der gesamte programmatische Zugriff auf die Klasse verwendet die Methoden getOpenedYn
und 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;
}
Hier ist die util-Klasse mit statischen Methoden toYesNo
und 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);
}
}
Die Verwendung von JPA 2.1-Konvertern ist die beste Lösung. Wenn Sie jedoch eine frühere Version von JPA verwenden, kann ich eine weitere Lösung (oder Problemumgehung) empfehlen. Erstellen Sie eine Aufzählung mit dem Namen BooleanWrapper mit zwei Werten von T und F und fügen Sie die folgende Methode hinzu, um den public Boolean getValue() { return this == T; }
umgebrochenen Wert zu erhalten: Ordnen Sie ihn mit @Enumerated (EnumType.STRING) zu.