Налаштуйте сплячий режим (за допомогою JPA) для збереження Y / N для типу Boolean замість 0/1


80

Чи можу я встановити JPA / hibernate для збереження Booleanтипів як Y/N? У базі даних (стовпець визначається як varchar2(1). В даний час він зберігає їх як 0/1. База даних - Oracle.

Відповіді:


8

Єдиний спосіб, як я зрозумів, як це зробити - це наявність двох властивостей для мого класу. Один як логічний для API програмування, який не включений у зіставлення. Це getter і setter посилаються на приватну змінну char, яка є Y / N. Потім у мене є ще одне захищене властивість, яке включено до зіставлення сплячого режиму, і воно отримує та встановлює посилання безпосередньо на змінну private char.

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


1
Мені довелося зробити щось подібне - я змінив тип члена з Boolean на String. У геттерах і сеттерах (які отримали та встановили Boolean) я написав код для перетворення Y / N у відповідне булеве значення.
sengs

@bernardn Ні, це краще.
Олександр


146

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

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

Зіставлення анотацій (розширення Hibernate):

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

28
Для тих, хто зацікавлений, існує також тип "true_false", який буде зберігати або "T", або "F".
Метт Солніт,

Це спрацювало, але я не міг ним скористатися, оскільки це спеціальна анотація для сплячого режиму. Дякую за відповідь. Може використовувати його в іншому проекті.
sengs

це для hibernate 4 та пізніших версій, це означає для java 1.6 та пізніших версій. дозвільна робота для сплячого режиму 3. *
storm_buster

7
@storm_buster Hibernate 4 не існувало, коли давали цю відповідь. Це чудово працює з Hibernate 3.x та java 1.5
ChssPly76,

3
Як тоді поставити 0 або 1?
JavaTechnical

84

Це чистий 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);
    }

Конвертер показує ідею, але, звичайно, не працює. Перетворювач використовує можливі значення Y, Nі T. Я також не впевнений, що слід опустити випадок наявності нульового значення в результаті перетворення.
Матіас

@Matthias Так, Т був друкарською помилкою. Я полагодив це. Дякую.
MarcG

У мене виникла проблема з використанням поля Y / N із JPQL і я опублікував тут наступне запитання: stackoverflow.com/questions/39581225/…
Кріс Вільямс

11

Я використав концепцію відповіді, опубліковану @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;
    }

}

Цей варіант також цікавий, якщо ви любите смайлики і просто втомилися від Т / Н чи Т / Ж у вашій базі даних. У цьому випадку стовпець бази даних повинен містити два символи замість одного. Можливо, це не велика проблема.

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

}

Чи потрібно розпаковувати 'b.booleanValue ()'?
LeOn - Han Li

За винятком NULLвластивості для NULLзначення бази даних, ця відповідь не надає жодної доданої вартості за допомогою відповіді @MarKG. Найкраще цю різницю фіксувати як коментар.
Mohnish

Ти на 5 років запізнився, хлопець
Джим

2

Щоб навіть краще виконати булеве відображення до 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"

2
Це глобально. Не підходить для особливих властивостей.
maxxyme

0

Щоб зробити це загальним способом JPA, використовуючи анотації getter, наведений нижче приклад працює для мене з Hibernate 3.5.4 та Oracle 11g. Зверніть увагу, що зіставлений getter та setter ( getOpenedYnStringі setOpenedYnString) є приватними методами. Ці методи забезпечують відображення, але весь програмний доступ до класу використовує методи getOpenedYnand 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;
}

Ось клас util зі статичними методами 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);
    }
}

-1

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.