2015-03-30 15 views
10

Ho definito un Hibernate UserType per trasformare i dati prima che entrino nel nostro database e quindi annullare la trasformazione quando viene riletto dal db. Funziona bene quando inserisco una riga o ottieni righe usando l'ID della riga o un altro modo per interrogare la riga. Tuttavia, quando si tenta di utilizzare una query per trovare un record, il legame parametro sembra fallire:Sospensione: impossibile eseguire la query con un UserType nella clausola where

org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [thisIsTheSearchString] did not match expected type [com.xxx.MyUserType (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [thisIsTheSearchString] did not match expected type [com.xxx.MyUserType (n/a)] 

ho provato attuazione LiteralType e il metodo objectToSQLString ma non sembra che questo metodo viene mai chiamato.

Come un esempio semplificato:

public class MyUserType implements UserType, LiteralType { 

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

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

    @Override 
    public boolean equals(Object x, Object y) throws HibernateException { 
     return ObjectUtils.equals(x, y); 
    } 

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

    @Override 
    public Object nullSafeGet(
      ResultSet rs, 
      String[] names, 
      SessionImplementor session, 
      Object owner) 
        throws HibernateException, SQLException 
    { 
     assert names.length == 1; 
     return untransform(rs.getString(names[0]);); 
    } 

    String transform(String untransformed) { 
     //... 
    } 

    String untransform(String transformed) { 
     //... 
    } 

    @Override 
    public void nullSafeSet(
      PreparedStatement st, 
      Object value, 
      int index, 
      SessionImplementor session) 
        throws HibernateException, SQLException 
    { 
     if (value == null) { 
      st.setNull(index, Types.VARCHAR); 
     } else { 
      final String untransformed = (String)value; 

      return transform(untransformed); 
     } 
    } 

    @Override 
    public Object deepCopy(Object value) throws HibernateException { 
     if (value == null) { 
      return null; 
     } 
     return (String)value; 
    } 

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

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

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

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

    // THIS NEVER GETS CALLED 
    @Override 
    public String objectToSQLString(Object value, Dialect dialect) 
      throws Exception 
    { 
     if (value == null) { 
      return null; 
     } 

     String transformed = transform((String)value); 

     StringType stringType = new StringType(); 
     String sqlString = stringType.objectToSQLString(transformed, dialect); 

     return sqlString; 
    } 
} 

L'entità assomiglia:

@Entity 
@Table(name = "blah_blah") 
@TypeDefs(value = { @TypeDef(name = "transformedText", typeClass = MyUserType.class)}) 
public class BlahBlah implements Serializable, Persistable<Long> { 

    //... 

    @Column(name = "transformed") 
    @Type(type = "transformedText") 
    String transformed; 

    //... 
} 

La mia domanda:

@Query(value = 
     "select b " + 
     "from BlahBlah b " + 
     "where b.transformed = ?1 ") 
public List<BlahBlah> findTransformed(String text); 
+0

Nella tua domanda ("dove b.transformed =? 1") dopo il punto interrogativo hai 1, è un errore di battitura al momento di pubblicare questa domanda o è realmente esistente nel tuo codice? :) – Bikku

+0

Rin, che "? 1" è nel codice. Credo che sia corretto - è come funziona la sostituzione dei parametri. –

risposta

2

Credo che hai bisogno di cambiare la classe restituita:

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

dovrebbe essere:

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

nella documentazione (https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/usertype/UserType.html#returnedClass()):

resoClass

Classe returnedClass()

classe restituita dal nullSafeGet(). Restituisce: Classe

e la vostra nullSafeGet sembra essere restituendo una stringa.

+0

Questo ha funzionato. Hibernate sta usando nullSafeSet per ottenere la stringa che deve lanciare nella query. Sembra che non stia utilizzando il metodo objectToSQLString, quindi potrebbe essere impossibile implementare LiteralType. –

1

Con Data primavera è possibile implementare un custom query implementation e si può scartare il EntityManager a una sessione di sospensione e quindi fornire il tipo personalizzato E si è creato alla tua richiesta:

@PersistenceContext 
private EntityManager entityManager; 

public List<BlahBlah> findTransformed(String text) { 
    Session session = entityManager.unwrap(Session.class); 

    TypeHelper typeHelper = session.getSessionFactory().getTypeHelper(); 

    List<BlahBlah> result = (List<BlahBlah>) session.createQuery(
     "select b " + 
     "from BlahBlah b " + 
     "where b.transformed = :transformed ") 
    .setParameter("transformed", text, typeHelper.custom(MyUserType.class)) 
    .list(); 
} 
Problemi correlati