2015-03-30 21 views
10

Mam typ użytkownika Hibernacja zdefiniowany do transformacji danych, zanim trafi do naszej bazy danych, a następnie cofnij przekształcenie go, gdy jest odczytywany z db. Działa to dobrze, gdy wstawiam wiersz lub otrzymam wiersze za pomocą identyfikatora wiersza lub w inny sposób, aby zapytać o wiersz. Jednak, gdy próbuję użyć kwerendy, aby znaleźć zapis, parametr wiążący wydaje się niepowodzeniem:Hibernate - Nie można wykonać kwerendy z UserType w miejscu gdzie klauzula

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)] 

Próbowałem wykonawczych LiteralType oraz metodę objectToSQLString ale to nie wygląda metoda ta nigdy nie nazwał.

W uproszczonym przykładzie:

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

Podmiot wygląda następująco:

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

    //... 
} 

Moje zapytanie:

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

W zapytaniu ("gdzie b.transformed =? 1") po znaku zapytania masz 1, czy jest to literówka w momencie opublikowania tego pytania lub czy rzeczywiście istnieje w twoim kodzie? :) – Bikku

+0

Rin, że "? 1" jest w kodzie. Uważam, że to prawda - tak działa podstawienie parametrów. –

Odpowiedz

2

Myślę, że trzeba zmienić zwrócony Klasa:

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

powinno być:

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

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

zwrócono

Klasa returnedClass()

Klasa zwrócony przez nullSafeGet(). Powroty: Class

i Twój nullSafeGet wydaje się być zwrócenie String.

+0

To działało. Hibernate używa nullSafeSet, aby uzyskać ciąg, który musi wprowadzić do zapytania. Wydaje się, że w ogóle nie używa ona metody objectToSQLString, więc możliwe jest zaimplementowanie funkcji LiteralType. –

1

ze sprężyną danych można wdrożyć custom query implementation i można rozpakować EntityManager do sesji hibernacji, a następnie podaj niestandardowy typ è utworzony na zapytanie:

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