HHH-17109 date/time arithmetic in criteria queries

This commit is contained in:
Gavin King 2023-08-20 16:20:35 +02:00
parent bdd78fe39c
commit 2e5c6fe3a1
5 changed files with 475 additions and 18 deletions

View File

@ -11,7 +11,9 @@ import java.math.BigInteger;
import java.sql.Date; import java.sql.Date;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -386,7 +388,6 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
@Override @Override
<N extends Number> JpaExpression<N> abs(Expression<N> x); <N extends Number> JpaExpression<N> abs(Expression<N> x);
@Override @Override
<N extends Number> JpaExpression<N> sum(Expression<? extends N> x, Expression<? extends N> y); <N extends Number> JpaExpression<N> sum(Expression<? extends N> x, Expression<? extends N> y);
@ -435,6 +436,127 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
@Override @Override
JpaExpression<Double> sqrt(Expression<? extends Number> x); JpaExpression<Double> sqrt(Expression<? extends Number> x);
/**
* Add two {@linkplain Duration durations}.
* @since 6.3
*/
JpaExpression<Duration> durationSum(Expression<Duration> x, Expression<Duration> y);
/**
* Add two {@linkplain Duration durations}.
* @since 6.3
*/
JpaExpression<Duration> durationSum(Expression<Duration> x, Duration y);
/**
* Subtract one {@linkplain Duration duration} from another.
* @since 6.3
*/
JpaExpression<Duration> durationDiff(Expression<Duration> x, Expression<Duration> y);
/**
* Subtract one {@linkplain Duration duration} from another.
* @since 6.3
*/
JpaExpression<Duration> durationDiff(Expression<Duration> x, Duration y);
/**
* Scale a {@linkplain Duration duration} by a number.
* @since 6.3
*/
JpaExpression<Duration> durationScaled(Expression<? extends Number> number, Expression<Duration> duration);
/**
* Scale a {@linkplain Duration duration} by a number.
* @since 6.3
*/
JpaExpression<Duration> durationScaled(Number number, Expression<Duration> duration);
/**
* Scale a {@linkplain Duration duration} by a number.
* @since 6.3
*/
JpaExpression<Duration> durationScaled(Expression<? extends Number> number, Duration duration);
/**
* A literal {@link Duration}, for example, "five days" or "30 minutes".
* @since 6.3
*/
@Incubating // layer breaker (leaks SQM type)
JpaExpression<Duration> duration(long magnitude, TemporalUnit unit);
/**
* Convert a {@link Duration} to a numeric magnitude in the given units.
* @param unit a choice of temporal granularity
* @param duration the duration in a "unit-free" form
* @return the magnitude of the duration measured in the given units
* @since 6.3
*/
@Incubating // layer breaker (leaks SQM type)
JpaExpression<Long> durationByUnit(TemporalUnit unit, Expression<Duration> duration);
/**
* Subtract two dates or two datetimes, returning the duration between the
* two dates or between two datetimes.
* @since 6.3
*/
<T extends Temporal> JpaExpression<Duration> durationBetween(Expression<T> x, Expression<T> y);
/**
* Subtract two dates or two datetimes, returning the duration between the
* two dates or between two datetimes.
* @since 6.3
*/
<T extends Temporal> JpaExpression<Duration> durationBetween(Expression<T> x, T y);
/**
* Add a duration to a date or datetime, that is, return a later date or
* datetime which is separated from the given date or datetime by the given
* duration.
* @since 6.3
*/
<T extends Temporal> JpaExpression<T> addDuration(Expression<T> datetime, Expression<Duration> duration);
/**
* Add a duration to a date or datetime, that is, return a later date or
* datetime which is separated from the given date or datetime by the given
* duration.
* @since 6.3
*/
<T extends Temporal> JpaExpression<T> addDuration(Expression<T> datetime, Duration duration);
/**
* Add a duration to a date or datetime, that is, return a later date or
* datetime which is separated from the given date or datetime by the given
* duration.
* @since 6.3
*/
<T extends Temporal> JpaExpression<T> addDuration(T datetime, Expression<Duration> duration);
/**
* Subtract a duration to a date or datetime, that is, return an earlier date
* or datetime which is separated from the given date or datetime by the given
* duration.
* @since 6.3
*/
<T extends Temporal> JpaExpression<T> subtractDuration(Expression<T> datetime, Expression<Duration> duration);
/**
* Subtract a duration to a date or datetime, that is, return an earlier date
* or datetime which is separated from the given date or datetime by the given
* duration.
* @since 6.3
*/
<T extends Temporal> JpaExpression<T> subtractDuration(Expression<T> datetime, Duration duration);
/**
* Subtract a duration to a date or datetime, that is, return an earlier date
* or datetime which is separated from the given date or datetime by the given
* duration.
* @since 6.3
*/
<T extends Temporal> JpaExpression<T> subtractDuration(T datetime, Expression<Duration> duration);
@Override @Override
JpaExpression<Long> toLong(Expression<? extends Number> number); JpaExpression<Long> toLong(Expression<? extends Number> number);

