HHH-17575 - Add a new @FractionalSeconds annotation

This commit is contained in:
Steve Ebersole 2023-12-19 00:31:33 -06:00
parent 53dbc959e1
commit 480072d4d1
11 changed files with 430 additions and 24 deletions

View File

@ -0,0 +1,51 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.hibernate.Incubating;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Indicates that the associated temporal value should be stored with fractional seconds.
* Only valid for values which contain seconds.
*
* @apiNote The presence or absence of this annotation implies different semantics for time
* versus timestamp based values. By default, time values are stored without fractional seconds
* whereas timestamp values are stored with a precision based on the
* {@linkplain org.hibernate.dialect.Dialect#getDefaultTimestampPrecision Dialect default}
*
* @see java.time.Instant
* @see java.time.LocalDateTime
* @see java.time.LocalTime
* @see java.time.OffsetDateTime
* @see java.time.OffsetTime
* @see java.time.ZonedDateTime
* @see java.sql.Time
* @see java.sql.Timestamp
* @see java.util.Calendar
*
* @author Steve Ebersole
*/
@Target({METHOD, FIELD})
@Retention( RUNTIME)
@Incubating
public @interface FractionalSeconds {
/**
* The fractional precision for the associated seconds. Generally this will be one of<ul>
* <li>3 (milliseconds)</li>
* <li>6 (microseconds)</li>
* <li>9 (nanoseconds)</li>
* </ul>
*/
int value();
}

View File

@ -17,6 +17,7 @@ import org.hibernate.annotations.Checks;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.ColumnTransformer;
import org.hibernate.annotations.ColumnTransformers;
import org.hibernate.annotations.FractionalSeconds;
import org.hibernate.annotations.GeneratedColumn;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.common.reflection.XProperty;
@ -77,6 +78,7 @@ public class AnnotatedColumn {
private Long length;
private Integer precision;
private Integer scale;
private Integer temporalPrecision; // technically scale, but most dbs call it precision so...
private Integer arrayLength;
private String logicalColumnName;
private boolean unique;
@ -183,6 +185,10 @@ public class AnnotatedColumn {
this.scale = scale;
}
public void setTemporalPrecision(Integer temporalPrecision) {
this.temporalPrecision = temporalPrecision;
}
public void setLogicalColumnName(String logicalColumnName) {
this.logicalColumnName = logicalColumnName;
}
@ -239,6 +245,7 @@ public class AnnotatedColumn {
length,
precision,
scale,
temporalPrecision,
arrayLength,
nullable,
sqlType,
@ -269,6 +276,7 @@ public class AnnotatedColumn {
Long length,
Integer precision,
Integer scale,
Integer temporalPrecision,
Integer arrayLength,
boolean nullable,
String sqlType,
@ -287,6 +295,9 @@ public class AnnotatedColumn {
mappingColumn.setPrecision( precision );
mappingColumn.setScale( scale );
}
if ( temporalPrecision != null ) {
mappingColumn.setTemporalPrecision( temporalPrecision );
}
mappingColumn.setArrayLength( arrayLength );
mappingColumn.setNullable( nullable );
mappingColumn.setSqlType( sqlType );
@ -488,6 +499,7 @@ public class AnnotatedColumn {
return buildColumnOrFormulaFromAnnotation(
null,
formulaAnn,
null,
// commentAnn,
nullability,
propertyHolder,
@ -498,6 +510,7 @@ public class AnnotatedColumn {
}
public static AnnotatedColumns buildColumnFromNoAnnotation(
FractionalSeconds fractionalSeconds,
// Comment commentAnn,
Nullability nullability,
PropertyHolder propertyHolder,
@ -506,6 +519,7 @@ public class AnnotatedColumn {
MetadataBuildingContext context) {
return buildColumnsFromAnnotations(
null,
fractionalSeconds,
// commentAnn,
nullability,
propertyHolder,
@ -517,6 +531,7 @@ public class AnnotatedColumn {
public static AnnotatedColumns buildColumnFromAnnotation(
jakarta.persistence.Column column,
org.hibernate.annotations.FractionalSeconds fractionalSeconds,
// Comment commentAnn,
Nullability nullability,
PropertyHolder propertyHolder,
@ -526,6 +541,7 @@ public class AnnotatedColumn {
return buildColumnOrFormulaFromAnnotation(
column,
null,
fractionalSeconds,
// commentAnn,
nullability,
propertyHolder,
@ -537,6 +553,7 @@ public class AnnotatedColumn {
public static AnnotatedColumns buildColumnsFromAnnotations(
jakarta.persistence.Column[] columns,
FractionalSeconds fractionalSeconds,
// Comment commentAnn,
Nullability nullability,
PropertyHolder propertyHolder,
@ -546,6 +563,7 @@ public class AnnotatedColumn {
return buildColumnsOrFormulaFromAnnotation(
columns,
null,
fractionalSeconds,
// commentAnn,
nullability,
propertyHolder,
@ -568,6 +586,7 @@ public class AnnotatedColumn {
return buildColumnsOrFormulaFromAnnotation(
columns,
null,
null,
// commentAnn,
nullability,
propertyHolder,
@ -581,6 +600,7 @@ public class AnnotatedColumn {
public static AnnotatedColumns buildColumnOrFormulaFromAnnotation(
jakarta.persistence.Column column,
org.hibernate.annotations.Formula formulaAnn,
org.hibernate.annotations.FractionalSeconds fractionalSeconds,
// Comment commentAnn,
Nullability nullability,
PropertyHolder propertyHolder,
@ -590,6 +610,7 @@ public class AnnotatedColumn {
return buildColumnsOrFormulaFromAnnotation(
column==null ? null : new jakarta.persistence.Column[] { column },
formulaAnn,
fractionalSeconds,
// commentAnn,
nullability,
propertyHolder,
@ -603,6 +624,7 @@ public class AnnotatedColumn {
public static AnnotatedColumns buildColumnsOrFormulaFromAnnotation(
jakarta.persistence.Column[] columns,
org.hibernate.annotations.Formula formulaAnn,
org.hibernate.annotations.FractionalSeconds fractionalSeconds,
// Comment comment,
Nullability nullability,
PropertyHolder propertyHolder,
@ -630,6 +652,7 @@ public class AnnotatedColumn {
final jakarta.persistence.Column[] actualColumns = overrideColumns( columns, propertyHolder, inferredData );
if ( actualColumns == null ) {
return buildImplicitColumn(
fractionalSeconds,
inferredData,
suffixForDefaultColumnName,
secondaryTables,
@ -647,7 +670,8 @@ public class AnnotatedColumn {
suffixForDefaultColumnName,
secondaryTables,
context,
actualColumns
actualColumns,
fractionalSeconds
);
}
}
@ -684,7 +708,8 @@ public class AnnotatedColumn {
String suffixForDefaultColumnName,
Map<String, Join> secondaryTables,
MetadataBuildingContext context,
jakarta.persistence.Column[] actualCols) {
jakarta.persistence.Column[] actualCols,
FractionalSeconds fractionalSeconds) {
final AnnotatedColumns parent = new AnnotatedColumns();
parent.setPropertyHolder( propertyHolder );
parent.setPropertyName( getRelativePath( propertyHolder, inferredData.getPropertyName() ) );
@ -708,6 +733,7 @@ public class AnnotatedColumn {
actualCols.length,
database,
column,
fractionalSeconds,
sqlType,
tableName
);
@ -734,6 +760,7 @@ public class AnnotatedColumn {
int numberOfColumns,
Database database,
jakarta.persistence.Column column,
FractionalSeconds fractionalSeconds,
String sqlType,
String tableName) {
final String columnName = logicalColumnName( inferredData, suffixForDefaultColumnName, database, column );
@ -742,7 +769,12 @@ public class AnnotatedColumn {
annotatedColumn.setImplicit( false );
annotatedColumn.setSqlType( sqlType );
annotatedColumn.setLength( (long) column.length() );
annotatedColumn.setPrecision( column.precision() );
if ( fractionalSeconds != null ) {
annotatedColumn.setTemporalPrecision( fractionalSeconds.value() );
}
else {
annotatedColumn.setPrecision( column.precision() );
}
annotatedColumn.setScale( column.scale() );
annotatedColumn.handleArrayLength( inferredData );
// annotatedColumn.setPropertyHolder( propertyHolder );
@ -881,6 +913,7 @@ public class AnnotatedColumn {
}
private static AnnotatedColumns buildImplicitColumn(
FractionalSeconds fractionalSeconds,
PropertyData inferredData,
String suffixForDefaultColumnName,
Map<String, Join> secondaryTables,
@ -920,6 +953,9 @@ public class AnnotatedColumn {
column.applyCheckConstraint( inferredData, 1 );
column.extractDataFromPropertyData( propertyHolder, inferredData );
column.handleArrayLength( inferredData );
if ( fractionalSeconds != null ) {
column.setTemporalPrecision( fractionalSeconds.value() );
}
column.bind();
return columns;
}

View File

@ -325,6 +325,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
referencedColumn.getLength(),
referencedColumn.getPrecision(),
referencedColumn.getScale(),
referencedColumn.getTemporalPrecision(),
referencedColumn.getArrayLength(),
mappingColumn != null && mappingColumn.isNullable(),
referencedColumn.getSqlType(),
@ -375,6 +376,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
column.getLength(),
column.getPrecision(),
column.getScale(),
column.getTemporalPrecision(),
column.getArrayLength(),
getMappingColumn().isNullable(),
column.getSqlType(),

View File

@ -761,6 +761,7 @@ public class BinderHelper {
final AnnotatedColumns discriminatorColumns = buildColumnOrFormulaFromAnnotation(
discriminatorColumn,
discriminatorFormula,
null,
// null,
nullability,
propertyHolder,

View File

@ -570,6 +570,7 @@ public abstract class CollectionBinder {
if ( property.isAnnotationPresent( jakarta.persistence.Column.class ) ) {
return buildColumnFromAnnotation(
property.getAnnotation( jakarta.persistence.Column.class ),
null,
// comment,
nullability,
propertyHolder,
@ -592,6 +593,7 @@ public abstract class CollectionBinder {
else if ( property.isAnnotationPresent( Columns.class ) ) {
return buildColumnsFromAnnotations(
property.getAnnotation( Columns.class ).columns(),
null,
// comment,
nullability,
propertyHolder,
@ -602,6 +604,7 @@ public abstract class CollectionBinder {
}
else {
return buildColumnFromNoAnnotation(
null,
// comment,
nullability,
propertyHolder,

View File

@ -9,6 +9,7 @@ package org.hibernate.boot.model.internal;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.Columns;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.FractionalSeconds;
import org.hibernate.annotations.JoinColumnOrFormula;
import org.hibernate.annotations.JoinColumnsOrFormulas;
import org.hibernate.annotations.JoinFormula;
@ -85,6 +86,7 @@ class ColumnsBuilder {
if ( property.isAnnotationPresent( Column.class ) ) {
columns = buildColumnFromAnnotation(
property.getAnnotation( Column.class ),
property.getAnnotation( FractionalSeconds.class ),
// comment,
nullability,
propertyHolder,
@ -107,6 +109,7 @@ class ColumnsBuilder {
else if ( property.isAnnotationPresent( Columns.class ) ) {
columns = buildColumnsFromAnnotations(
property.getAnnotation( Columns.class ).columns(),
null,
// comment,
nullability,
propertyHolder,
@ -144,6 +147,7 @@ class ColumnsBuilder {
if ( columns == null && !property.isAnnotationPresent( ManyToMany.class ) ) {
//useful for collection of embedded elements
columns = buildColumnFromNoAnnotation(
property.getAnnotation( FractionalSeconds.class ),
// comment,
nullability,
propertyHolder,

View File

@ -70,6 +70,7 @@ public class IdBagBinder extends BagBinder {
final AnnotatedColumns idColumns = AnnotatedColumn.buildColumnsFromAnnotations(
new Column[] { collectionIdAnn.column() },
// null,
null,
Nullability.FORCED_NOT_NULL,
propertyHolder,
propertyData,

View File

@ -63,7 +63,6 @@ import org.hibernate.type.descriptor.java.BasicJavaType;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.java.spi.FormatMapperBasedJavaType;
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
import org.hibernate.type.descriptor.java.spi.JsonJavaType;
import org.hibernate.type.descriptor.java.spi.RegistryHelper;
@ -233,9 +232,10 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
@Override
public long getColumnLength() {
final Selectable column = getColumn();
if ( column instanceof Column ) {
final Long length = ( (Column) column ).getLength();
final Selectable selectable = getColumn();
if ( selectable instanceof Column ) {
final Column column = (Column) selectable;
final Long length = column.getLength();
return length == null ? NO_COLUMN_LENGTH : length;
}
else {
@ -245,10 +245,14 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
@Override
public int getColumnPrecision() {
final Selectable column = getColumn();
if ( column instanceof Column ) {
final Integer length = ( (Column) column ).getPrecision();
return length == null ? NO_COLUMN_PRECISION : length;
final Selectable selectable = getColumn();
if ( selectable instanceof Column ) {
final Column column = (Column) selectable;
if ( column.getTemporalPrecision() != null ) {
return column.getTemporalPrecision();
}
final Integer precision = column.getPrecision();
return precision == null ? NO_COLUMN_PRECISION : precision;
}
else {
return NO_COLUMN_PRECISION;
@ -257,10 +261,11 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
@Override
public int getColumnScale() {
final Selectable column = getColumn();
if ( column instanceof Column ) {
final Integer length = ( (Column) column ).getScale();
return length == null ? NO_COLUMN_SCALE : length;
final Selectable selectable = getColumn();
if ( selectable instanceof Column ) {
final Column column = (Column) selectable;
final Integer scale = column.getScale();
return scale == null ? NO_COLUMN_SCALE : scale;
}
else {
return NO_COLUMN_SCALE;

View File

@ -23,12 +23,15 @@ import org.hibernate.engine.spi.Mapping;
import org.hibernate.loader.internal.AliasConstantsHelper;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.internal.TypecheckUtil;
import org.hibernate.sql.Template;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.BasicType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.JdbcTypeNameMapper;
import org.hibernate.type.descriptor.java.JavaTypeHelper;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.sql.DdlType;
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
@ -51,6 +54,7 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
private Long length;
private Integer precision;
private Integer scale;
private Integer temporalPrecision;
private Integer arrayLength;
private Value value;
private int typeIndex;
@ -422,12 +426,23 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
Size calculateColumnSize(Dialect dialect, Mapping mapping) {
Type type = getValue().getType();
Long lengthToUse = getLength();
Integer precisionToUse = getPrecision();
Integer scaleToUse = getScale();
if ( type instanceof EntityType ) {
type = getTypeForEntityValue( mapping, type, getTypeIndex() );
}
if ( type instanceof ComponentType ) {
type = getTypeForComponentValue( mapping, type, getTypeIndex() );
}
if ( type instanceof BasicType ) {
final BasicType<?> basicType = (BasicType<?>) type;
if ( JavaTypeHelper.isTemporal( basicType.getExpressibleJavaType() ) ) {
precisionToUse = getTemporalPrecision();
lengthToUse = null;
scaleToUse = null;
}
}
if ( type == null ) {
throw new AssertionFailure( "no typing information available to determine column size" );
}
@ -435,9 +450,9 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
Size size = dialect.getSizeStrategy().resolveSize(
jdbcMapping.getJdbcType(),
jdbcMapping.getJdbcJavaType(),
precision,
scale,
length
precisionToUse,
scaleToUse,
lengthToUse
);
size.setArrayLength( arrayLength );
return size;
@ -674,6 +689,14 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
this.scale = scale;
}
public Integer getTemporalPrecision() {
return temporalPrecision;
}
public void setTemporalPrecision(Integer temporalPrecision) {
this.temporalPrecision = temporalPrecision;
}
public String getComment() {
return comment;
}

View File

@ -463,7 +463,19 @@ public final class DateTimeUtils {
);
}
public static <T extends Temporal> T roundToSecondPrecision(T temporal, int precision) {
//noinspection unchecked
return (T) temporal.with(
ChronoField.NANO_OF_SECOND,
roundToPrecision( temporal.get( ChronoField.NANO_OF_SECOND ), precision )
);
}
public static long roundToPrecision(int nano, int precision) {
assert precision < 9 : "Precision (scale) should be less-than 9 - " + precision;
if ( precision == 0 ) {
return 0;
}
final int precisionMask = pow10( 9 - precision );
final int nanosToRound = nano % precisionMask;
return nano - nanosToRound + ( nanosToRound >= ( precisionMask >> 1 ) ? precisionMask : 0 );
@ -478,17 +490,17 @@ public final class DateTimeUtils {
case 2:
return 100;
case 3:
return 1000;
return 1_000;
case 4:
return 10000;
return 10_000;
case 5:
return 100000;
return 100_000;
case 6:
return 1000000;
return 1_000_000;
case 7:
return 10000000;
return 10_000_000;
case 8:
return 100000000;
return 100_000_000;
default:
return (int) Math.pow( 10, exponent );
}

View File

@ -0,0 +1,268 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.orm.test.temporal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZonedDateTime;
import org.hibernate.annotations.FractionalSeconds;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.dialect.SybaseDialect;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.type.descriptor.DateTimeUtils;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.DomainModelScope;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("JUnitMalformedDeclaration")
public class FractionalSecondsTests {
@Test
@DomainModel(annotatedClasses = {TestEntity.class, TestEntity0.class, TestEntity3.class, TestEntity9.class} )
void testMapping(DomainModelScope scope) {
final MetadataImplementor domainModel = scope.getDomainModel();
final Dialect dialect = domainModel.getDatabase().getDialect();
final int defaultPrecision = dialect.getDefaultTimestampPrecision();
final PersistentClass entityBinding = scope.getEntityBinding( TestEntity.class );
checkPrecision( "theInstant", defaultPrecision, entityBinding, domainModel );
checkPrecision( "theLocalDateTime", defaultPrecision, entityBinding, domainModel );
checkPrecision( "theLocalTime", defaultPrecision, entityBinding, domainModel );
checkPrecision( "theOffsetDateTime", defaultPrecision, entityBinding, domainModel );
checkPrecision( "theOffsetTime", defaultPrecision, entityBinding, domainModel );
checkPrecision( "theZonedDateTime", defaultPrecision, entityBinding, domainModel );
final PersistentClass entityBinding0 = scope.getEntityBinding( TestEntity0.class );
checkPrecision( "theInstant", 0, entityBinding0, domainModel );
final PersistentClass entityBinding3 = scope.getEntityBinding( TestEntity3.class );
checkPrecision( "theInstant", 3, entityBinding3, domainModel );
checkPrecision( "theLocalDateTime", 3, entityBinding3, domainModel );
checkPrecision( "theLocalTime", 3, entityBinding3, domainModel );
final PersistentClass entityBinding9 = scope.getEntityBinding( TestEntity9.class );
checkPrecision( "theInstant", 9, entityBinding9, domainModel );
checkPrecision( "theOffsetDateTime", 9, entityBinding9, domainModel );
checkPrecision( "theOffsetTime", 9, entityBinding9, domainModel );
checkPrecision( "theZonedDateTime", 9, entityBinding9, domainModel );
}
private void checkPrecision(
String propertyName,
int expectedMinimumSize,
PersistentClass entityBinding,
MetadataImplementor domainModel) {
final Property theInstant = entityBinding.getProperty( propertyName );
final BasicValue value = (BasicValue) theInstant.getValue();
final Column column = (Column) value.getColumn();
final Size columnSize = column.getColumnSize( value.getDialect(), domainModel );
assertThat( columnSize.getPrecision() ).isEqualTo( expectedMinimumSize );
}
@Test
@DomainModel(annotatedClasses = TestEntity.class)
@SessionFactory
@SkipForDialect( dialectClass = DB2Dialect.class, reason = "Occasional mismatch in rounding versus our code" )
@SkipForDialect(dialectClass = SybaseDialect.class, reason = "Because... Sybase...", matchSubTypes = true)
void testUsage(SessionFactoryScope scope) {
final Instant start = Instant.now();
scope.inTransaction( (session) -> {
final TestEntity testEntity = new TestEntity();
testEntity.id = 1;
testEntity.theInstant = start;
session.persist( testEntity );
} );
scope.inTransaction( (session) -> {
final TestEntity testEntity = session.find( TestEntity.class, 1 );
final Dialect dialect = session.getSessionFactory().getJdbcServices().getDialect();
if ( dialect instanceof DerbyDialect
|| dialect instanceof MariaDBDialect ) {
assertThat( testEntity.theInstant ).isEqualTo( start );
}
else {
assertThat( testEntity.theInstant ).isEqualTo( DateTimeUtils.roundToSecondPrecision( start, 6 ) );
}
} );
}
@Test
@DomainModel(annotatedClasses = TestEntity0.class)
@SessionFactory
@SkipForDialect( dialectClass = H2Dialect.class, reason = "Occasional mismatch in rounding versus our code" )
@SkipForDialect( dialectClass = MariaDBDialect.class, reason = "Occasional mismatch in rounding versus our code" )
@SkipForDialect( dialectClass = MySQLDialect.class, reason = "Occasional mismatch in rounding versus our code", matchSubTypes = true )
@SkipForDialect( dialectClass = OracleDialect.class, reason = "Occasional mismatch in rounding versus our code" )
@SkipForDialect( dialectClass = SQLServerDialect.class, reason = "Occasional mismatch in rounding versus our code" )
@SkipForDialect( dialectClass = PostgreSQLDialect.class, reason = "Occasional mismatch in rounding versus our code", matchSubTypes = true )
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby does not support sized timestamp" )
@SkipForDialect(dialectClass = SybaseDialect.class, reason = "Because... Sybase...", matchSubTypes = true)
void testUsage0(SessionFactoryScope scope) {
final Instant start = Instant.now();
scope.inTransaction( (session) -> {
final TestEntity0 testEntity = new TestEntity0();
testEntity.id = 1;
testEntity.theInstant = start;
session.persist( testEntity );
} );
scope.inTransaction( (session) -> {
final TestEntity0 testEntity = session.find( TestEntity0.class, 1 );
assertThat( testEntity.theInstant ).isEqualTo( DateTimeUtils.roundToSecondPrecision( start, 0 ) );
} );
}
@Test
@DomainModel(annotatedClasses = TestEntity3.class)
@SessionFactory
@SkipForDialect( dialectClass = MariaDBDialect.class, reason = "Occasional mismatch in rounding versus our code" )
@SkipForDialect( dialectClass = HSQLDialect.class, reason = "Occasional mismatch in rounding versus our code" )
@SkipForDialect( dialectClass = DB2Dialect.class, reason = "Occasional mismatch in rounding versus our code" )
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby does not support sized timestamp" )
@SkipForDialect(dialectClass = SybaseDialect.class, reason = "Because... Sybase...", matchSubTypes = true)
void testUsage3(SessionFactoryScope scope) {
final Instant start = Instant.now();
scope.inTransaction( (session) -> {
final TestEntity3 testEntity = new TestEntity3();
testEntity.id = 1;
testEntity.theInstant = start;
session.persist( testEntity );
} );
scope.inTransaction( (session) -> {
final TestEntity3 testEntity = session.find( TestEntity3.class, 1 );
assertThat( testEntity.theInstant ).isEqualTo( DateTimeUtils.roundToSecondPrecision( start, 3 ) );
} );
}
@Test
@DomainModel(annotatedClasses = TestEntity9.class)
@SessionFactory
@SkipForDialect( dialectClass = MariaDBDialect.class, reason = "MariaDB only supports precision <= 6" )
@SkipForDialect( dialectClass = MySQLDialect.class, reason = "MySQL only supports precision <= 6", matchSubTypes = true )
@SkipForDialect( dialectClass = SQLServerDialect.class, reason = "SQL Server only supports precision <= 6" )
@SkipForDialect(dialectClass = SybaseDialect.class, reason = "Because... Sybase...", matchSubTypes = true)
void testUsage9(SessionFactoryScope scope) {
final Instant start = Instant.now();
scope.inTransaction( (session) -> {
final TestEntity9 testEntity = new TestEntity9();
testEntity.id = 1;
testEntity.theInstant = start;
session.persist( testEntity );
} );
scope.inTransaction( (session) -> {
final TestEntity9 testEntity = session.find( TestEntity9.class, 1 );
assertThat( testEntity.theInstant ).isEqualTo( start );
} );
}
@Entity(name="TestEntity")
@Table(name="TestEntity")
public static class TestEntity {
@Id
private Integer id;
private Instant theInstant;
private LocalDateTime theLocalDateTime;
private LocalTime theLocalTime;
private OffsetDateTime theOffsetDateTime;
private OffsetTime theOffsetTime;
private ZonedDateTime theZonedDateTime;
}
@Entity(name="TestEntity0")
@Table(name="TestEntity0")
public static class TestEntity0 {
@Id
private Integer id;
@FractionalSeconds(0)
private Instant theInstant;
}
@Entity(name="TestEntity3")
@Table(name="TestEntity3")
public static class TestEntity3 {
@Id
private Integer id;
@FractionalSeconds(3)
private Instant theInstant;
@FractionalSeconds(3)
private LocalDateTime theLocalDateTime;
@FractionalSeconds(3)
private LocalTime theLocalTime;
}
@Entity(name="TestEntity9")
@Table(name="TestEntity9")
public static class TestEntity9 {
@Id
private Integer id;
@FractionalSeconds(9)
private Instant theInstant;
@FractionalSeconds(9)
private OffsetDateTime theOffsetDateTime;
@FractionalSeconds(9)
private OffsetTime theOffsetTime;
@FractionalSeconds(9)
private ZonedDateTime theZonedDateTime;
}
}