Domanda

Sto usando Hibernate come mio fornitore JPA con il suo collegamento ad un database Progress. Quando un valore NaN persiste, causa molti problemi: impedisce la lettura della riga in determinate circostanze. C'è un modo per agganciarsi alla persistenza standard di doppio tipo per convertire NaN (e probabilmente + e - infinito) in un valore diverso? Non importa se si perdono le informazioni NaN o infinito, voglio solo una riga leggibile!

So che potrei fare qualcosa del genere:

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

Ma sono preoccupato per la manutenzione in quanto dovrà essere aggiunto manualmente per i doppi associati al database.

È stato utile?

Soluzione

La mia prima impressione sarebbe quella di cercare il tipo in cui Hibernate persiste double come. Quindi puoi riformattare il metodo set (...) in DoubleType . Ciò significherebbe tuttavia che dovrai annotare ogni tipo di Double con @ org.hibernate.annotations.type (type = " myDouble ") dopo aver definito " myDouble " usando @ org.hibernate.annotations.TypeDef in informazioni sul pacchetto - penso che tu voglia evitare tutto questo per mantenimento (oltre a ciò dovresti andare nel cuore di Hibernate).

Altri suggerimenti

È possibile modificare l'ibernazione stessa. Tutto quello che devi fare è cambiare la classe DoubleType.

Ovviamente, dovresti mantenere quella patch man mano che l'ibernazione si evolve, ma considerando che si trova in una singola classe piuttosto stabile, potrebbe essere più semplice che specificare un UserType per ogni doppia nel tuo modello di dominio.

A seguito di questa discussione , ho la sensazione, che in letargo non offre un modo per convertire NaN in qualcos'altro. Penso che devi prevenire i valori NaN prima, anche prima che vengano scritti nelle variabili dei membri del bean (come l'aggiunta del codice di protezione / conversione ai setter).

Modifica

Temo che la migliore soluzione spiacevole sia usare il codice di guardia e, cosa ancora peggiore, una colonna aggiuntiva sulla tabella, per contrassegnare se il valore è un numero o meno. Ciò che certamente complicherà la query e le operazioni di inserimento. Ma hai bisogno di NaN nel database e non puoi combattere il driver / database jdbc per comportarsi correttamente (e accettare NaN come input valido per NUMBER campi).

Alla fine ho usato la soluzione UserType ma ho risolto il problema di manutenzione con un test unitario. La classe di tipo è la seguente:

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

Il test unitario è approssimativamente (alcuni dettagli rimossi per brevità):

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

Ho avuto esattamente lo stesso problema e, con la guida di queste soluzioni, ho anche preparato una classe di tipo personalizzata che estende DoubleType. All'interno di quella classe ho convertito i valori NaN in null nella funzione set e viceversa per la funzione get, poiché null è OK per le colonne del mio database. Ho anche cambiato la mappatura per le possibili colonne NaN nella classe di tipo personalizzata. Tale soluzione ha funzionato perfettamente per l'ibernazione 3.3.2.

Sfortunatamente, dopo aver aggiornato Hibernate a 3.6.10, ha smesso di funzionare. Per farlo funzionare di nuovo, ho sostituito il tipo personalizzato dall'estensione di DoubleType per implementare UserType.

Le importanti implementazioni delle funzioni dei tipi di dati dovrebbero essere le seguenti:

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

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

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

Ed ecco le funzioni get e 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);
}

scusa ma a giudicare dai tuoi esempi e dalla tua domanda in realtà hai problemi a comprendere la persistenza di Java. Le entità del database sono autogestite tramite getter e setter: possono fare qualsiasi convalida che desideri che abbiano. Se imposti davvero degli attributi senza di essi, perdi i concetti fondamentali di sviluppo orientato agli oggetti E persistenza, in particolare le entità gestite. Pensa che tu debba riprogettare il tuo progetto, avere problemi come questi sono un segno sicuro di difetti di progettazione fondamentali ... solo dando un consiglio qui - e questa è la soluzione:

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

.... è così facile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top