View File

@ -11,10 +11,12 @@ import java.math.BigInteger;
import java.sql.Date; import java.sql.Date;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -2039,4 +2041,89 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
Expression<?>... arguments) { Expression<?>... arguments) {
return criteriaBuilder.percentRank( order, filter, window, arguments ); return criteriaBuilder.percentRank( order, filter, window, arguments );
} }
@Override
public JpaExpression<Duration> durationSum(Expression<Duration> x, Expression<Duration> y) {
return criteriaBuilder.durationSum( x, y );
}
@Override
public JpaExpression<Duration> durationSum(Expression<Duration> x, Duration y) {
return criteriaBuilder.durationSum( x, y );
}
@Override
public JpaExpression<Duration> durationDiff(Expression<Duration> x, Expression<Duration> y) {
return criteriaBuilder.durationDiff( x, y );
}
@Override
public JpaExpression<Duration> durationDiff(Expression<Duration> x, Duration y) {
return criteriaBuilder.durationDiff( x, y );
}
@Override
public JpaExpression<Duration> durationScaled(Expression<? extends Number> number, Expression<Duration> duration) {
return criteriaBuilder.durationScaled( number, duration );
}
@Override
public JpaExpression<Duration> durationScaled(Number number, Expression<Duration> duration) {
return criteriaBuilder.durationScaled( number, duration );
}
@Override
public JpaExpression<Duration> durationScaled(Expression<? extends Number> number, Duration duration) {
return criteriaBuilder.durationScaled( number, duration );
}
@Override
public <T extends Temporal> JpaExpression<Duration> durationBetween(Expression<T> x, Expression<T> y) {
return criteriaBuilder.durationBetween( x, y );
}
@Override
public <T extends Temporal> JpaExpression<Duration> durationBetween(Expression<T> x, T y) {
return criteriaBuilder.durationBetween( x, y );
}
@Override
public <T extends Temporal> JpaExpression<T> addDuration(Expression<T> datetime, Expression<Duration> duration) {
return criteriaBuilder.addDuration( datetime, duration );
}
@Override
public <T extends Temporal> JpaExpression<T> addDuration(Expression<T> datetime, Duration duration) {
return criteriaBuilder.addDuration( datetime, duration );
}
@Override
public <T extends Temporal> JpaExpression<T> addDuration(T datetime, Expression<Duration> duration) {
return criteriaBuilder.addDuration(datetime, duration);
}
@Override
public <T extends Temporal> JpaExpression<T> subtractDuration(Expression<T> datetime, Expression<Duration> duration) {
return criteriaBuilder.subtractDuration( datetime, duration );
}
@Override
public <T extends Temporal> JpaExpression<T> subtractDuration(Expression<T> datetime, Duration duration) {
return criteriaBuilder.subtractDuration( datetime, duration );
}
@Override
public <T extends Temporal> JpaExpression<T> subtractDuration(T datetime, Expression<Duration> duration) {
return criteriaBuilder.subtractDuration(datetime, duration);
}
@Override
public JpaExpression<Long> durationByUnit(TemporalUnit unit, Expression<Duration> duration) {
return criteriaBuilder.durationByUnit(unit, duration);
}
@Override
public JpaExpression<Duration> duration(long magnitude, TemporalUnit unit) {
return criteriaBuilder.duration( magnitude, unit );
}
} }

