2016-04-25 10 views
0

In einigen Legacy-Code, mit denen ich arbeite, habe ich ein Hibernate-Modell mit einem UserType-Attribut, um einen Boolean mit Ganzzahlen für die Indizierung darzustellen. Außerdem speichert es 'false' als null für die Oracle-Optimierung.Abfrage im Ruhezustand mit UserType Spalte macht keine Konvertierung mit nullSafeSet

/** 
* Data type for a boolean value stored in a NUMBER oracle column. 1 evaluates 
* to true, null evaluates to false. 
* 
* This is designed for oracle indexing, since null values are not indexed. 
* 
* 
*/ 

public class BooleanUserType implements UserType { 
    private static final int[] SQL_TYPES = { Types.INTEGER }; 

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

    public Class<?> returnedClass() { 
     return Boolean.class; 
    } 

    public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException { 
     Boolean result = false; 
     resultSet.getInt(names[0]); 
     if (!resultSet.wasNull()) { 
      result = true; 
     } 
     return result; 
    } 

    public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index) throws HibernateException, SQLException { 
     Boolean input = (Boolean) value; 
     if (input == null || input == false) { 
      preparedStatement.setNull(index, Types.INTEGER); 

     } else { 
      preparedStatement.setInt(index, 1); 
     } 
    } 

    public Object deepCopy(Object value) throws HibernateException { 
     return value; 
    } 

    public boolean isMutable() { 
     return false; 
    } 

    public Object assemble(Serializable cached, Object owner) throws HibernateException { 
     return cached; 
    } 

    public Serializable disassemble(Object value) throws HibernateException { 
     return (Serializable) value; 
    } 

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

    public int hashCode(Object x) throws HibernateException { 
     return x.hashCode(); 
    } 

    public boolean equals(Object x, Object y) throws HibernateException { 
     if (x == y) 
      return true; 
     if (null == x || null == y) 
      return false; 
     return x.equals(y); 
    } 

Dieser BooleanUserType als eine Spalte für dieses (bearbeitet) Modell:

public class CustomString implements Serializable{ 
    private static final long serialVersionUID = -5311205585865001342L; 

    @Id 
    @Column(name="custom_string_id") 
    private Long id; 

    @Column(name="text") 
    private String text; 

    @Column(name="active") 
    @Type(type="my.org.BooleanUserType") // store false as 'null' -- an Oracle optimization 
    private Boolean active; 

    (...) 

zu this StackOverflow reply Nach

nullSafeSet aufgerufen wird, sowohl wenn das Unternehmen gespeichert/aktualisiert wird und wenn Abfrageparameter gesetzt werden muss

Dies ist jedoch nicht das Verhalten, das ich beobachte, als wenn ich für dieses Modell mit einem Boolean für die active Spalte abfragen, wird die Konvertierung nicht vorgenommen und ich bekomme nicht die erwarteten Ergebnisse.

Ich habe ein Custom Objekt auf der DB gespeichert:

new CustomString(1, "text", Boolean.FALSE); 

ich die Abfrage machen, die nicht wie ich erwarten würde funktioniert:

Criteria c = session.createCriteria(CustomString.class); 
c.add(Restrictions.eq("active", Boolean.FALSE)); 
c.list() // No results! 

Wenn ich diese stattdessen tun, es funktioniert:

Criteria c = session.createCriteria(CustomString.class); 
c.add(Restrictions.isNull("active")); 
c.list() // Expected result! 

Speichern auch das Objekt mit Boolean.TRUE statt FALSE, und dann für TRUE Abfrage, funktioniert gut.

Ich habe einige Debug-Protokollzeilen in meiner nullSafeSet-Methode hinzugefügt, und ich kann überprüfen, dass es nicht aufgerufen wird, wenn die Abfrage erstellt wird.

Sollte nullSafeSet eigentlich von Hibernate aufgerufen werden und irgendwie nicht wie erwartet funktionieren, oder fehlt mir da etwas und ich sollte es anders angehen?

Antwort

0

Die zu Grunde liegende Logik nullSafeSet() Logik wird nur ausgewertet, wenn Objekte in Einfüge-/Aktualisierungsszenarien tatsächlich in die DB gespült werden. Wenn Sie eine Critera Abfrage erstellen, verwendet Hibernate die @Column Annotation, um Spaltennamen zu bestimmen, wird aber nicht automatisch "= FALSE" in "is null" für Sie umdrehen.

Genauer gesagt, der SELECT Teil der Abfrage ist durch die in der Klasse definiert @Columns pro bestimmt, so Hibernate immer ausgeführt wird:

SELECT custom_string_id, text, active FROM [schema].[table_name] 

Restrictions Hinzufügen zu Ihrem Criteria dort vermehren wird WHERE Prädikat, aber Es wird Ihre BooleanUserType Logik erst dann wirksam einsetzen, wenn es Zeilen zurückerhält, die in Instanzen Ihrer Entität geparst werden.

Schließlich bin ich nicht sicher, welche Oracle-Optimierung Sie bezogen haben, aber wenn Sie versuchen, die Tatsache, dass Null-Werte aus Indizes zu nutzen versuchen, möchten Sie möglicherweise nicht für NULL statt False als anfordern Es würde einen vollständigen Tabellenscan ausführen.Wenn Sie jedoch nur die aktiven Zeilen (True) interessiert haben und inaktive Zeilen effektiv veralten/archivieren möchten, dann würde Ihr Ansatz, die alten Daten auf NULL zu setzen, tatsächlich die Index-Leistungsgewinne erzielen, auf die Sie sich beziehen.

+0

Was ist mit einem benutzerdefinierten Typ stattdessen? http://learningviacode.blogspot.ie/2011/09/creating-hibernate-custom-type-2.html – mendez8

+0

Ich habe keine großen Hoffnungen dafür, aber Sie können dies überprüfen: http: // stackoverflow .com/questions/29145693/benutzerdefiniert-Prädikat-to-handle-custom-user-defined-types-hibernate-usertype –