2015-03-30 7 views
10

J'ai un Hibernate UserType défini pour transformer les données avant qu'elles ne pénètrent dans notre base de données, puis les annuler quand elles sont relues depuis la base de données. Cela fonctionne bien lorsque j'insère une ligne ou que j'obtiens des lignes en utilisant l'ID de la ligne ou d'une autre manière pour interroger la ligne. Cependant, lorsque je tente d'utiliser une requête pour trouver un enregistrement, le paramètre de liaison semble échouer:Hibernate - Impossible d'exécuter la requête avec un UserType dans la clause 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)] 

j'ai essayé la mise en œuvre LiteralType et la méthode objectToSQLString mais il ne ressemble pas à cette méthode est jamais appelée.

Comme exemple simplifié:

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é ressemble à:

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

    //... 
} 

Ma requête:

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

Dans votre requête ("où b.transformed =? 1") après le point d'interrogation vous avez 1, est-ce une faute de frappe dans le moment de la publication de cette question ou existe-t-il réellement dans votre code? :) – Bikku

+0

Rin, que "? 1" est dans le code. Je crois que c'est correct - c'est comme cela que fonctionne la substitution de paramètres. –

Répondre

2

Je pense que vous avez besoin de changer la classe retournée:

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

devrait être:

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

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

retournéClasse

Classe returnedClass()

La classe retournée par nullSafeGet(). Retours: Classe

et votre nullSafeGet semble être le retour d'une chaîne.

+0

Cela a fonctionné. Hibernate utilise nullSafeSet pour obtenir la chaîne qu'il doit lancer dans la requête. Il ne semble pas du tout utiliser la méthode objectToSQLString, il est donc possible de ne pas implémenter LiteralType. –

1

Avec vous pouvez données Spring mettre en œuvre un custom query implementation et vous pouvez déballez le EntityManager à une session Hibernate, puis fournissez le type personnalisé youe vous avez créé à votre requête:

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