我们遇到了一个问题,试图将CriteriaBuilder用于自定义类型字段。
下面的示例显示使用自定义@Type注释的birthdate字段。
@Entity
@Table(name = "People")
public class People {
@Id
@NotNull
@Column(name = "id")
private Integer id;
@NotNull
@Size(min = 1, max = 20)
@Column(name = "Name")
private String name;
@NotNull
@Column(name = "Birthdate")
@Type(type = "test.MyDateType")
@Temporal(TemporalType.TIMESTAMP)
private MyDate birthdate;
...
}
MyDateType实现了hibernate的EnhancedUserType
package test;
import java.io.Serializable;
import java.sql.*;
import java.text.*;
import java.util.*;
import java.util.Date;
import org.hibernate.HibernateException;
import org.hibernate.usertype.EnhancedUserType;
import org.hibernate.engine.spi.SessionImplementor;
public class MyDateType implements EnhancedUserType {
static final ThreadLocal<Calendar> utcCalendar = new ThreadLocal<Calendar>() {
@Override
protected Calendar initialValue() {
return Calendar.getInstance(TimeZone.getTimeZone("GMT"));
}
};
static final ThreadLocal<SimpleDateFormat> utcSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
df.setTimeZone(TimeZone.getTimeZone("GMT"));
return df;
}
};
private static final int[] SQL_TYPES = {Types.TIMESTAMP};
@Override
public int[] sqlTypes() {
return SQL_TYPES;
}
@Override
public boolean equals(Object o1, Object o2) {
if(o1 == o2) {
return true;
}
if((o1 == null) || (o2 == null)) {
return false;
}
return o1.equals(o2);
}
@Override
public boolean isMutable() {
return true;
}
@Override
public Class<?> returnedClass() {
return objectClass;
}
protected Class<?> objectClass = MyDate.class;
@Override
public Object deepCopy(Object value) {
return (value == null) ? null : new MyDate(((Date)value).getTime());
}
@Override
public Object nullSafeGet(ResultSet rs, String[] columns, SessionImplementor si, Object owner) throws HibernateException, SQLException {
Timestamp ts = rs.getTimestamp(columns[0], utcCalendar.get());
if(ts != null) {
return new Date(ts.getTime());
}
return null;
}
@Override
public void nullSafeSet(PreparedStatement statement, Object value, int index, SessionImplementor si) throws HibernateException,
SQLException {
Object val = value;
if(!(val instanceof java.sql.Timestamp)) {
val = (val == null) ? null : new java.sql.Timestamp(((Date)val).getTime());
}
statement.setTimestamp(index, (java.sql.Timestamp)val, utcCalendar.get());
}
@Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return deepCopy(cached);
}
@Override
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable)deepCopy(value);
}
@Override
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return deepCopy(original);
}
@Override
public Object fromXMLString(String xmlValue) {
try {
return new Date(utcSimpleDateFormat.get().parse(xmlValue).getTime());
} catch(ParseException e) {
System.out.println(e);
}
return xmlValue;
}
@Override
public String objectToSQLString(Object value) {
return null;
}
@Override
public String toXMLString(Object value) {
return value.toString();
}
}
测试时,我们按以下方式创建查询
MyDate start = new MyDate(...);
MyDate end = new MyDate(...):
cq.where(cb.between(root.<MyDate> get("birthdate"), start, end));
TypedQuery<People> q = entityManager.createQuery(cq);
List<People> results = q.getResultList();
在尝试执行查询时获取执行
22:03:44,733 WARN [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (http-/0.0.0.0:8080-1) SQL Error: 210, SQLState: S0001
22:03:44,734 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (http-/0.0.0.0:8080-1) Conversion failed when converting datetime from binary/varbinary string.
我们知道注释有效,因为当我们查询Id时,我们会为birthdate字段获取正确的数据。 当我们构建以下查询时,它也可以正常工作。
MyDate start = new MyDate(...);
MyDate end = new MyDate(...):
TypedQuery<People> q = entityManager.createQuery("select p from people where birthdate >= :start and birthdate <= :end", People.class);
q.setParameter("start", start);
q.setParameter("end", end);
List<People> results = q.getResultList();
使用 CriteriaBuilder 和 CriteriaQuery 构建查询时,我们忽略了什么?