6

Ho un oggetto Java con mappatura Hibernate, JKL, che è pieno di una serie di normali campi mappabili in modalità hibernate (come stringhe e interi).Mapping un'opzione FunctionalJava <Type> con Hibernate

Viene aggiunto un nuovo campo incorporato (che si trova nella stessa tabella, non una mappatura), asdf, che è un fj.data.Option<ASDF>. Ho reso un'opzione per chiarire che questo campo potrebbe non contenere nulla (al contrario di dover gestire null ogni volta che accedo ad esso).

Come si configura la mappatura nel file JKL.hbm.xml? Mi piacerebbe ibernare per convertire automaticamente un null nel database in un none di fj.data.Option<ASDF> quando recupera l'oggetto. Dovrebbe anche convertire un'istanza non nulla di ASDF in un some di fj.data.Option<ASDF>. C'è qualche altro inganno che devo fare? Grazie.

risposta

12

Suggerirei di introdurre Option di FunctionalJava negli accessor (getter e setter), lasciando Hibernate in grado di gestire un semplice campo java che può essere null.

Ad esempio, per un optional Integer campo:

// SQL 
CREATE TABLE `JKL` (
    `JKL_ID` INTEGER PRIMARY KEY, 
    `MY_FIELD` INTEGER DEFAULT NULL 
) 

È possibile mappare un campo privato Hibernate direttamente:

// Java 
@Column(nullable = true) 
private Integer myField; 

Si potrebbe quindi introdurre Option al confine di accesso:

// Java 
public fj.data.Option<Integer> getMyField() { 
    return fj.data.Option.fromNull(myField); 
} 

public void setMyField(fj.data.Option<Integer> value) { 
    myField = value.toNull(); 
} 

Funziona per le tue esigenze?

2

È possibile utilizzare i tipi di mapping personalizzati di Hibernate. La documentazione è here. Ecco un esempio analogo di mapping Scala's Option a una mappatura Hibernate.

In poche parole, è necessario estendere l'interfaccia org.hibernate.UserType. Puoi anche creare una classe base generica con un sottotipo di tipo JKL, simile a quello che vedi nell'esempio di Scala.

0

Penso che utilizzando getter/setter è più semplice, ma qui è un esempio di quello che ho fatto per farlo funzionare:

(Funziona bene per il numero e la stringa, ma non per la data (errore con @Temporal annotazione)).

import com.cestpasdur.helpers.PredicateHelper; 
import com.google.common.annotations.VisibleForTesting; 
import com.google.common.base.Optional; 
import org.apache.commons.lang.ObjectUtils; 
import org.apache.commons.lang.StringUtils; 
import org.hibernate.HibernateException; 
import org.hibernate.usertype.UserType; 
import org.joda.time.DateTime; 

import java.io.Serializable; 
import java.sql.*; 

public class OptionUserType implements UserType { 


@Override 
public int[] sqlTypes() { 
    return new int[]{ 
      Types.NULL 
    }; 
} 

@Override 
public Class returnedClass() { 
    return Optional.class; 
} 

@Override 
public boolean equals(Object o, Object o2) throws HibernateException { 
    return ObjectUtils.equals(o, o2); 

} 

@Override 
public int hashCode(Object o) throws HibernateException { 
    assert (o != null); 
    return o.hashCode(); 
} 

@Override 
public Optional<? extends Object> nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException { 
    return Optional.fromNullable(rs.getObject(names[0])); 
} 

@VisibleForTesting 
void handleDate(PreparedStatement st, Date value, int index) throws SQLException { 
    st.setDate(index, value); 
} 

@VisibleForTesting 
void handleNumber(PreparedStatement st, String stringValue, int index) throws SQLException { 
    Double doubleValue = Double.valueOf(stringValue); 
    st.setDouble(index, doubleValue); 
} 

@Override 
public void nullSafeSet(PreparedStatement st, Object value, int index) throws SQLException { 

    if (value != null) { 
     if (value instanceof Optional) { 
      Optional optionalValue = (Optional) value; 
      if (optionalValue.isPresent()) { 
       String stringValue = String.valueOf(optionalValue.get()); 


       if (StringUtils.isNotBlank(stringValue)) { 

        if (PredicateHelper.IS_DATE_PREDICATE.apply(stringValue)) { 
         handleDate(st, new Date(DateTime.parse(stringValue).getMillis()), index); 
        } else if (StringUtils.isNumeric(stringValue)) { 
         handleNumber(st, stringValue, index); 
        } else { 
         st.setString(index, optionalValue.get().toString()); 
        } 
       } else { 
        st.setString(index, null); 
       } 


      } else { 
       System.out.println("else Some"); 
      } 

     } else { 
      //TODO replace with Preconditions guava 
      throw new IllegalArgumentException(value + " is not implemented"); 

     } 
    } else { 
     st.setString(index, null); 

    } 


} 

@Override 
public Object deepCopy(Object o) throws HibernateException { 
    return o; 
} 

@Override 
public boolean isMutable() { 
    return false; 
} 

@Override 
public Serializable disassemble(Object o) throws HibernateException { 
    return (Serializable) o; 
} 

@Override 
public Object assemble(Serializable serializable, Object o) throws HibernateException { 
    return serializable; 
} 

@Override 
public Object replace(Object original, Object target, Object owner) throws HibernateException { 
    return original; 
} 
}