Konfigurieren Sie den Ruhezustand (mithilfe von JPA) so, dass J / N für den Typ Boolean anstelle von 0/1 gespeichert wird

80

Kann ich JPA / Hibernate so einrichten, dass BooleanTypen 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.

sengs
quelle

Antworten:

8

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.

Spencer Ruport
quelle
1
Ich musste etwas Ähnliches tun - ich habe den Typ des Mitglieds von Boolean in String geändert. In den Gettern und Setzern (die Boolean erhalten und gesetzt haben) habe ich Code geschrieben, um Y / N in den entsprechenden Boolean-Wert umzuwandeln.
Sengs
@bernardn Nein, es ist das bessere.
Alexander
Hier ist eine bessere Lösung thoughts-on-java.org/hibernate-tips-how-to-map-a-boolean-to-yn
Ganesh Hoolageri
145

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();
ChssPly76
quelle
28
Für diejenigen, die interessiert sind, gibt es auch einen "true_false" -Typ, der entweder "T" oder "F" speichert.
Matt Solnit
Dies hat funktioniert, aber ich konnte es nicht verwenden, da es sich um eine bestimmte Anmerkung im Ruhezustand handelt. Danke für die Antwort. Könnte es in einem anderen Projekt verwenden.
Sengs
Dies gilt für den Ruhezustand 4 und höher, dh für Java 1.6 und höher. Doent Arbeit für Winterschlaf 3. *
Storm_Buster
7
@storm_buster Hibernate 4 war nicht vorhanden, als diese Antwort gegeben wurde. Es funktioniert einwandfrei mit Hibernate 3.x und Java 1.5
ChssPly76
3
Wie setzt man dann 0 oder 1?
JavaTechnical
84

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 nulldie Datenbank nullin Java befinden sollte.

Wenn Sie jedoch nullin Java zurückkehren, erhalten Sie einen, NullPointerExceptionwenn 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 , nullwie falseund die obige Implementierung verwenden. Dann gibt Hibernate keine Ausnahmen aus, unabhängig vom Inhalt der Datenbank.

Und wenn Sie nullAusnahmen 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 nullin 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);
    }
MarcG
quelle
Der Konverter zeigt die Idee, funktioniert aber natürlich nicht. Der Konverter verwendet die möglichen Werte Y, Nund T. Ich bin mir auch nicht sicher, ob man den Fall eines Nullwerts infolge einer Konvertierung weglassen sollte.
Matthias
@ Matthias Ja, T war ein Tippfehler. Ich habe es repariert. Vielen Dank.
MarcG
Ich hatte ein Problem mit dem Y / N-Feld mit JPQL und stellte hier eine Folgefrage
Chris Williams
11

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

}
Jim Tough
quelle
Ist das Unboxing 'b.booleanValue ()' notwendig?
LeOn - Han Li
Mit Ausnahme der NULLEigenschaft für den NULLDatenbankwert bietet diese Antwort keinen Mehrwert gegenüber der @ MarKG-Antwort. Am besten erfassen Sie diesen Unterschied als Kommentar.
Mohnish
Du bist 5 Jahre zu spät
Jim Tough
2

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"
MA Hogendoorn
quelle
2
Das ist global. Nicht ausreichend für singuläre Eigenschaften.
Maxxyme
0

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 ( getOpenedYnStringund setOpenedYnString) private Methoden sind. Diese Methoden stellen die Zuordnung bereit, aber der gesamte programmatische Zugriff auf die Klasse verwendet die Methoden getOpenedYnund 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 toYesNound 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);
    }
}
Dave Moten
quelle
-1

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.

Ravshan Samandarov
quelle