web-dev-qa-db-fra.com

Configurez hibernate (à l'aide de JPA) pour stocker Y / N pour le type booléen au lieu de 0/1

Puis-je configurer JPA/hibernate pour qu'il persiste Boolean tape comme Y/N? Dans la base de données (la colonne est définie par varchar2(1). Elle est actuellement stockée sous la forme 0/1. La base de données est Oracle.

75
sengs

La seule façon pour moi d’y arriver est d’avoir deux propriétés pour ma classe. Un comme booléen pour l'API de programmation qui n'est pas incluse dans le mappage. Son getter et son setter font référence à une variable de caractère privé qui est Y/N. J'ai ensuite une autre propriété protégée qui est incluse dans le mappage hibernate et ses getters et setters font directement référence à la variable private char.

EDIT: Comme il a été souligné, d’autres solutions sont directement intégrées à Hibernate. Je laisse cette réponse parce que cela peut fonctionner dans des situations où vous travaillez avec un ancien champ qui ne joue pas avec Nice avec les options intégrées. En plus de cela, cette approche n’a pas de conséquences négatives sérieuses.

8
Spencer Ruport

Hibernate a un type "yes_no" intégré qui ferait ce que vous voulez. Il correspond à une colonne CHAR (1) de la base de données.

Cartographie de base: <property name="some_flag" type="yes_no"/>

Mappage d'annotation (extensions Hibernate):

@Type(type="yes_no")
public boolean getFlag();
137
ChssPly76

C'est du JPA pur sans utiliser de getters/setters. Depuis 2013/2014, c’est la meilleure réponse sans utiliser d’annotations spécifiques à Hibernate, mais veuillez noter que cette solution est JPA 2.1 et n’était pas disponible au moment où la question a été posée:

@Entity
public class Person {    

    @Convert(converter=BooleanToStringConverter.class)
    private Boolean isAlive;    
    ...
}

Et alors:

@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);
        }
    }

Edit:

L’implémentation ci-dessus considère que tout ce qui est différent du caractère "Y", y compris null, en tant que false. Est-ce exact? Certaines personnes ici considèrent cela comme incorrect et pensent que null dans la base de données devrait être null en Java.

Mais si vous retournez null en Java, vous obtiendrez un NullPointerException si votre champ est un booléen primitif. En d'autres termes, à moins que certains de vos champs utilisent réellement le classe Boolean, il est préférable de considérer null comme false et d'utiliser l'implémentation ci-dessus. Ensuite, Hibernate n'émettra aucune exception quel que soit le contenu de la base de données.

Et si vous voulez accepter null et émettre des exceptions si le contenu de la base de données n'est pas strictement correct, alors je suppose que vous ne devriez pas accepter tous caractères autres que "Y", " N "et null. Rendez-le cohérent et n'acceptez aucune variation telle que "y", "n", "0" et "1", qui ne feront que rendre votre vie plus difficile plus tard. Ceci est une implémentation plus stricte:

@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);
    }

Et encore une autre option, si vous voulez autoriser null dans Java mais pas dans la base de données:

@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);
    }
78
MarcG

J'ai utilisé le concept de la réponse publiée par @marcg et cela fonctionne très bien avec JPA 2.1. Son code n'était pas tout à fait correct, alors je publie ma mise en œuvre opérationnelle. Ceci convertira les champs d'entité Boolean en une colonne de caractères Y/N dans la base de données.

De ma classe d'entité:

@Convert(converter=BooleanToYNStringConverter.class)
@Column(name="LOADED", length=1)
private Boolean isLoadedSuccessfully;

Ma classe de convertisseur:

/**
 * 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;
    }

}

Cette variante est également amusante si vous aimez les émoticônes et que vous en avez vraiment marre de Y/N ou de T/F dans votre base de données. Dans ce cas, votre colonne de base de données doit comporter deux caractères au lieu d'un. Probablement pas un gros problème.

/**
 * 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;
    }

}
11
Jim Tough

Pour faire un meilleur mappage booléen sur Y/N, ajoutez à votre configuration Hibernate:

<!-- 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>

Vous pouvez maintenant utiliser des booléens dans HQL, par exemple:

"FROM " + SomeDomainClass.class.getName() + " somedomainclass " +
"WHERE somedomainclass.someboolean = false"
2
M.A. Hogendoorn

Pour le faire de manière JPA générique en utilisant des annotations getter, l'exemple ci-dessous fonctionne pour moi avec Hibernate 3.5.4 et Oracle 11g. Notez que le getter et le setter mappés (getOpenedYnString et setOpenedYnString) sont des méthodes privées. Ces méthodes fournissent le mappage, mais tout accès par programme à la classe utilise les méthodes getOpenedYn et 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;
}

Voici la classe util avec les méthodes statiques toYesNo et 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);
    }
}
0
Dave Moten

l'utilisation de convertisseurs JPA 2.1 est la meilleure solution. Toutefois, si vous utilisez une version antérieure de JPA, je peux vous recommander une solution supplémentaire (ou une solution de contournement). Créez une énumération appelée BooleanWrapper avec 2 valeurs de T et F et ajoutez-y la méthode suivante pour obtenir une valeur encapsulée: public Boolean getValue() { return this == T; }, mappez-la avec @Enumerated (EnumType.STRING).

0
Ravshan Samandarov