【问题标题】:UserType api methods are not called from Criteria Builder query in hibernate休眠中的 Criteria Builder 查询不调用 UserType api 方法
【发布时间】:2017-08-24 21:56:27
【问题描述】:

我有一个场景,我使用 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;
}

当当前时间大于出生日期时,我有两个查询要从 Db 中检索人

1)jpql中的一个,实际调用了UserType#nullSafeSet如预期的那样

2) 但如果我通过标准构建器构建相同的查询,则永远不会调用 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 类

        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 where p.birthDtTm = date;

日期在 UTC 时区中

【问题讨论】:

  • 尝试使用 @Converter , javax.persistence.AttributeConverter 仍然看到相同的结果,有谁知道我会做错什么?遵循此文档click me

标签: hibernate jpa criteria jpa-criteria


【解决方案1】:

这里的问题是在休眠版本中直到 "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 实现的用户类型。复合用户类型, 这可以在课堂上看到

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 [注意:这是非常不鼓励的]

这是一种解决方法, 为相应的谓词创建一个 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();

但这是有风险的,因为如果在标准查询的开发过程中遗漏了一个参数,那么对于会话的扩展,只会应用休眠自定义类型。

【讨论】:

猜你喜欢
  • 2012-03-01
  • 2016-03-08
  • 1970-01-01
  • 1970-01-01
  • 2022-08-15
  • 2013-08-03
  • 2011-08-09
相关资源
最近更新 更多