1

我有一个场景,我使用org.hibernate.usertype.UserType为休眠中的实体字段使用自定义用户类型。

这是日期转换为 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 语句应该是 Ex:SELECT * FROM Person p where p.birthDtTm = date;

日期在 UTC 时区

4

1 回答 1

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.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-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,然后将其发送到谓词,并且仅在查询创建完成时才分配设置的参数。

        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();

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

于 2017-04-03T22:08:03.127 回答