我有一个场景,我使用 org.hibernate.usertype.UserType 在hibernate中为实体字段使用自定义用户类型。
日期转换为UTC
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;
public final class UTCTimestampType implements ParameterizedType, UserType
{..}
我的实体类如下所示
@Entity
public class Person extends AbstractPerson
{
@Column(name = "BIRTH_DT_TM")
@Type(type = "com.myexample.hibernate.types.UTCTimestampType")
protected Date birthDtTm;
}
当前时间大于brith date
时,我有两个查询从Db审阅此人1)jpql中的一个,实际上按预期调用UserType#nullSafeSet
2)但如果我通过条件构建器构建相同的查询,则类UTCTEststampType中的 UserType#nullSafeSet 实现永远不会被调用,并且查询只会执行。
我不确定为什么会发生这种情况
这是我对案例(1)的样本单元测试
String query = "SELECT pFROM Person p where p.birthDtTm = :now";
Query q = entityManager.createQuery(query);
q.setParameter("now", new Date());
List<Person> persons= q.getResultList();
但我的条件查询没有通过我的自定义UserType类
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> criteriaQuery = criteriaBuilder.createQuery(Person.class);
Root<Person> root = criteriaQuery.from(Person.class);
Predicate predicate = criteriaBuilder.greaterThanOrEqualTo(
root.get(Person_.birthDtTm), new Date());
criteriaQuery.where(predicate);
criteriaQuery.select(root);
TypedQuery<Person> q = entityManager.createQuery(criteriaQuery2);
List<Person> persons = q.getResultList();
我希望在我的查询执行之前将出生日期字段转换为UTC时区,这在jpql查询中有效,但在条件构建器中,永远不会调用自定义类型,并且查询将按传入时间执行。 我的结束sql语句应该是 例如:SELECT * FROM Person p,其中p.birthDtTm = date;
其中日期为UTC时区
答案 0 :(得分:0)
这里的问题是在hibernate版本中直到“hibernate-core”“version = 5.1.5.Final”和“hibernate-entitymanager”“version = 5.1.5.Final “,使用实现的任何用户特定类型 org.hibernate.usertype.UserType 不受支持,因为hibernate仅确认 org.hibernate.type.CompositeCustomType ,即使用 org.hibernate.usertype实现的用户类型。 CompositeUserType 下, 这可以在课堂上看到
org.hibernate.jpa.internal.QueryImpl#mightNeedRedefinition
private boolean mightNeedRedefinition(Class javaType, Type expectedType)
{
// only redefine dates/times/timestamps that are not wrapped in a CompositeCustomType
if(expectedType == null)
{
return java.util.Date.class.isAssignableFrom(javaType);
}
else
{
return java.util.Date.class.isAssignableFrom(javaType)
&& !CompositeCustomType.class.isAssignableFrom(expectedType.getClass());
}
}
我认为这是hibernate版本中的一个错误,这些已经从“hibernate-core”“version = 5.2.0.Final”和更高版本修复[注意:这些需要jdk8]
所以有两种方法可以实现结果,
1)更新“hibernate-core”“version = 5.2.0.Final”或以上[注意:从此版本开始hibernate-entitymanager Hibernate的JPA支持已合并到hibernate-core模块]
如果暂时不能转移到“hibernate-core”“version = 5.2.0.Final”,那么下面的两个可能会派上用场,
2)具有自定义用户类型的任何java日期类型字段都应实现CompositeUserType
(或)
3)如果用户类型实现org.hibernate.usertype.UserType [注意:强烈建议不要这样做],这是一种愚蠢的解决方式。
这是一个解决方法,这是, 为相应的谓词创建一个ParameterExpression 然后将其发送到谓词,仅在查询创建完成时才分配set参数。
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> criteriaQuery = criteriaBuilder.createQuery(Person.class);
Root<Person> root = criteriaQuery.from(Person.class);
ParameterExpression<Date> p = criteriaBuilder.parameter(Date.class);
Predicate predicate = criteriaBuilder.greaterThanOrEqualTo(root.get(Person_.birthDtTm), p);
criteriaQuery.where(predicate);
criteriaQuery.select(root);
TypedQuery<Person> q = entityManager.createQuery(criteriaQuery2);
query.setParameter(p, new Date();
List<Person> persons = q.getResultList();
但风险很大,因为如果在开发条件查询时遗漏了单个参数,那么对于会话的重新定义,只会应用休眠自定义类型。