Настройте режим гибернации (с использованием JPA) для хранения Y / N для типа Boolean вместо 0/1 - PullRequest
73 голосов
/ 20 июля 2009

Можно ли настроить JPA / спящий режим для сохранения Boolean типов как Y/N? В базе данных (столбец определен как varchar2(1). В настоящее время он хранит их как 0/1. База данных - Oracle.

Ответы [ 7 ]

136 голосов
/ 20 июля 2009

Hibernate имеет встроенный тип "yes_no", который будет делать то, что вы хотите. Он отображается в столбец CHAR (1) в базе данных.

Основное отображение: <property name="some_flag" type="yes_no"/>

Отображение аннотаций (расширения Hibernate):

@Type(type="yes_no")
public boolean getFlag();
77 голосов
/ 13 марта 2014

Это чистый JPA без использования методов получения / установки. По состоянию на 2013/2014 гг. Это лучший ответ без использования каких-либо аннотаций, специфичных для Hibernate, но, пожалуйста, обратите внимание, что это решение JPA 2.1, и оно не было доступно, когда вопрос был задан впервые:

@Entity
public class Person {    

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

А потом:

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

Редактировать:

Приведенная выше реализация рассматривает все, что отличается от символа "Y", включая null, как false. Это верно? Некоторые люди здесь считают это неверным и считают, что null в базе данных должно быть null в Java.

Но если вы вернете null в Java, вы получите NullPointerException, если ваше поле будет примитивным логическим . Другими словами, если только некоторые из ваших полей на самом деле не используют класс Boolean , лучше всего рассматривать null как false и использовать приведенную выше реализацию. Тогда Hibernate не будет выдавать никаких исключений независимо от содержимого базы данных.

И если вы действительно хотите принять null и выдавать исключения, если содержимое базы данных не совсем корректно, то, я думаю, вам не следует принимать любые символов, кроме "Y", "N "и null. Сделайте это непротиворечивым и не допускайте никаких изменений, таких как «y», «n», «0» и «1», которые только усложнят вашу жизнь в дальнейшем. Это более строгая реализация:

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

И еще один вариант, если вы хотите разрешить null в Java, но не в базе данных:

@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);
    }
12 голосов
/ 28 мая 2014

Я использовал концепцию из ответа, опубликованного @marcg, и он отлично работает с JPA 2.1. Его код был не совсем верным, поэтому я разместил свою рабочую реализацию. Это преобразует Boolean поля сущностей в столбец символов Y / N в базе данных.

Из моего класса сущности:

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

Мой класс конвертеров:

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

}

Этот вариант также интересен, если вы любите смайлики и просто устали от Y / N или T / F в вашей базе данных. В этом случае столбец базы данных должен содержать два символа вместо одного. Вероятно, не имеет большого значения.

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

}
8 голосов
/ 20 июля 2009

Единственный способ, которым я понял, как это сделать - это иметь два свойства для моего класса. Один как логическое значение для API программирования, который не включен в отображение. Это getter и setter, ссылающиеся на приватную переменную char, которая равна Y / N. Затем у меня есть другое защищенное свойство, которое включено в отображение hibernate, и его методы получения и установки напрямую ссылаются на частную переменную char.

РЕДАКТИРОВАТЬ: Как уже указывалось, есть другие решения, которые непосредственно встроены в Hibernate. Я оставляю этот ответ, потому что он может работать в ситуациях, когда вы работаете с устаревшим полем, которое не очень хорошо сочетается со встроенными опциями. Кроме того, у этого подхода нет серьезных негативных последствий.

2 голосов
/ 15 мая 2013

Чтобы сделать еще лучше логическое отображение на Y / N, добавьте в свою конфигурацию гибернации:

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

Теперь вы можете использовать логические значения в HQL, например:

"FROM " + SomeDomainClass.class.getName() + " somedomainclass " +
"WHERE somedomainclass.someboolean = false"
0 голосов
/ 21 апреля 2014

с использованием преобразователей JPA 2.1 - лучшее решение, однако, если вы используете более раннюю версию JPA, я могу порекомендовать еще одно решение (или обходной путь). Создайте перечисление BooleanWrapper с двумя значениями T и F и добавьте к нему следующий метод, чтобы получить упакованное значение: public Boolean getValue() { return this == T; }, сопоставьте его с @Enumerated (EnumType.STRING).

0 голосов
/ 15 января 2014

Чтобы сделать это стандартным способом JPA с использованием аннотаций геттеров, приведенный ниже пример работает для меня с Hibernate 3.5.4 и Oracle 11g. Обратите внимание, что сопоставленные методы получения и установки (getOpenedYnString и setOpenedYnString) являются закрытыми методами. Эти методы обеспечивают отображение, но весь программный доступ к классу использует методы getOpenedYn и 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;
}

Вот класс утилит со статическими методами toYesNo и 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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...