Предотвращение сохранения NaN Hibernate - PullRequest
2 голосов
/ 27 ноября 2009

Я использую Hibernate в качестве провайдера JPA с подключением к базе данных Progress. Когда значение NaN сохраняется, это вызывает много проблем - это предотвращает чтение строки при определенных обстоятельствах. Есть ли способ подключиться к стандартному постоянству двойного типа, чтобы преобразовать NaN (и, вероятно, + и - бесконечность) в другое значение? Неважно, будет ли потеряна информация о NaN или бесконечности, я просто хочу читабельную строку!

Я знаю, что мог бы сделать что-то вроде этого:

@Column(name = "doubleColumn")
public double getDoubleColumn() {
    return PersistedDouble.convert(doubleColumn);
}

Но меня беспокоит обслуживание, так как его придется добавлять вручную для любых пар, сопоставленных с базой данных.

Ответы [ 6 ]

3 голосов
/ 28 ноября 2009

Моим первым впечатлением будет поиск типа, который в Hibernate сохраняется double как. Таким образом, вы можете выполнить рефакторинг метода set(...) в DoubleType. Это означало бы, однако, что вам нужно будет аннотировать каждый тип Double с помощью @org.hibernate.annotations.type(type="myDouble") после того, как вы определили «myDouble», используя @org.hibernate.annotations.TypeDef в package-info - я думаю, вы хотите избежать всего этого для сопровождения (кроме придется идти в сердце Hibernate).

2 голосов
/ 28 ноября 2009

Вы можете изменить сам Hibernate. Все, что вам нужно сделать, это изменить класс DoubleType.

Конечно, вам нужно будет поддерживать этот патч по мере развития гибернации, но, учитывая, что он находится в одном достаточно стабильном классе, это может быть проще, чем указание UserType для каждого двойного в вашей модели домена.

1 голос
/ 05 марта 2013

У меня была точно такая же проблема, и под руководством этих решений я также подготовил класс пользовательских типов, расширяющий DoubleType. Внутри этого класса я преобразовал значения NaN в null в функции set и наоборот для функции get, поскольку для моих столбцов базы данных null в порядке. Я также изменил отображение для возможных столбцов NaN на класс пользовательских типов. Это решение отлично работает для Hibernate 3.3.2.

К сожалению, после обновления Hibernate до 3.6.10 он перестал работать. Чтобы заставить его работать снова, я заменил пользовательский тип с расширения DoubleType на реализацию UserType.

Важные реализации функций типа данных должны быть следующими:

private int[] types = { Types.DOUBLE };

public int[] sqlTypes()
{
    return types;
}

@SuppressWarnings("rawtypes")
public Class returnedClass()
{
    return Double.class;
}

А вот функции get и set:

public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException
{
    Double value = rs.getDouble(names[0]);
    if (rs.wasNull())
        return Double.NaN;
    else
        return value;
}

public void nullSafeSet(PreparedStatement ps, Object value, int index) throws HibernateException, SQLException
{
    Double dbl = (Double) value;
    if ((dbl == null) || (Double.isNaN(dbl)))
        ps.setNull(index, Types.DOUBLE);
    else
        ps.setDouble(index, dbl);
}
1 голос
/ 28 ноября 2009

В конце концов я использовал решение UserType, но решил проблему обслуживания с помощью модульного теста. Класс типов выглядит следующим образом:

public class ParsedDoubleType extends DoubleType {
    private static final long serialVersionUID = 1L;

    @Override
    public void set(PreparedStatement st, Object value, int index) throws SQLException {
        Double doubleValue = (Double) value;
        if (doubleValue.isInfinite() || doubleValue.isNaN()) {
            Logger.getLogger(ParsedDoubleType.class).warn("Attempted to send a NaN or infinity value to the database " 
                + "- this is not supported.\nStatement=" + st + " valueIndex=" + index);
            doubleValue = Double.valueOf(0);
        }
        super.set(st, doubleValue, index);
    }
}

Юнит тест примерно (некоторые детали для краткости удалены):

Ejb3Configuration hibernateConfig = new Ejb3Configuration().configure("InMemoryDatabasePersistenceUnit", null);
for (Iterator<?> iterator = hibernateConfig.getClassMappings(); iterator.hasNext();) {
    PersistentClass clazz = (PersistentClass) iterator.next();
    Iterator<?> propertyIterator = clazz.getPropertyIterator();
    while (propertyIterator.hasNext()) {
        if (property.getType().equals(Hibernate.DOUBLE)) {
            Assert.fail("Raw double type found. Annotate with @Type(type = \"package.ParsedDoubleType\")\n" 
                + "Class " + clazz + " property " + property);
        }
    }
}
1 голос
/ 27 ноября 2009

После этого обсуждения у меня возникает ощущение, что в спящем режиме нет способа превратить NaN во что-то еще. Я думаю, вы должны предотвратить значения NaN раньше, даже до того, как они будут записаны в переменные-члены bean-компонента (например, добавление защитного кода / кода преобразования в сеттеры).

EDIT

Боюсь, лучшее неприятное решение - это использовать защитный код и, что еще хуже, дополнительный столбец в таблице, чтобы указать, является ли значение числом или нет. Что, безусловно, усложнит операции запроса и вставки. Но вам нужен NaN в базе данных, и вы не можете бороться с драйвером / базой данных jdbc, чтобы вести себя правильно (и принять NaN в качестве допустимых входных данных для полей NUMBER).

0 голосов
/ 21 июня 2013

извините, но, судя по вашим примерам и вашему вопросу, у вас действительно есть проблемы с пониманием упорства Java. Объекты базы данных управляются самостоятельно через геттеры и сеттеры - они могут выполнять любую проверку, которую вы хотели бы иметь. Если вы действительно устанавливаете атрибуты без них, вам не хватает основных концепций объектно-ориентированной разработки и персистентности - в частности, управляемых объектов. Мне кажется, что вам нужно реорганизовать свой проект, поскольку подобные проблемы являются явным признаком фундаментальных недостатков дизайна ... просто дать несколько советов здесь - и вот решение:

@Column(name="doubleColumn"}
private Double doubleColumn = Double.NaN  //yes, this is intentional. Verily.

public void setDouble(Double d)
{
    if(d.isNan || d.isInfinite()
    {
       //do something nice here
    }
    else
       this.doubleColumn = d;
}
public Double getDouble()
{
   return !this.doubleColumn.isNaN() && !this.doubleColumn.isInfinite() ? this.doubleColumn : new Double();
}

.... это так просто.

...