I have pushed some updates on HHH-14736 to
https://github.com/pdinc-oss/hibernate-orm/tree/HHH-14736 - please provide feedback.
It needs more tests, but the crux of the change is as follows:
diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/SourceType.java
b/hibernate-core/src/main/java/org/hibernate/annotations/SourceType.java
index cfd0c153c7..0277ed1ef0 100644
--- a/hibernate-core/src/main/java/org/hibernate/annotations/SourceType.java
+++ b/hibernate-core/src/main/java/org/hibernate/annotations/SourceType.java
@@ -17,6 +17,16 @@ public enum SourceType {
*/
VM( "timestamp" ),
+ /**
+ * Get the binary, non-timestamp from the database.
+ */
+ DBBINARY( "dbbinary" ),
+
+ /**
+ * Get the binary, non-timestamp from the database, as a {@link Long}.
+ */
+ DBLONG( "dblong" ),
+
/**
* Get the timestamp from the database.
*/
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
index 344b1e234c..971b948256 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
@@ -782,6 +782,7 @@ public final class AnnotationBinder {
// check properties
final InheritanceState.ElementsToProcess elementsToProcess =
inheritanceState.getElementsToProcess();
inheritanceState.postProcess( persistentClass, entityBinder );
+
context.getMetadataCollector().getDatabase().getDialect().getVersionColumnSupport().filter(
elementsToProcess );
final boolean subclassAndSingleTableStrategy = inheritanceState.getType()
== InheritanceType.SINGLE_TABLE
&& inheritanceState.hasParents();
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
index d157f8c599..0244ee4259 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
@@ -60,6 +60,8 @@ import org.hibernate.dialect.pagination.LegacyLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.unique.DefaultUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
+import org.hibernate.dialect.version.DefaultNoOpVersionColumnSupportImpl;
+import org.hibernate.dialect.version.VersionColumnSupport;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.LobCreator;
@@ -1011,6 +1013,19 @@ public abstract class Dialect implements ConversionContext {
}
}
+ // VERSION support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected VersionColumnSupport versionSupport = new
DefaultNoOpVersionColumnSupportImpl();
+
+ /**
+ * Get the appropriate {@link VersionColumnSupport}
+ *
+ * @return the {@link VersionColumnSupport}
+ * @since 5.1
+ */
+ public VersionColumnSupport getVersionColumnSupport() {
+ return versionSupport;
+ }
// GUID support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Then the magic happens inside the filter
1st it checks if it is an @Version property and that the rules for mapping Long to
rowversion are in place
public void filter(ElementsToProcess elementsToProcess) {
if ( elementsToProcess == null )
return;
List<PropertyData> elements = elementsToProcess.getElements();
if ( elements == null || elements.isEmpty() )
return;
for ( int i = 0; i < elements.size(); ++i ) {
PropertyData element = elements.get( i );
if ( element == null )
continue;
XProperty prop = element.getProperty();
if ( prop == null )
continue;
if ( prop.getAnnotation( Version.class ) != null ) {
SQLServerNativeVersionAsLongOverride override =
prop.getAnnotation( SQLServerNativeVersionAsLongOverride.class );
if ( prop.getType().getName().equals( Long.class.getName() ) ) {
if ( ( override == null &&
sqlServerDialect.isNativeVersionAsLong() ) || override.value() ) {
elements.set( i, new
FilteredSQLServerLongVersionPropertyData( sqlServerDialect, sqlTypeRowversion, element )
);
}
}
else if ( prop.getType().getName().equals( byte[].class.getName()
) ) {
elements.set( i, new
FilteredSQLServerByteArrayVersionPropertyData( sqlServerDialect, sqlTypeRowversion,
element ) );
}
}
}
}
2nd (only showing the Long, there is one for byte[] too) then it on the fly filters the
PropertyData to add the required non-default values and Hibernate specific configurations
to the property
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
// called from ColumnsBuilder:extractMetadata():76
T res = property.getAnnotation( annotationType );
if ( annotationType == null )
return res;
else if ( annotationType.isAssignableFrom( ColumnTransformer.class ) )
return (T) new FilteredSQLServerColumnTransformerAnnotation(
sqlServerDialect, property,
(ColumnTransformer) ( res == null ? defaults.get(
ColumnTransformer.class ) : res ) );
else if ( annotationType.isAssignableFrom( Generated.class ) )
return (T) new FilteredSQLServerGeneratedAnnotation( property, (Generated)
( res == null ? defaults.get( Generated.class ) : res ) );
else if ( annotationType.isAssignableFrom( Column.class ) )
return (T) new FilteredSQLServerColumnAnnotation( sqlTypeRowversion,
property,
(Column) ( res == null ? defaults.get( Column.class ) :
res ) );
else if ( annotationType.isAssignableFrom( Source.class ) )
return (T) new FilteredSQLServerSourceAnnotation( property,
SOURCE.value(), (Source) ( res == null ? defaults.get( Source.class ) : res ) );
else
return res;
}
Respectfully,
Jason Pyeron
From: Jason Pyeron [mailto:jpyeron@pdinc.us]
Sent: Sunday, August 1, 2021 11:48 AM
To: 'Hibernate Dev' <hibernate-dev(a)lists.jboss.org>
Subject: HHH-14736 advice needed - metamodel updates by Dialect
I am new to the meta model internals of Hibernate, please bear with me.
I am working in processElementAnnotations to use the new VersionColumnSupport to add
Dialect based defaults to the meta model – but I seem to be looking at this wrong.
Is there a test case, commit, or other documentation I can review to review as an
example?
Basically, if the Dialect says so (e.g. SQL Server @Version on Long) and the author of the
Entity does not specify conflicting configuration
Add to the meta model:
@Source(value = SourceType.DBLONG)
@ColumnTransformer(read = "CAST(… as BIGINT)")
@Column(columnDefinition = "rowversion", nullable = false, insertable = false,
updatable = false)
@Generated(GenerationTime.ALWAYS)
I was updating the Column in the SimpleValue, but that did not get reflected in the
runtime behavior – it only helped with schema generation.
commit 682fcbab75b8b055c676c7a4de1247a888b23e9c (HEAD -> HHH-14736,
pdinc-oss/HHH-14736) -
https://github.com/pdinc-oss/hibernate-orm/tree/HHH-14736
Author: Jason Pyeron <jpyeron(a)pdinc.us <mailto:jpyeron@pdinc.us> >
Date: Tue Jul 20 00:56:38 2021 -0400
HHH-14736 WIP: SQL Server @Version test cases (fails as expected)
* SQL Server 2016 dialect defaults to the new behavior, older version require it to be
turned on
* added VersionColumnSupport to Dialect
* added SQLServerLongRowVersionType / SQLServerBinaryRowVersionType (was:
SQLServerRowVersionType)
* when non-JPA annotations are allied, all tests pass
TODO:
1: update meta model to imply @ColumnTransformer(read = "CAST(...columnName... as
BIGINT)")
2: update meta model to imply @Column(columnDefinition = "rowversion"), the
create SQL already works
3: update meta model to imply @Column(nullable = false)
4: update meta model to imply @Column(insertable = false, updatable = false)
5: update meta model to imply @Generated(GenerationTime.ALWAYS)
6: need test case for setting SQLServer2012 dialect to use new features
7: address HqlSqlWalker.isDatabaseGeneratedTimestamp() - needs test case and possible
code
M hibernate-core/src/main/java/org/hibernate/annotations/SourceType.java
M hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
M hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
M hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2005Dialect.java
M hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2016Dialect.java
M hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java
A
hibernate-core/src/main/java/org/hibernate/dialect/version/DefaultNoOpVersionColumnSupportImpl.java
A
hibernate-core/src/main/java/org/hibernate/dialect/version/SQLServerVersionColumnSupport.java
A
hibernate-core/src/main/java/org/hibernate/dialect/version/VersionColumnSupport.java
M hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlSqlWalker.java
M
hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java
M hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java
M hibernate-core/src/main/java/org/hibernate/type/RowVersionType.java
A
hibernate-core/src/main/java/org/hibernate/type/SQLServerBinaryRowVersionType.java
A hibernate-core/src/main/java/org/hibernate/type/SQLServerLongRowVersionType.java
M
hibernate-core/src/test/java/org/hibernate/test/annotations/various/TimestampTest.java
A
hibernate-core/src/test/java/org/hibernate/test/optlock/OptimisticLockHHH14736SQLServerJPAOnlyTest.java
A
hibernate-core/src/test/java/org/hibernate/test/optlock/OptimisticLockHHH14736Test.java
-Jason
--
Jason Pyeron | Architect
Contractor |
PD Inc | Certified SBA 8(a)
10 w 24th St | Certified SBA HUBZone
Baltimore, MD | CAGE Code: 1WVR6
.com: jpyeron(a)pdinc.us <mailto:jpyeron@pdinc.us>
tel : 202-741-9397