UserType api methods are not called from Criteria Builder query in hibernate

久未见 提交于 2019-12-11 08:08:23

问题


I have a scenario where i use a custom user type for a entity field in hibernate using the org.hibernate.usertype.UserType.

It's a date conversion to UTC

    import org.hibernate.usertype.ParameterizedType;
    import org.hibernate.usertype.UserType;

    public final class UTCTimestampType implements ParameterizedType, UserType
    {..}

My entity class is like the below

@Entity
public class Person extends AbstractPerson
{
    @Column(name = "BIRTH_DT_TM")
    @Type(type = "com.myexample.hibernate.types.UTCTimestampType")
    protected Date birthDtTm;
}

I have two queries to retreive person from Db when current time is greater than brith date

1)One in jpql, which actually calls the UserType#nullSafeSet as expected

2) but if i build the same query via criteria builder the UserType#nullSafeSet implementation in the class UTCTimestampType is never get called and the query just executes.

Am not sure why this happens

this i my sample unit test for case (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();

But my criteria query doesn't go through my custom 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();

I want the birth date field to be converted to UTC time zone before my query is executed, this worked in jpql query, but in criteria builder the custom type is never called and the query executes with the passed in time. My end sql statment should be Ex:SELECT * FROM Person p where p.birthDtTm = date;

where date is in UTC time zone


回答1:


The problem here was that in hibernate version till "hibernate-core" "version = 5.1.5.Final" and "hibernate-entitymanager" "version = 5.1.5.Final" , any user specific types implemented using org.hibernate.usertype.UserType is not supported, since hibernate only acknowledges org.hibernate.type.CompositeCustomType i.e. user types implemented using org.hibernate.usertype.CompositeUserType, this can be seen in the 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());
    }
}

I believe this to be a bug in the hibernate versions, these have been fixed from "hibernate-core" "version = 5.2.0.Final" and higher version [note: these require jdk8]

So there are two ways to achieve the result,

1) update "hibernate-core" "version = 5.2.0.Final" or above [Note: from this version onwards hibernate-entitymanager Hibernate's JPA support has been merged into the hibernate-core module]

if a move to "hibernate-core" "version = 5.2.0.Final" is not possible at the moment, then the below two might come in handy,

2) any fields of java date types with custom user type should implement the CompositeUserType

(or)

3) a hacky way of getting around if the user type implements org.hibernate.usertype.UserType [note: this is highly discouraged]

The is a hack to workaround this is to, create a ParameterExpression for the corresponding predicate then send that to the predicate and only assign the set parameter only when the query creation is completed.

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

but its risky since if a single parameter is missed in the development of the criteria query, then for the reamining of the session only hibernates custom type will be applied.



来源:https://stackoverflow.com/questions/43126937/usertype-api-methods-are-not-called-from-criteria-builder-query-in-hibernate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!