2009-11-27 4 views
2

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

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

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

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

+0

Вы изучали использование аннотации @NotNull Hibernate Validator? (Это может быть не вариант, если вы хотите только чистый JPA) – Tim

+0

Делает ли @NotNull проверку для NaN? –

ответ

3

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

+0

Это похоже на лучшее решение - спасибо за помощь. 137 @Показать аннотации позже, и это работает! –

1

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

EDIT

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

+0

Спасибо за это - я надеялся, что может быть что-то, что участники этой дискуссии пропустили. Решение защиты (или в этом случае UserType) испытывает те же проблемы обслуживания, что и в вопросе. Плюс в этой ситуации NaN на самом деле является правильным результатом, поэтому он чувствует себя неправомерным, чтобы сделать его незаконным на уровне объекта (даже если он теряется при его сопоставлении). –

+0

Это плохо - потому что это, очевидно, базы данных и драйверы jdbc, которые имеют проблемы с обработкой NaN, а не спящий режим ... и эта проблема NaN, кажется, предотвращает переносное (независимое от базы данных) решение ... –

+0

Да, это определенно проблема JDBC - I надеялся, что Hibernate сможет обойти ошибки для меня! В любом случае, я бы хотел, чтобы я принял ваш ответ, но я разрешу только выбрать его. –

1

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

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

+0

Это определенно правильно - к сожалению, SO позволит мне принять только один ответ. Я подумал об этом, но, как вы упомянули, это немного больно, когда обновляется Hibernate. Спасибо за вашу помощь. –

1

В конце концов я использовал решение 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

У меня была точно такая же проблема, а также с руководством этих решений, я также подготовил класс пользовательского типа, расширяющий DoubleType. Внутри этого класса я преобразовал значения NaN в null в функцию set и наоборот для функции get, так как null для столбцов базы данных OK. Я также изменил отображение для возможных столбцов NaN в класс настраиваемого типа. Это решение отлично работало для спящего режима 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; 
} 

А вот получить и установить функции:

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

извините, но, судя по вашим примерам и ваш вопрос вы на самом деле есть проблемы понимания java persistence. Субъекты базы данных управляются самостоятельно через геттеры и сеттеры - они могут выполнять любую проверку, которую вы хотели бы иметь. Если вы действительно устанавливаете атрибуты без них, вам не хватает основных концепций объектно-ориентированной разработки и, в частности, сущности, управляемых сущностями. Methinks вам нужно реорганизовать свой проект, имея такие проблемы, как они верный признак основных конструктивных недостатков ... просто дать некоторые советы здесь - и вот решение:

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

.... его, что легко.

Смежные вопросы