当前位置: 首页 > 知识库问答 >
问题:

Hibernate CompositeUserType,可在JPA-QL(或HQL)查询中进行比较

仉运乾
2023-03-14

我为Hibernate创建了一个自定义类型来存储OffsetDateTime的时间戳和偏移量(因为默认的JPA 2.2/Hibernate 5.2 with java 8支持实现会丢失偏移量信息):

public class OffsetDateTimeHibernateType implements CompositeUserType {

    @Override
    public Class returnedClass() {
        return OffsetDateTime.class;
    }

    @Override
    public String[] getPropertyNames() {
        return new String[] {"dateTime", "zoneOffset"};
    }

    @Override
    public Type[] getPropertyTypes() {
        // Not sure if we should use LocalDateTimeType.INSTANCE instead of TIMESTAMP
        return new Type[]{StandardBasicTypes.TIMESTAMP, StandardBasicTypes.INTEGER};
    }

    @Override
    public Object getPropertyValue(Object o, int propertyIndex) {
        if (o == null) {
            return null;
        }
        OffsetDateTime offsetDateTime = (OffsetDateTime) o;
        switch (propertyIndex) {
            case 0:
                return Timestamp.valueOf(offsetDateTime.toLocalDateTime());
            case 1:
                return offsetDateTime.getOffset().getTotalSeconds();
            default:
                throw new IllegalArgumentException("The propertyIndex (" + propertyIndex
                        + ") must be 0 or 1.");
        }
    }

    @Override
    public OffsetDateTime nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner)
            throws SQLException {
        if (resultSet == null) {
            return null;
        }
        Timestamp timestamp = (Timestamp) StandardBasicTypes.TIMESTAMP.nullSafeGet(resultSet, names[0], session, owner);
        if (timestamp == null) {
            throw new IllegalStateException("The timestamp (" + timestamp + ") for an "
                    + OffsetDateTime.class.getSimpleName() + "cannot be null.");
        }
        LocalDateTime localDateTime = timestamp.toLocalDateTime();
        Integer zoneOffsetSeconds = (Integer) StandardBasicTypes.INTEGER.nullSafeGet(resultSet, names[1], session, owner);
        if (zoneOffsetSeconds == null) {
            throw new IllegalStateException("The zoneOffsetSeconds (" + zoneOffsetSeconds + ") for an "
                    + OffsetDateTime.class.getSimpleName() + "cannot be null.");
        }
        return OffsetDateTime.of(localDateTime, ZoneOffset.ofTotalSeconds(zoneOffsetSeconds));
    }

    @Override
    public void nullSafeSet(PreparedStatement statement, Object value, int parameterIndex, SessionImplementor session)
            throws SQLException {
        if (value == null) {
            statement.setNull(parameterIndex, StandardBasicTypes.TIMESTAMP.sqlType());
            statement.setNull(parameterIndex, StandardBasicTypes.INTEGER.sqlType());
            return;
        }
        OffsetDateTime offsetDateTime = (OffsetDateTime) value;
        statement.setTimestamp(parameterIndex, Timestamp.valueOf(offsetDateTime.toLocalDateTime()));
        statement.setInt(parameterIndex, offsetDateTime.getOffset().getTotalSeconds());
    }

    // ************************************************************************
    // Mutable related methods
    // ************************************************************************

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public Object deepCopy(Object value) {
        return value; // OffsetDateTime is immutable
    }

    @Override
    public Object replace(Object original, Object target, SessionImplementor session, Object owner) {
        return original; // OffsetDateTime is immutable
    }

    @Override
    public void setPropertyValue(Object component, int property, Object value) {
        throw new UnsupportedOperationException("A OffsetDateTime is immutable.");
    }

    // ************************************************************************
    // Other methods
    // ************************************************************************

    @Override
    public boolean equals(Object a, Object b) {
        if (a == b) {
            return true;
        } else if (a == null || b == null) {
            return false;
        }
        return a.equals(b);
    }

    @Override
    public int hashCode(Object o) {
        if (o == null) {
            return 0;
        }
        return o.hashCode();
    }

    @Override
    public Serializable disassemble(Object value, SessionImplementor session) {
        return (Serializable) value;
    }

    @Override
    public Object assemble(Serializable cached, SessionImplementor session, Object owner) {
        return cached;
    }

}

现在,我想比较一下,所以这个JPA-QL查询可以工作:

       @NamedQuery(name = "Shift.myQuery",
                   query = "select sa from Shift sa" +
                           " where sa.endDateTime >= :startDateTime" +
                           " and sa.startDateTime < :endDateTime")

在这个模型上:

@Entity
public class Shift {