View File

@ -649,6 +649,8 @@ public interface NodeBuilder extends HibernateCriteriaBuilder {
BasicType<Integer> getIntegerType(); BasicType<Integer> getIntegerType();
BasicType<Long> getLongType();
BasicType<Character> getCharacterType(); BasicType<Character> getCharacterType();
SessionFactoryImplementor getSessionFactory(); SessionFactoryImplementor getSessionFactory();

View File

@ -13,10 +13,12 @@ import java.math.BigInteger;
import java.sql.Date; import java.sql.Date;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -97,6 +99,7 @@ import org.hibernate.query.sqm.tree.domain.SqmSetJoin;
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin; import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic; import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmByUnit;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
import org.hibernate.query.sqm.tree.expression.SqmCaseSimple; import org.hibernate.query.sqm.tree.expression.SqmCaseSimple;
import org.hibernate.query.sqm.tree.expression.SqmCastTarget; import org.hibernate.query.sqm.tree.expression.SqmCastTarget;
@ -104,6 +107,7 @@ import org.hibernate.query.sqm.tree.expression.SqmCoalesce;
import org.hibernate.query.sqm.tree.expression.SqmCollation; import org.hibernate.query.sqm.tree.expression.SqmCollation;
import org.hibernate.query.sqm.tree.expression.SqmCollectionSize; import org.hibernate.query.sqm.tree.expression.SqmCollectionSize;
import org.hibernate.query.sqm.tree.expression.SqmDistinct; import org.hibernate.query.sqm.tree.expression.SqmDistinct;
import org.hibernate.query.sqm.tree.expression.SqmDurationUnit;
import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmExtractUnit; import org.hibernate.query.sqm.tree.expression.SqmExtractUnit;
import org.hibernate.query.sqm.tree.expression.SqmFormat; import org.hibernate.query.sqm.tree.expression.SqmFormat;
@ -112,6 +116,7 @@ import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralNull; import org.hibernate.query.sqm.tree.expression.SqmLiteralNull;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression; import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmOver; import org.hibernate.query.sqm.tree.expression.SqmOver;
import org.hibernate.query.sqm.tree.expression.SqmToDuration;
import org.hibernate.query.sqm.tree.expression.SqmTrimSpecification; import org.hibernate.query.sqm.tree.expression.SqmTrimSpecification;
import org.hibernate.query.sqm.tree.expression.SqmTuple; import org.hibernate.query.sqm.tree.expression.SqmTuple;
import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation; import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation;
@ -191,6 +196,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
private final transient ValueHandlingMode criteriaValueHandlingMode; private final transient ValueHandlingMode criteriaValueHandlingMode;
private transient BasicType<Boolean> booleanType; private transient BasicType<Boolean> booleanType;
private transient BasicType<Integer> integerType; private transient BasicType<Integer> integerType;
private transient BasicType<Long> longType;
private transient BasicType<Character> characterType; private transient BasicType<Character> characterType;
private final transient Map<Class<? extends HibernateCriteriaBuilder>, HibernateCriteriaBuilder> extensions; private final transient Map<Class<? extends HibernateCriteriaBuilder>, HibernateCriteriaBuilder> extensions;
@ -258,6 +264,17 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return integerType; return integerType;
} }
@Override
public BasicType<Long> getLongType() {
final BasicType<Long> longType = this.longType;
if ( longType == null ) {
return this.longType =
getTypeConfiguration().getBasicTypeRegistry()
.resolve( StandardBasicTypes.LONG );
}
return longType;
}
@Override @Override
public BasicType<Character> getCharacterType() { public BasicType<Character> getCharacterType() {
final BasicType<Character> characterType = this.characterType; final BasicType<Character> characterType = this.characterType;
@ -921,11 +938,122 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
} }
@Override @Override
public <N extends Number> SqmExpression<N> sum(Expression<? extends N> x, Expression<? extends N> y) { public JpaExpression<Duration> duration(long magnitude, TemporalUnit unit) {
return createSqmArithmeticNode( BinaryArithmeticOperator.ADD, (SqmExpression<?>) x, (SqmExpression<?>) y ); return new SqmToDuration<>(
literal( magnitude ),
new SqmDurationUnit<>( unit, getLongType(), this ),
getTypeConfiguration().standardBasicTypeForJavaType( Duration.class ),
this
);
} }
private <N extends Number> SqmExpression<N> createSqmArithmeticNode( @Override
public JpaExpression<Long> durationByUnit(TemporalUnit unit, Expression<Duration> duration) {
return new SqmByUnit(
new SqmDurationUnit<>( unit, getLongType(), this ),
(SqmExpression<Duration>) duration,
getLongType(),
this
);
}
@Override
public JpaExpression<Duration> durationSum(Expression<Duration> x, Expression<Duration> y) {
return createSqmArithmeticNode( BinaryArithmeticOperator.ADD,
(SqmExpression<Duration>) x, (SqmExpression<Duration>) y );
}
@Override
public JpaExpression<Duration> durationSum(Expression<Duration> x, Duration y) {
return createSqmArithmeticNode( BinaryArithmeticOperator.ADD,
(SqmExpression<Duration>) x, value( y ) );
}
@Override
public JpaExpression<Duration> durationDiff(Expression<Duration> x, Expression<Duration> y) {
return createSqmArithmeticNode( BinaryArithmeticOperator.SUBTRACT,
(SqmExpression<Duration>) x, (SqmExpression<Duration>) y );
}
@Override
public JpaExpression<Duration> durationDiff(Expression<Duration> x, Duration y) {
return createSqmArithmeticNode( BinaryArithmeticOperator.SUBTRACT,
(SqmExpression<Duration>) x, value( y ) );
}
@Override
public JpaExpression<Duration> durationScaled(Expression<? extends Number> number, Expression<Duration> duration) {
return createSqmArithmeticNode( BinaryArithmeticOperator.MULTIPLY,
(SqmExpression<? extends Number>) number, (SqmExpression<Duration>) duration );
}
@Override
public JpaExpression<Duration> durationScaled(Number number, Expression<Duration> duration) {
return createSqmArithmeticNode( BinaryArithmeticOperator.MULTIPLY,
value( number ), (SqmExpression<Duration>) duration );
}
@Override
public JpaExpression<Duration> durationScaled(Expression<? extends Number> number, Duration duration) {
return createSqmArithmeticNode( BinaryArithmeticOperator.MULTIPLY,
(SqmExpression<? extends Number>) number, value( duration ) );
}
@Override
public <T extends Temporal> JpaExpression<Duration> durationBetween(Expression<T> x, Expression<T> y) {
return createSqmArithmeticNode( BinaryArithmeticOperator.SUBTRACT,
(SqmExpression<T>) x, (SqmExpression<T>) y );
}
@Override
public <T extends Temporal> JpaExpression<Duration> durationBetween(Expression<T> x, T y) {
return createSqmArithmeticNode( BinaryArithmeticOperator.SUBTRACT,
(SqmExpression<T>) x, value( y ) );
}
@Override
public <T extends Temporal> JpaExpression<T> addDuration(Expression<T> datetime, Expression<Duration> duration) {
return createSqmArithmeticNode( BinaryArithmeticOperator.ADD,
(SqmExpression<T>) datetime, (SqmExpression<Duration>) duration );
}
@Override
public <T extends Temporal> JpaExpression<T> addDuration(Expression<T> datetime, Duration duration) {
return createSqmArithmeticNode( BinaryArithmeticOperator.ADD,
(SqmExpression<T>) datetime, value( duration ) );
}
@Override
public <T extends Temporal> JpaExpression<T> addDuration(T datetime, Expression<Duration> duration) {
return createSqmArithmeticNode( BinaryArithmeticOperator.ADD,
value( datetime ), (SqmExpression<Duration>) duration );
}
@Override
public <T extends Temporal> JpaExpression<T> subtractDuration(Expression<T> datetime, Expression<Duration> duration) {
return createSqmArithmeticNode( BinaryArithmeticOperator.SUBTRACT,
(SqmExpression<T>) datetime, (SqmExpression<Duration>) duration );
}
@Override
public <T extends Temporal> JpaExpression<T> subtractDuration(Expression<T> datetime, Duration duration) {
return createSqmArithmeticNode( BinaryArithmeticOperator.SUBTRACT,
(SqmExpression<T>) datetime, value( duration ) );
}
@Override
public <T extends Temporal> JpaExpression<T> subtractDuration(T datetime, Expression<Duration> duration) {
return createSqmArithmeticNode( BinaryArithmeticOperator.SUBTRACT,
value( datetime ), (SqmExpression<Duration>) duration );
}
@Override
public <N extends Number> SqmExpression<N> sum(Expression<? extends N> x, Expression<? extends N> y) {
return createSqmArithmeticNode( BinaryArithmeticOperator.ADD,
(SqmExpression<? extends N>) x, (SqmExpression<? extends N>) y );
}
private <N> SqmExpression<N> createSqmArithmeticNode(
BinaryArithmeticOperator operator, BinaryArithmeticOperator operator,
SqmExpression<?> leftHandExpression, SqmExpression<?> leftHandExpression,
SqmExpression<?> rightHandExpression) { SqmExpression<?> rightHandExpression) {
@ -947,7 +1075,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public <N extends Number> SqmExpression<N> sum(Expression<? extends N> x, N y) { public <N extends Number> SqmExpression<N> sum(Expression<? extends N> x, N y) {
return createSqmArithmeticNode( return createSqmArithmeticNode(
BinaryArithmeticOperator.ADD, BinaryArithmeticOperator.ADD,
(SqmExpression<?>) x, (SqmExpression<? extends N>) x,
value( y ) value( y )
); );
} }
@ -957,7 +1085,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return createSqmArithmeticNode( return createSqmArithmeticNode(
BinaryArithmeticOperator.ADD, BinaryArithmeticOperator.ADD,
value( x ), value( x ),
(SqmExpression<?>) y (SqmExpression<? extends N>) y
); );
} }
@ -965,8 +1093,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public <N extends Number> SqmExpression<N> prod(Expression<? extends N> x, Expression<? extends N> y) { public <N extends Number> SqmExpression<N> prod(Expression<? extends N> x, Expression<? extends N> y) {
return createSqmArithmeticNode( return createSqmArithmeticNode(
BinaryArithmeticOperator.MULTIPLY, BinaryArithmeticOperator.MULTIPLY,
(SqmExpression<?>) x, (SqmExpression<? extends N>) x,
(SqmExpression<?>) y (SqmExpression<? extends N>) y
); );
} }
@ -974,7 +1102,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public <N extends Number> SqmExpression<N> prod(Expression<? extends N> x, N y) { public <N extends Number> SqmExpression<N> prod(Expression<? extends N> x, N y) {
return createSqmArithmeticNode( return createSqmArithmeticNode(
BinaryArithmeticOperator.MULTIPLY, BinaryArithmeticOperator.MULTIPLY,
(SqmExpression<?>) x, (SqmExpression<? extends N>) x,
value( y ) value( y )
); );
} }
@ -984,7 +1112,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return createSqmArithmeticNode( return createSqmArithmeticNode(
BinaryArithmeticOperator.MULTIPLY, BinaryArithmeticOperator.MULTIPLY,
value( x ), value( x ),
(SqmExpression<?>) y (SqmExpression<? extends N>) y
); );
} }
@ -992,8 +1120,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public <N extends Number> SqmExpression<N> diff(Expression<? extends N> x, Expression<? extends N> y) { public <N extends Number> SqmExpression<N> diff(Expression<? extends N> x, Expression<? extends N> y) {
return createSqmArithmeticNode( return createSqmArithmeticNode(
BinaryArithmeticOperator.SUBTRACT, BinaryArithmeticOperator.SUBTRACT,
(SqmExpression<?>) x, (SqmExpression<? extends N>) x,
(SqmExpression<?>) y (SqmExpression<? extends N>) y
); );
} }
@ -1001,7 +1129,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public <N extends Number> SqmExpression<N> diff(Expression<? extends N> x, N y) { public <N extends Number> SqmExpression<N> diff(Expression<? extends N> x, N y) {
return createSqmArithmeticNode( return createSqmArithmeticNode(
BinaryArithmeticOperator.SUBTRACT, BinaryArithmeticOperator.SUBTRACT,
(SqmExpression<?>) x, (SqmExpression<? extends N>) x,
value( y ) value( y )
); );
} }
@ -1011,7 +1139,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return createSqmArithmeticNode( return createSqmArithmeticNode(
BinaryArithmeticOperator.SUBTRACT, BinaryArithmeticOperator.SUBTRACT,
value( x ), value( x ),
(SqmExpression<?>) y (SqmExpression<? extends N>) y
); );
} }
@ -1019,8 +1147,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public SqmExpression<Number> quot(Expression<? extends Number> x, Expression<? extends Number> y) { public SqmExpression<Number> quot(Expression<? extends Number> x, Expression<? extends Number> y) {
return createSqmArithmeticNode( return createSqmArithmeticNode(
BinaryArithmeticOperator.QUOT, BinaryArithmeticOperator.QUOT,
(SqmExpression<?>) x, (SqmExpression<? extends Number>) x,
(SqmExpression<?>) y (SqmExpression<? extends Number>) y
); );
} }
@ -1028,7 +1156,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public SqmExpression<Number> quot(Expression<? extends Number> x, Number y) { public SqmExpression<Number> quot(Expression<? extends Number> x, Number y) {
return createSqmArithmeticNode( return createSqmArithmeticNode(
BinaryArithmeticOperator.QUOT, BinaryArithmeticOperator.QUOT,
(SqmExpression<?>) x, (SqmExpression<? extends Number>) x,
value( y ) value( y )
); );
} }
@ -1650,7 +1778,17 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override @Override
public <T> SqmExpression<T> value(T value) { public <T> SqmExpression<T> value(T value) {
return inlineValue( value ) ? literal( value ) : valueParameter( value ); if ( value instanceof Duration ) {
final Duration duration = (Duration) value;
final JpaExpression<Duration> expression = duration.getNano() == 0
? duration( duration.getSeconds(), TemporalUnit.SECOND )
: duration( duration.getNano() + duration.getSeconds() * 1_000_000_000, TemporalUnit.NANOSECOND );
//noinspection unchecked
return (SqmExpression<T>) expression;
}
else {
return inlineValue( value ) ? literal( value ) : valueParameter( value );
}
} }
private <T> boolean isInstance(BindableType<T> bindableType, T value) { private <T> boolean isInstance(BindableType<T> bindableType, T value) {

View File

@ -8,17 +8,24 @@ package org.hibernate.orm.test.jpa.criteria.basic;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.PostgresPlusDialect;
import org.hibernate.dialect.SybaseDialect;
import org.hibernate.orm.test.jpa.metamodel.AbstractMetamodelSpecificTest; import org.hibernate.orm.test.jpa.metamodel.AbstractMetamodelSpecificTest;
import org.hibernate.orm.test.jpa.metamodel.Phone; import org.hibernate.orm.test.jpa.metamodel.Phone;
import org.hibernate.orm.test.jpa.metamodel.Product; import org.hibernate.orm.test.jpa.metamodel.Product;
import org.hibernate.orm.test.jpa.metamodel.Product_; import org.hibernate.orm.test.jpa.metamodel.Product_;
import org.hibernate.query.Query; import org.hibernate.query.Query;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.sqm.TemporalUnit;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.SkipForDialect; import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@ -252,6 +259,107 @@ public class ExpressionsTest extends AbstractMetamodelSpecificTest {
); );
} }
@Test @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "numeric overflows")
@SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "does not support extract(epoch)")
public void testDateTimeOperations() {
HibernateCriteriaBuilder builder = (HibernateCriteriaBuilder) this.builder;
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<LocalDate> criteria = builder.createQuery(LocalDate.class);
criteria.select( builder.addDuration( builder.localDate(),
builder.duration(2, TemporalUnit.YEAR) ) );
entityManager.createQuery(criteria).getSingleResult();
}
);
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<LocalDate> criteria = builder.createQuery(LocalDate.class);
criteria.select( builder.addDuration(
// had to call literal() here because parameter-based binding caused error from
// database since it couldn't tell what sort of dateadd() function was being called
builder.literal( LocalDate.of(2000,1, 1) ),
builder.duration(2, TemporalUnit.YEAR) ) );
assertEquals( LocalDate.of(2002,1, 1),
entityManager.createQuery(criteria).getSingleResult() );
}
);
//SQL Server and others don't like this
// doInJPA(
// this::entityManagerFactory,
// entityManager -> {
// CriteriaQuery<LocalDate> criteria = builder.createQuery(LocalDate.class);
// criteria.select( builder.after( builder.localDate(), Duration.ofDays(2*365) ) );
// entityManager.createQuery(criteria).getSingleResult();
// }
// );
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<LocalDateTime> criteria = builder.createQuery(LocalDateTime.class);
criteria.select( builder.subtractDuration( builder.localDateTime(), Duration.ofMinutes(30) ) );
entityManager.createQuery(criteria).getSingleResult();
}
);
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Duration> criteria = builder.createQuery(Duration.class);
criteria.select( builder.durationScaled( 5, builder.duration(2, TemporalUnit.HOUR ) ) );
assertEquals( Duration.ofHours(10), entityManager.createQuery(criteria).getSingleResult() );
}
);
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Duration> criteria = builder.createQuery(Duration.class);
criteria.select( builder.durationSum( builder.duration(30, TemporalUnit.MINUTE ),
builder.duration(2, TemporalUnit.HOUR) ) );
assertEquals( Duration.ofMinutes(150), entityManager.createQuery(criteria).getSingleResult() );
}
);
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Long> criteria = builder.createQuery(Long.class);
criteria.select( builder.durationByUnit( TemporalUnit.SECOND,
builder.durationSum( builder.duration(30, TemporalUnit.MINUTE),
builder.duration(2, TemporalUnit.HOUR) ) ) );
assertEquals( 150*60L, entityManager.createQuery(criteria).getSingleResult() );
}
);
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Duration> criteria = builder.createQuery(Duration.class);
criteria.select( builder.durationBetween( builder.localDate(),
LocalDate.of(2000,1, 1) ) );
entityManager.createQuery(criteria).getSingleResult();
}
);
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Duration> criteria = builder.createQuery(Duration.class);
criteria.select( builder.durationBetween( builder.localDate(),
builder.subtractDuration( builder.localDate(),
builder.duration(2, TemporalUnit.DAY) ) ) );
assertEquals( Duration.ofDays(2), entityManager.createQuery(criteria).getSingleResult() );
}
);
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Duration> criteria = builder.createQuery(Duration.class);
criteria.select( builder.durationBetween( builder.localDateTime(),
builder.subtractDuration( builder.localDateTime(),
builder.duration(20, TemporalUnit.HOUR) ) ) );
assertEquals( Duration.ofHours(20), entityManager.createQuery(criteria).getSingleResult() );
}
);
}
@Test @Test
@SkipForDialect(dialectClass = DerbyDialect.class, reason = "By default, unless some kind of context enables inference," + @SkipForDialect(dialectClass = DerbyDialect.class, reason = "By default, unless some kind of context enables inference," +
"a numeric/decimal parameter has the type DECIMAL(31,31) which might cause an overflow on certain arithmetics." + "a numeric/decimal parameter has the type DECIMAL(31,31) which might cause an overflow on certain arithmetics." +