休眠中的 Criteria Builder 查询不调用 UserType api 方法
UserType api methods are not called from Criteria Builder query in hibernate
我有一个场景,我使用 org.hibernate.usertype.UserType.
在休眠中为实体字段使用自定义用户类型
这是转换为 UTC 的日期
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;
public final class UTCTimestampType implements ParameterizedType, UserType
{..}
我的实体class如下图
@Entity
public class Person extends AbstractPerson
{
@Column(name = "BIRTH_DT_TM")
@Type(type = "com.myexample.hibernate.types.UTCTimestampType")
protected Date birthDtTm;
}
当当前时间大于出生日期时,我有两个查询要从 Db 中检索人
1)jpql中的一个,它实际上按预期调用了UserType#nullSafeSet
2) 但如果我通过条件生成器构建相同的查询,则 class UTCTimestampType 中的 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 class
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 声明应该是
Ex:SELECT * FROM Person p where p.birthDtTm = date;
其中日期为 UTC 时区
这里的问题是在休眠版本中直到 "hibernate-core" "version = 5.1.5.Final" 和 "hibernate-entitymanager" "version = 5.1.5.Final" ,使用实现的任何用户特定类型
不支持 org.hibernate.usertype.UserType,因为 hibernate 只承认 org.hibernate.type.CompositeCustomType,即使用 [= 实现的用户类型56=],
这可以在 class
中看到
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-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,这是一种绕过的 hacky 方式 [注意:非常不鼓励这样做]
解决这个问题的方法是,
为相应的谓词创建一个 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();
但这是有风险的,因为如果在标准查询的开发过程中遗漏了一个参数,那么为了重新定义会话,将只应用休眠自定义类型。
我有一个场景,我使用 org.hibernate.usertype.UserType.
在休眠中为实体字段使用自定义用户类型这是转换为 UTC 的日期
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;
public final class UTCTimestampType implements ParameterizedType, UserType
{..}
我的实体class如下图
@Entity
public class Person extends AbstractPerson
{
@Column(name = "BIRTH_DT_TM")
@Type(type = "com.myexample.hibernate.types.UTCTimestampType")
protected Date birthDtTm;
}
当当前时间大于出生日期时,我有两个查询要从 Db 中检索人
1)jpql中的一个,它实际上按预期调用了UserType#nullSafeSet
2) 但如果我通过条件生成器构建相同的查询,则 class UTCTimestampType 中的 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 class
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 声明应该是 Ex:SELECT * FROM Person p where p.birthDtTm = date;
其中日期为 UTC 时区
这里的问题是在休眠版本中直到 "hibernate-core" "version = 5.1.5.Final" 和 "hibernate-entitymanager" "version = 5.1.5.Final" ,使用实现的任何用户特定类型 不支持 org.hibernate.usertype.UserType,因为 hibernate 只承认 org.hibernate.type.CompositeCustomType,即使用 [= 实现的用户类型56=], 这可以在 class
中看到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-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,这是一种绕过的 hacky 方式 [注意:非常不鼓励这样做]
解决这个问题的方法是, 为相应的谓词创建一个 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();
但这是有风险的,因为如果在标准查询的开发过程中遗漏了一个参数,那么为了重新定义会话,将只应用休眠自定义类型。