    @Type(type = "...OffsetDateTimeHibernateType")
    @Columns(columns = {@Column(name = "startDateTime"), @Column(name="startDateTimeOffset")})
    private OffsetDateTime startDateTime;
    @Type(type = "...OffsetDateTimeHibernateType")
    @Columns(columns = {@Column(name = "endDateTime"), @Column(name="endDateTimeOffset")})
    private OffsetDateTime endDateTime;

    ...

}

但这在以下方面失败了:

HHH000177: Error in named query: Shift.myQuery: org.hibernate.hql.internal.ast.QuerySyntaxException: >= operator not supported on composite types. [select sa from org.optaplanner.openshift.employeerostering.shared.shift.Shift sa where sa.endDateTime >= :startDateTime and sa.startDateTime < :endDateTime]
    at org.hibernate.hql.internal.ast.QuerySyntaxException.generateQueryException(QuerySyntaxException.java:79)
    at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:103)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:218)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:142)
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:115)
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:76)
    at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:150)
    at org.hibernate.internal.NamedQueryRepository.checkNamedQueries(NamedQueryRepository.java:155)
    at org.hibernate.internal.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:796)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:492)
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:422)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:880)

如何使CustomUserType具有可比性?

共有1个答案

傅新
2023-03-14

Hibernate无法知道如何将自定义类型与多个列进行比较。您知道列之间的关系,但Hibernate不知道。在没有进行测试的情况下(如果我稍后有时间,可以这样做),我认为您可以重写查询以使用部件的属性名称,例如:

select sa from Shift sa
  where sa.endDateTime.dateTime >= :startDateTimeDateTimePart
  and sa.startDateTime.dateTime < :endDateTimeDateTimePart

要使其与偏移量一起工作,您需要规范化您比较的值,即将偏移量表示的小时数添加到日期。您可以使用数据库的自定义函数来做到这一点,请参阅JPA 2.2(https://jcp.org/aboutJava/communityprocess/mrel/jsr338/index.html)中的4.6.17.3。当然,您也可以在数据库中定义一个自定义比较函数,它将两个部分都作为输入参数并使用该函数调用它,但就我个人而言,我会尽可能地坚持预定义的函数。无论您使用什么数据库,都应该涵盖向时间戳添加小时。

 类似资料:
  • 问题内容: 在Hibernate 3中,有没有办法等效于以下HQL中的MySQL限制? 如果可能,我不想使用setMaxResults。这肯定在较旧的Hibernate / HQL版本中是可行的,但似乎已经消失了。 问题答案: 几年前,当有人问到为什么它在Hibernate 2中有效但在Hibernate 3中无效时,此消息发布在Hibernate论坛上: 在HQL中, 从不 支持Limit 子句

  • 有些事情我想应该很容易,但我在网上搜索了很长时间后找不到答案: 我只是想使用graphql进行一次变异,然后再使用另一个graphql查询刷新数据,如何在一个HTTP(graphql)请求中实现这一点? 失败的尝试: 我尝试使用批处理发送包装在一个数组中的两个graphql模式,但它是异步的,查询会在不需要的变异之前返回 我试着用突变编写相同的查询,实际上什么都不突变,并把它放在真正的突变之后,这

  • 问题内容: 在Hibernate 3中,有没有办法等效于HQL中的以下MySQL限制? 如果可能的话,我不想使用setMaxResults。这肯定在较旧的Hibernate / HQL版本中是可行的,但似乎已经消失了。 问题答案: 几年前,当有人问到为什么它在Hibernate 2中有效但在Hibernate 3中无效时,此消息发布在Hibernate论坛上: 在HQL中,从不支持Limit 子句

  • 问题内容: 我对HQL查询和hibernate有疑问。 我有一个用户类和一个角色类。用户可以具有许多角色。所以我有一个像这样的ManyToMany关系: 在用户类别中: 在角色类中: 此映射创建了存储关系的第三个表(PORTAIL_USERROLE)。像这样一切正常。当我有一个用户时,我将检索角色。 但是,我的问题是:在HQL查询中,如何获得具有特定角色的所有用户?任何类都代表PORTAIL_US

  • 我在创建使用嵌套查询或使用联接更新实体CommitteeMembership的HQL时遇到问题,我首先尝试了以下查询: 但生成的SQL错误如下: 在“交叉连接”之后没有任何this使Hibernate抛出SQLGrammarException 之后,我将查询更改为使用子查询: 现在Hibernate投球 任何人都知道我如何在HQL中编写此更新查询??

  • 问题内容: 我在优化Hibernate查询以免执行联接或辅助选择时遇到了麻烦。 执行Hibernate查询(条件或hql)时,例如: …,而where子句检查不需要与其他表进行任何联接的属性…,但是Hibernate仍与其他表进行完全联接(或辅助选择,具体取决于我如何设置fetchMode)。 有问题的对象(GiftCard)有几个ManyToOne关联,在这种情况下(但不一定是所有情况),我希望