6

Ich habe ein Hibernate-Mapped-Java-Objekt, JKL, die voll von einer Reihe von normalen Hibernate-mappable Felder (wie Strings und Ganzzahlen).Mapping einer FunctionalJava-Option <Type> mit Hibernate

Ich bin ein neues eingebettetes Feld hinzugefügt (die in der gleichen Tabelle lebt - keine Zuordnung), asdf, die eine fj.data.Option<ASDF> ist. Ich habe es zu einer Option gemacht, um klarzustellen, dass dieses Feld tatsächlich nichts enthält (im Gegensatz dazu, dass ich jedes Mal, wenn ich darauf zugreife, mit null umgehen muss).

Wie richte ich das Mapping in meiner JKL.hbm.xml Datei ein? Ich möchte Hibernate, um automatisch eine null in der Datenbank in eine none von fj.data.Option<ASDF> konvertieren, wenn es das Objekt abruft. Es sollte auch eine Nicht-Null-Instanz von ASDF in eine some von fj.data.Option<ASDF> konvertieren. Gibt es noch andere Tricks, die ich tun muss? Vielen Dank.

Antwort

12

Ich würde vorschlagen, FunctionalJava Option in den Accessoren (Getter und Setter), während Hibernate ein einfaches Java-Feld, das null sein darf, zu behandeln.

Zum Beispiel für ein optionales Integer Feld:

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

Sie ein Hibernate privates Feld direkt abbilden:

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

Sie könnten dann einführen Option an der Accessor Grenze:

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

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

Funktioniert das für Ihre Bedürfnisse?

2

Sie können die benutzerdefinierten Zuordnungstypen von Hibernate verwenden. Dokumentation ist here. Hier ist ein analoges Beispiel von mapping Scala's Option zu einem Hibernate-Mapping.

Einfach gesagt, müssten Sie die org.hibernate.UserType Schnittstelle erweitern. Sie könnten auch eine generische typisierte Basisklasse mit einem JKL -typed-Untertyp erstellen, ähnlich wie im Scala-Beispiel.

0

ich getter/setter denke, mit einfacher, aber hier ist ein Beispiel dafür, was ich getan habe, damit es funktioniert:

(Es funktioniert gut für Nummer und String, aber nicht für das Datum (Fehler mit @Temporal Anmerkung)).

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