在hibernate中,不会从Criteria Builder查询中调用UserType api方法

时间:2017-03-30 19:38:36

标签: hibernate jpa criteria jpa-criteria

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

1 个答案:

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

但风险很大,因为如果在开发条件查询时遗漏了单个参数,那么对于会话的重新定义,只会应用休眠自定义类型。