Hibernate - 无法在 where 子句中使用 UserType 执行查询
Hibernate - Can't execute query with a UserType in where clause
我定义了一个 Hibernate UserType,用于在数据进入我们的数据库之前转换数据,然后在从数据库中读回数据时取消转换。当我使用行的 ID 或其他查询行的方式插入行或获取行时,这很有效。但是,当我尝试使用查询查找记录时,参数绑定似乎失败了:
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)]
我尝试实施 LiteralType
和 objectToSQLString
方法,但似乎从未调用过此方法。
举个简单的例子:
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;
}
}
实体看起来像:
@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;
//...
}
我的查询:
@Query(value =
"select b " +
"from BlahBlah b " +
"where b.transformed = ?1 ")
public List<BlahBlah> findTransformed(String text);
使用 Spring 数据,您可以实现 custom query implementation 并且可以将 EntityManager
解包到 Hibernate 会话,然后将您创建的自定义类型提供给您的查询:
@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();
}
我认为你需要更改返回的 class:
@Override
public Class returnedClass() {
return MyUserType.class;
}
应该是:
@Override
public Class returnedClass() {
return String.class;
}
在文档中 (https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/usertype/UserType.html#returnedClass()):
returnedClass
Class returnedClass()
The class returned by nullSafeGet().
Returns:
Class
并且您的 nullSafeGet 似乎正在返回一个字符串。
我定义了一个 Hibernate UserType,用于在数据进入我们的数据库之前转换数据,然后在从数据库中读回数据时取消转换。当我使用行的 ID 或其他查询行的方式插入行或获取行时,这很有效。但是,当我尝试使用查询查找记录时,参数绑定似乎失败了:
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)]
我尝试实施 LiteralType
和 objectToSQLString
方法,但似乎从未调用过此方法。
举个简单的例子:
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;
}
}
实体看起来像:
@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;
//...
}
我的查询:
@Query(value =
"select b " +
"from BlahBlah b " +
"where b.transformed = ?1 ")
public List<BlahBlah> findTransformed(String text);
使用 Spring 数据,您可以实现 custom query implementation 并且可以将 EntityManager
解包到 Hibernate 会话,然后将您创建的自定义类型提供给您的查询:
@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();
}
我认为你需要更改返回的 class:
@Override
public Class returnedClass() {
return MyUserType.class;
}
应该是:
@Override
public Class returnedClass() {
return String.class;
}
在文档中 (https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/usertype/UserType.html#returnedClass()):
returnedClass
Class returnedClass()
The class returned by nullSafeGet(). Returns: Class
并且您的 nullSafeGet 似乎正在返回一个字符串。