2009-11-27 7 views
2

J'utilise Hibernate comme fournisseur JPA pour la connexion à une base de données Progress. Quand une valeur NaN persiste, cela cause beaucoup de problèmes - cela empêche la lecture de la ligne dans certaines circonstances. Y at-il un moyen de se connecter à la persistance de type double standard pour convertir NaN (et probablement + et - infini) en une valeur différente? Peu importe si les informations NaN ou Infinity sont perdues, je veux juste une ligne lisible!Empêcher la persistance de NaN par Hibernate

Je sais que je pouvais faire quelque chose comme ceci:

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

Mais je suis inquiet pour l'entretien car cela doit être ajouté manuellement pour toutes les doubles mises en correspondance avec la base de données.

+0

Avez-vous envisagé d'utiliser l'annotation @NotNull de Hibernate Validator? (Cela peut ne pas être une option si vous voulez seulement du JPA pur) – Tim

+0

Est-ce que @NotNull vérifie NaN? –

Répondre

3

Ma première impression de ceci serait de rechercher le type qu'Hibernate conserve double as. Vous pouvez donc refactoriser la méthode set(...) en DoubleType. Cela voudrait dire cependant que vous auriez besoin d'annoter chaque type Double avec @org.hibernate.annotations.type(type="myDouble") après avoir défini "myDouble" en utilisant @org.hibernate.annotations.TypeDef dans package-info - Je pense que vous voulez éviter tout cela pour la maintenance (au-delà de ce que vous auriez à aller dans le coeur d'Hibernate).

+0

Cela semble être la meilleure solution - merci pour votre aide. 137 @Type annotations plus tard et ça marche! –

1

Following this discussion, J'ai le sentiment, que hibernate n'offre pas un moyen de convertir NaN en quelque chose d'autre. Je pense que vous devez empêcher les valeurs NaN plus tôt, avant même qu'elles soient écrites dans les variables membres du bean (comme l'ajout d'un code de garde/conversion aux setters).

EDIT

Je crains, la meilleure solution désagréable est d'utiliser le code de garde et, ce qui est encore pire, une colonne supplémentaire sur la table, pour marquer, si la valeur est un nombre ou non. Ce qui va certainement compliquer les opérations de requête et d'insertion. Mais vous avez besoin de NaN dans la base de données et vous ne pouvez pas combattre le pilote/base de données jdbc pour vous comporter correctement (et accepter NaN comme entrées valides pour les champs NUMBER).

+0

Merci pour cela - j'espérais qu'il y aurait quelque chose que les participants à cette discussion avaient manqué. La solution de garde (ou d'ailleurs UserType) souffre du même problème de maintenance que celui décrit dans la question. De plus, dans cette situation, NaN est en fait le résultat correct, il est donc inapproprié de le rendre illégal au niveau de l'objet (même s'il est perdu lorsqu'il est mappé). –

+0

C'est mauvais - parce que c'est évidemment les bases de données et les pilotes jdbc qui ont des problèmes de gestion de NaN, pas d'hibernation ... et ce problème NaN semble empêcher une solution portable (indépendante de la base de données) –

+0

Ouais c'est vraiment un problème JDBC - I J'espérais qu'Hibernate serait capable de contourner les insectes pour moi! Quoi qu'il en soit, j'aimerais pouvoir accepter votre réponse, mais SO me permettra seulement d'en choisir une. –

1

Vous pouvez modifier Hibernate lui-même. Tout ce que vous avez à faire est de changer la classe DoubleType. Bien sûr, vous devrez alors maintenir ce correctif lorsque Hibernate évoluera, mais étant donné qu'il s'agit d'une classe unique, plutôt stable, cela peut être plus facile que de spécifier un UserType pour chaque doublon de votre modèle de domaine.

+0

Ceci est certainement correct - malheureusement, je ne vais accepter qu'une seule réponse. Je l'ai considéré mais comme tu l'as mentionné, c'est un peu pénible quand Hibernate se met à jour. Merci de votre aide. –

1

J'ai utilisé la solution UserType à la fin, mais j'ai résolu le problème de maintenance avec un test unitaire. La classe de type est la suivante:

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

Le test unitaire est à peu près (quelques détails supprimés par souci de concision):

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

J'ai eu exactement le même problème, et avec les conseils de ces solutions, je a également préparé une classe de type personnalisée qui étend DoubleType. A l'intérieur de cette classe, j'ai converti les valeurs NaN en null à la fonction set et vice versa pour la fonction get, puisque null est OK pour mes colonnes de base de données. J'ai également changé le mappage pour les colonnes possibles NaN à la classe de type personnalisée. Cette solution a parfaitement fonctionné pour Hibernate 3.3.2.

Malheureusement, après la mise à niveau d'Hibernate vers la version 3.6.10, il a cessé de fonctionner. Afin de le faire fonctionner à nouveau, j'ai remplacé le type personnalisé d'étendre DoubleType pour implémenter UserType.

Les importantes mises en œuvre de la fonction de type de données doivent être les suivantes:

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

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

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

Et voici les obtenir et les fonctions sont:

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

désolé mais à en juger par vos exemples et votre question que vous avez réellement problèmes de compréhension de la persistance de Java. Les entités de base de données sont auto-gérées via des getters et des setters - ceux-ci peuvent faire n'importe quelle validation que vous voudriez qu'ils aient. Si vous définissez vraiment les attributs sans eux, il vous manque des concepts de base de développement orienté objet et des entités gérées par persistance en particulier. Il me semble que vous devez réorganiser votre projet, ayant des problèmes comme ceux-ci sont un signe certain de défauts de conception fondamentaux ... juste donner quelques conseils ici - et thats la solution:

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

.... son que facile.

Questions connexes