Hibernate CriteriaBuilder时间类型比较报错排查求助
问题分析与解决方案
你的问题根源很明确:Hibernate的criteriaBuilder.currentTime()生成的是SQL DATETIME类型的表达式,但你的数据库字段是TIME类型,两者在MSSQL中无法直接用比较运算符(>=、<=等)进行比较,所以抛出了类型不兼容的错误。
你添加的sendTimeAsDateTime=false参数其实作用是告诉JDBC驱动将Java的java.sql.Time对象以SQL TIME类型发送给数据库,但它无法改变Hibernate生成的SQL表达式中currentTime()对应的类型——这个方法默认生成的是包含日期和时间的DATETIME类型,所以这个参数对你的问题没有帮助。
下面给你两种可行的解决方案:
方案一:在Criteria查询中转换当前时间的SQL类型
直接在查询里用MSSQL的CONVERT函数,把currentTime()生成的DATETIME转成TIME类型,确保比较两边类型一致:
@Override public Long countAll(boolean isAvailable) { CriteriaQuery<Long> query = criteriaBuilder.createQuery(Long.class); Root<ParkingLotEntity> root = query.from(ParkingLotEntity.class); // 使用MSSQL的CONVERT函数将当前时间转为TIME类型 Expression<Time> currentTime = criteriaBuilder.function( "CONVERT", Time.class, criteriaBuilder.literal("time"), criteriaBuilder.currentTime() ); Predicate predicate; if (isAvailable) { predicate = criteriaBuilder.and( criteriaBuilder.equal(root.get(ParkingLotEntity_.isAvailable), true), criteriaBuilder.greaterThanOrEqualTo(root.get(ParkingLotEntity_.openingHour), currentTime), criteriaBuilder.lessThanOrEqualTo(root.get(ParkingLotEntity_.closingHour), currentTime) ); } else { predicate = criteriaBuilder.or( criteriaBuilder.equal(root.get(ParkingLotEntity_.isAvailable), false), criteriaBuilder.lessThan(root.get(ParkingLotEntity_.openingHour), currentTime), criteriaBuilder.greaterThan(root.get(ParkingLotEntity_.closingHour), currentTime) ); } return entityManager.createQuery(query .select(criteriaBuilder.count(root)) .where(predicate)) .getSingleResult(); }
方案二:在Java端生成TIME类型的当前时间参数
另一种更直观的方式是在Java代码中获取当前时间的时间部分,转换为java.sql.Time对象,然后作为参数传入查询,这样Hibernate会生成参数化查询,自动匹配数据库的TIME类型:
@Override public Long countAll(boolean isAvailable) { CriteriaQuery<Long> query = criteriaBuilder.createQuery(Long.class); Root<ParkingLotEntity> root = query.from(ParkingLotEntity.class); // 提取当前时间的时分秒部分,转为java.sql.Time Time currentSqlTime = Time.valueOf(LocalTime.now()); Predicate predicate; if (isAvailable) { predicate = criteriaBuilder.and( criteriaBuilder.equal(root.get(ParkingLotEntity_.isAvailable), true), criteriaBuilder.greaterThanOrEqualTo(root.get(ParkingLotEntity_.openingHour), currentSqlTime), criteriaBuilder.lessThanOrEqualTo(root.get(ParkingLotEntity_.closingHour), currentSqlTime) ); } else { predicate = criteriaBuilder.or( criteriaBuilder.equal(root.get(ParkingLotEntity_.isAvailable), false), criteriaBuilder.lessThan(root.get(ParkingLotEntity_.openingHour), currentSqlTime), criteriaBuilder.greaterThan(root.get(ParkingLotEntity_.closingHour), currentSqlTime) ); } return entityManager.createQuery(query .select(criteriaBuilder.count(root)) .where(predicate)) .getSingleResult(); }
额外优化建议
为了避免Hibernate映射时的类型歧义,你可以在实体字段上明确指定数据库列类型,确保映射完全匹配:
@Column(name = "[OPENING_HOUR]", nullable = false, columnDefinition = "time") private Time openingHour; @Column(name = "[CLOSING_HOUR]", nullable = false, columnDefinition = "time") private Time closingHour;
这样可以确保Hibernate不会将TIME字段误映射为其他类型,减少隐式转换带来的问题。
内容的提问来源于stack exchange,提问作者Huỳnh Thanh Bình
相关产品推荐
相关产品推荐

