Fixes for Yoann's search + 6.0 tracking

- Support for generated values
- See `@ProposedGenerated` for proposed change to `@Generated`; without that change, `@Generated` will not work with update-generation; nor does it work on 5.x
This commit is contained in:
Steve Ebersole 2021-08-02 15:42:58 -05:00
parent b8afa46d8f
commit 230c787b3c
56 changed files with 2545 additions and 540 deletions

View File

@ -61,7 +61,7 @@ public class PropertyAccessException extends HibernateException {
@Override
public String getMessage() {
return originalMessage()
+ ( wasSetter ? " setter of " : " getter of " )
+ StringHelper.qualify( persistentClass.getName(), propertyName );
+ " : `" + StringHelper.qualify( persistentClass.getName(), propertyName )
+ ( wasSetter ? "` (setter)" : "` (getter)" );
}
}

View File

@ -348,13 +348,15 @@ public class PropertyBinder {
return NoValueGeneration.INSTANCE;
}
final GenerationTiming when = valueGeneration.getGenerationTiming();
if ( valueGeneration.getValueGenerator() == null ) {
// if we have an in-db generator, mark it as not insertable nor updatable
insertable = false;
if ( when == GenerationTiming.ALWAYS ) {
updatable = false;
}
updatable = false;
// final GenerationTiming when = valueGeneration.getGenerationTiming();
// if ( when == GenerationTiming.ALWAYS ) {
// // it should also be not-updatable
// updatable = false;
// }
}
return valueGeneration;
@ -394,21 +396,18 @@ public class PropertyBinder {
private <A extends Annotation> AnnotationValueGeneration<A> getValueGenerationFromAnnotation(
XProperty property,
A annotation) {
ValueGenerationType generatorAnnotation = annotation.annotationType()
.getAnnotation( ValueGenerationType.class );
final ValueGenerationType generatorAnnotation = annotation.annotationType().getAnnotation( ValueGenerationType.class );
if ( generatorAnnotation == null ) {
return null;
}
Class<? extends AnnotationValueGeneration<?>> generationType = generatorAnnotation.generatedBy();
AnnotationValueGeneration<A> valueGeneration = instantiateAndInitializeValueGeneration(
annotation, generationType, property
);
final Class<? extends AnnotationValueGeneration<?>> generationType = generatorAnnotation.generatedBy();
final AnnotationValueGeneration<A> valueGeneration = instantiateAndInitializeValueGeneration( annotation, generationType, property );
if ( annotation.annotationType() == Generated.class &&
property.isAnnotationPresent( javax.persistence.Version.class ) &&
valueGeneration.getGenerationTiming() == GenerationTiming.INSERT ) {
if ( annotation.annotationType() == Generated.class
&& property.isAnnotationPresent( javax.persistence.Version.class )
&& valueGeneration.getGenerationTiming() == GenerationTiming.INSERT ) {
throw new AnnotationException(
"@Generated(INSERT) on a @Version property not allowed, use ALWAYS (or NEVER): "

View File

@ -0,0 +1,32 @@
/*
* 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.internal.util;
/**
* Support for mutable boolean references, generally used from within
* anon inner classes, lambdas, etc
*
* @author Steve Ebersole
*/
public class MutableBoolean {
private boolean value;
public MutableBoolean() {
}
public MutableBoolean(boolean value) {
this.value = value;
}
public boolean getValue() {
return value;
}
public void setValue(boolean value) {
this.value = value;
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.metamodel.mapping;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.java.MutabilityPlanExposer;
@ -28,8 +29,18 @@ public interface AttributeMapping extends ModelPart, ValueMapping, Fetchable, Pr
ManagedMappingType getDeclaringType();
/**
* The getter/setter access to this attribute
*/
PropertyAccess getPropertyAccess();
/**
* The value generation strategy to use for this attribute.
*
* @apiNote Only relevant for non-id attributes
*/
ValueGeneration getValueGeneration();
@Override
default EntityMappingType findContainingEntityMapping() {
return getDeclaringType().findContainingEntityMapping();

View File

@ -7,8 +7,15 @@
package org.hibernate.metamodel.mapping;
/**
* Provides access to details about an attribute specific to a particular
* entity in the hierarchy. Accounts for attribute/association overrides, etc
*
* @author Steve Ebersole
*/
@FunctionalInterface
public interface AttributeMetadataAccess {
/**
* Resolve the details about the attribute
*/
AttributeMetadata resolveAttributeMetadata(EntityMappingType entityMappingType);
}

View File

@ -195,6 +195,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp
attributeMapping = BasicAttributeMapping.withSelectableMapping(
original,
original.getPropertyAccess(),
original.getValueGeneration(),
selectableMapping
);
currentIndex++;

View File

@ -0,0 +1,52 @@
/*
* 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.metamodel.mapping;
import org.hibernate.Incubating;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.internal.NoGeneratedValueResolver;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGeneration;
/**
* Generalized contract covering an attribute's generation handling
*
* @author Steve Ebersole
*/
@Incubating
public interface GeneratedValueResolver {
static GeneratedValueResolver from(
ValueGeneration valueGeneration,
GenerationTiming requestedTiming,
int dbSelectionPosition) {
assert requestedTiming != GenerationTiming.NEVER;
if ( valueGeneration == null || valueGeneration.getGenerationTiming().includes( GenerationTiming.NEVER ) ) {
return NoGeneratedValueResolver.INSTANCE;
}
if ( requestedTiming == GenerationTiming.ALWAYS && valueGeneration.getGenerationTiming() == GenerationTiming.INSERT ) {
return NoGeneratedValueResolver.INSTANCE;
}
// todo (6.x) : incorporate `org.hibernate.tuple.InDatabaseValueGenerationStrategy`
// and `org.hibernate.tuple.InMemoryValueGenerationStrategy` from `EntityMetamodel`.
// this requires unification of the read and write (insert/update) aspects of
// value generation which we'll circle back to as we convert write operations to
// use the "runtime mapping" (`org.hibernate.metamodel.mapping`) model
if ( valueGeneration.getValueGenerator() == null ) {
// in-db generation (column-default, function, etc)
return new InDatabaseGeneratedValueResolver( requestedTiming, dbSelectionPosition );
}
return new InMemoryGeneratedValueResolver( valueGeneration.getValueGenerator(), requestedTiming );
}
GenerationTiming getGenerationTiming();
Object resolveGeneratedValue(Object[] row, Object entity, SharedSessionContractImplementor session);
}

View File

@ -0,0 +1,38 @@
/*
* 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.metamodel.mapping;
import org.hibernate.Internal;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.tuple.GenerationTiming;
/**
* GeneratedValueResolver impl for in-db generation. It extracts the generated value
* from a array of the ResultSet values
*
* @author Steve Ebersole
*/
@Internal
public class InDatabaseGeneratedValueResolver implements GeneratedValueResolver {
private final GenerationTiming timing;
private final int resultPosition;
public InDatabaseGeneratedValueResolver(GenerationTiming timing, int resultPosition) {
this.timing = timing;
this.resultPosition = resultPosition;
}
@Override
public GenerationTiming getGenerationTiming() {
return timing;
}
@Override
public Object resolveGeneratedValue(Object[] row, Object entity, SharedSessionContractImplementor session) {
return row[resultPosition];
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.metamodel.mapping;
import org.hibernate.Internal;
import org.hibernate.Session;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGenerator;
/**
* GeneratedValueResolver impl for in-memory generation
*
* @author Steve Ebersole
*/
@Internal
public class InMemoryGeneratedValueResolver implements GeneratedValueResolver {
private final GenerationTiming generationTiming;
private final ValueGenerator valueGenerator;
public InMemoryGeneratedValueResolver(ValueGenerator valueGenerator, GenerationTiming generationTiming) {
this.valueGenerator = valueGenerator;
this.generationTiming = generationTiming;
}
@Override
public GenerationTiming getGenerationTiming() {
return generationTiming;
}
@Override
public Object resolveGeneratedValue(Object[] row, Object entity, SharedSessionContractImplementor session) {
return valueGenerator.generateValue( (Session) session, entity );
}
}

View File

@ -13,6 +13,7 @@ import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.sql.results.graph.FetchOptions;
import org.hibernate.tuple.ValueGeneration;
/**
* @author Steve Ebersole
@ -22,6 +23,7 @@ public abstract class AbstractSingularAttributeMapping
implements SingularAttributeMapping {
private final PropertyAccess propertyAccess;
private final ValueGeneration valueGeneration;
public AbstractSingularAttributeMapping(
String name,
@ -29,9 +31,13 @@ public abstract class AbstractSingularAttributeMapping
StateArrayContributorMetadataAccess attributeMetadataAccess,
FetchOptions mappedFetchOptions,
ManagedMappingType declaringType,
PropertyAccess propertyAccess) {
PropertyAccess propertyAccess,
ValueGeneration valueGeneration) {
super( name, attributeMetadataAccess, mappedFetchOptions, stateArrayPosition, declaringType );
this.propertyAccess = propertyAccess;
this.valueGeneration = valueGeneration != null
? valueGeneration
: NoValueGeneration.INSTANCE;
}
public AbstractSingularAttributeMapping(
@ -41,13 +47,22 @@ public abstract class AbstractSingularAttributeMapping
FetchTiming fetchTiming,
FetchStyle fetchStyle,
ManagedMappingType declaringType,
PropertyAccess propertyAccess) {
PropertyAccess propertyAccess,
ValueGeneration valueGeneration) {
super( name, attributeMetadataAccess, fetchTiming, fetchStyle, stateArrayPosition, declaringType );
this.propertyAccess = propertyAccess;
this.valueGeneration = valueGeneration != null
? valueGeneration
: NoValueGeneration.INSTANCE;
}
@Override
public PropertyAccess getPropertyAccess() {
return propertyAccess;
}
@Override
public ValueGeneration getValueGeneration() {
return valueGeneration;
}
}

View File

@ -13,12 +13,12 @@ import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.ConvertibleModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
@ -39,6 +39,7 @@ import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
@ -77,8 +78,9 @@ public class BasicAttributeMapping
BasicValueConverter valueConverter,
JdbcMapping jdbcMapping,
ManagedMappingType declaringType,
PropertyAccess propertyAccess) {
super( attributeName, stateArrayPosition, attributeMetadataAccess, mappedFetchTiming, mappedFetchStyle, declaringType, propertyAccess );
PropertyAccess propertyAccess,
ValueGeneration valueGeneration) {
super( attributeName, stateArrayPosition, attributeMetadataAccess, mappedFetchTiming, mappedFetchStyle, declaringType, propertyAccess, valueGeneration );
this.navigableRole = navigableRole;
this.tableExpression = tableExpression;
this.mappedColumnExpression = mappedColumnExpression;
@ -106,6 +108,7 @@ public class BasicAttributeMapping
public static BasicAttributeMapping withSelectableMapping(
BasicValuedModelPart original,
PropertyAccess propertyAccess,
ValueGeneration valueGeneration,
SelectableMapping selectableMapping) {
String attributeName = null;
int stateArrayPosition = 0;
@ -143,7 +146,8 @@ public class BasicAttributeMapping
valueConverter,
selectableMapping.getJdbcMapping(),
declaringType,
propertyAccess
propertyAccess,
valueGeneration
);
}

View File

@ -75,7 +75,8 @@ public class DiscriminatedAssociationAttributeMapping
fetchTiming,
FetchStyle.SELECT,
declaringType,
propertyAccess
propertyAccess,
null
);
this.navigableRole = attributeRole;

View File

@ -50,6 +50,7 @@ import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
import org.hibernate.tuple.ValueGeneration;
/**
* @author Steve Ebersole
@ -75,7 +76,8 @@ public class EmbeddedAttributeMapping
FetchStyle mappedFetchStyle,
EmbeddableMappingType embeddableMappingType,
ManagedMappingType declaringType,
PropertyAccess propertyAccess) {
PropertyAccess propertyAccess,
ValueGeneration valueGeneration) {
super(
name,
stateArrayPosition,
@ -83,7 +85,8 @@ public class EmbeddedAttributeMapping
mappedFetchTiming,
mappedFetchStyle,
declaringType,
propertyAccess
propertyAccess,
valueGeneration
);
this.navigableRole = navigableRole;
@ -107,7 +110,7 @@ public class EmbeddedAttributeMapping
SelectableMappings selectableMappings,
EmbeddableValuedModelPart inverseModelPart,
MappingModelCreationProcess creationProcess) {
super( inverseModelPart.getFetchableName(), -1, null, inverseModelPart.getMappedFetchOptions(), null, null );
super( inverseModelPart.getFetchableName(), -1, null, inverseModelPart.getMappedFetchOptions(), null, null, null );
this.navigableRole = inverseModelPart.getNavigableRole().getParent().append( inverseModelPart.getFetchableName() );

View File

@ -0,0 +1,186 @@
/*
* 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.metamodel.mapping.internal;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Incubating;
import org.hibernate.LockOptions;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.loader.ast.internal.LoaderSelectBuilder;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.GeneratedValueResolver;
import org.hibernate.metamodel.mapping.InDatabaseGeneratedValueResolver;
import org.hibernate.metamodel.mapping.StateArrayContributorMapping;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
import org.hibernate.sql.exec.spi.Callback;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.hibernate.tuple.GenerationTiming;
/**
* @author Steve Ebersole
*/
@Incubating
public class GeneratedValuesProcessor {
private final SelectStatement selectStatement;
private final List<GeneratedValueDescriptor> valueDescriptors = new ArrayList<>();
private final List<JdbcParameter> jdbcParameters = new ArrayList<>();
private final EntityMappingType entityDescriptor;
private final SessionFactoryImplementor sessionFactory;
public GeneratedValuesProcessor(
EntityMappingType entityDescriptor,
GenerationTiming timing,
SessionFactoryImplementor sessionFactory) {
this.entityDescriptor = entityDescriptor;
this.sessionFactory = sessionFactory;
// NOTE: we only care about db-generated values here. in-memory generation
// is applied before the insert/update happens.
final List<StateArrayContributorMapping> generatedValuesToSelect = new ArrayList<>();
entityDescriptor.visitAttributeMappings( (attr) -> {
//noinspection RedundantClassCall
if ( ! StateArrayContributorMapping.class.isInstance( attr ) ) {
return;
}
if ( attr.getValueGeneration().getGenerationTiming() == GenerationTiming.NEVER ) {
return;
}
final GeneratedValueResolver generatedValueResolver = GeneratedValueResolver.from(
attr.getValueGeneration(),
timing,
generatedValuesToSelect.size()
);
//noinspection RedundantClassCall
if ( ! InDatabaseGeneratedValueResolver.class.isInstance( generatedValueResolver ) ) {
// again, we only care about in in-db generations here
return;
}
// this attribute is generated for the timing we are processing...
valueDescriptors.add( new GeneratedValueDescriptor( generatedValueResolver, (StateArrayContributorMapping) attr ) );
generatedValuesToSelect.add( (StateArrayContributorMapping) attr );
});
if ( generatedValuesToSelect.isEmpty() ) {
selectStatement = null;
}
else {
selectStatement = LoaderSelectBuilder.createSelect(
entityDescriptor,
generatedValuesToSelect,
entityDescriptor.getIdentifierMapping(),
null,
1,
LoadQueryInfluencers.NONE,
LockOptions.READ,
jdbcParameters::add,
sessionFactory
);
}
}
public void processGeneratedValues(Object entity, Object id, Object[] state, SharedSessionContractImplementor session) {
if ( selectStatement == null ) {
return;
}
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
final JdbcParameterBindings jdbcParamBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() );
int offset = jdbcParamBindings.registerParametersForEachJdbcValue(
id,
Clause.WHERE,
entityDescriptor.getIdentifierMapping(),
jdbcParameters,
session
);
assert offset == jdbcParameters.size();
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory
.buildSelectTranslator( sessionFactory, selectStatement )
.translate( jdbcParamBindings, QueryOptions.NONE );
final List<Object[]> results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
jdbcSelect,
jdbcParamBindings,
new ExecutionContext() {
@Override
public SharedSessionContractImplementor getSession() {
return session;
}
@Override
public QueryOptions getQueryOptions() {
return QueryOptions.NONE;
}
@Override
public String getQueryIdentifier(String sql) {
return sql;
}
@Override
public QueryParameterBindings getQueryParameterBindings() {
return QueryParameterBindings.NO_PARAM_BINDINGS;
}
@Override
public Callback getCallback() {
throw new UnsupportedOperationException( "Follow-on locking not supported yet" );
}
},
(row) -> row,
ListResultsConsumer.UniqueSemantic.FILTER
);
assert results.size() == 1;
final Object[] dbSelectionResults = results.get( 0 );
for ( int i = 0; i < valueDescriptors.size(); i++ ) {
final GeneratedValueDescriptor descriptor = valueDescriptors.get( i );
final Object generatedValue = descriptor.resolver.resolveGeneratedValue( dbSelectionResults, entity, session );
state[ descriptor.attribute.getStateArrayPosition() ] = generatedValue;
descriptor.attribute.getAttributeMetadataAccess()
.resolveAttributeMetadata( entityDescriptor )
.getPropertyAccess()
.getSetter()
.set( entity, generatedValue, sessionFactory );
}
}
private static class GeneratedValueDescriptor {
public final GeneratedValueResolver resolver;
public final StateArrayContributorMapping attribute;
public GeneratedValueDescriptor(GeneratedValueResolver resolver, StateArrayContributorMapping attribute) {
this.resolver = resolver;
this.attribute = attribute;
}
}
}

View File

@ -55,6 +55,7 @@ import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor;
import org.hibernate.metamodel.mapping.CollectionMappingType;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.GeneratedValueResolver;
import org.hibernate.metamodel.mapping.PropertyBasedMapping;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings;
@ -83,6 +84,7 @@ import org.hibernate.property.access.internal.ChainedPropertyAccessImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.type.AnyType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.BasicType;
@ -349,9 +351,9 @@ public class MappingModelCreationHelper {
final FetchTiming fetchTiming = bootProperty.isLazy() ? FetchTiming.DELAYED : FetchTiming.IMMEDIATE;
final FetchStyle fetchStyle = bootProperty.isLazy() ? FetchStyle.SELECT : FetchStyle.JOIN;
final ValueGeneration valueGeneration = bootProperty.getValueGenerationStrategy();
if ( valueConverter != null ) {
if ( isAttrFormula ) {
throw new MappingException( String.format(
"Value converter should not be set for column [%s] annotated with @Formula [%s]",
@ -367,11 +369,15 @@ public class MappingModelCreationHelper {
.getDomainModel()
.getTypeConfiguration()
.getBasicTypeRegistry()
.resolve(
valueConverter.getRelationalJavaDescriptor(),
resolution.getJdbcTypeDescriptor()
);
.resolve( valueConverter.getRelationalJavaDescriptor(), resolution.getJdbcTypeDescriptor() );
final GeneratedValueResolver generatedValueResolver;
if ( valueGeneration == null ) {
generatedValueResolver = NoGeneratedValueResolver.INSTANCE;
}
else if ( valueGeneration.getValueGenerator() == null ) {
// in-db generation
}
return new BasicAttributeMapping(
attrName,
@ -388,7 +394,8 @@ public class MappingModelCreationHelper {
valueConverter,
mappingBasicType.getJdbcMapping(),
declaringType,
propertyAccess
propertyAccess,
valueGeneration
);
}
else {
@ -407,7 +414,8 @@ public class MappingModelCreationHelper {
null,
attrType,
declaringType,
propertyAccess
propertyAccess,
valueGeneration
);
}
}
@ -449,7 +457,8 @@ public class MappingModelCreationHelper {
FetchStyle.JOIN,
attributeMappingType,
declaringType,
propertyAccess
propertyAccess,
bootProperty.getValueGenerationStrategy()
),
creationProcess
);

View File

@ -0,0 +1,31 @@
/*
* 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.metamodel.mapping.internal;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.GeneratedValueResolver;
import org.hibernate.tuple.GenerationTiming;
/**
* @author Steve Ebersole
*/
public class NoGeneratedValueResolver implements GeneratedValueResolver {
/**
* Singleton access
*/
public static final NoGeneratedValueResolver INSTANCE = new NoGeneratedValueResolver();
@Override
public GenerationTiming getGenerationTiming() {
return GenerationTiming.NEVER;
}
@Override
public Object resolveGeneratedValue(Object[] row, Object entity, SharedSessionContractImplementor session) {
throw new UnsupportedOperationException( "NoGeneratedValueResolver does not support generated values" );
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.metamodel.mapping.internal;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.tuple.ValueGenerator;
/**
* @author Steve Ebersole
*/
public class NoValueGeneration implements ValueGeneration {
/**
* Singleton access
*/
public static final NoValueGeneration INSTANCE = new NoValueGeneration();
@Override
public GenerationTiming getGenerationTiming() {
return GenerationTiming.NEVER;
}
@Override
public ValueGenerator<?> getValueGenerator() {
return (session, owner) -> null;
}
@Override
public boolean referenceColumnInSql() {
return false;
}
@Override
public String getDatabaseGeneratedReferencedColumnValue() {
return null;
}
@Override
public boolean timingMatches(GenerationTiming timing) {
return timing == GenerationTiming.NEVER;
}
}

View File

@ -72,6 +72,7 @@ import org.hibernate.sql.results.graph.collection.internal.CollectionDomainResul
import org.hibernate.sql.results.graph.collection.internal.DelayedCollectionFetch;
import org.hibernate.sql.results.graph.collection.internal.EagerCollectionFetch;
import org.hibernate.sql.results.graph.collection.internal.SelectEagerCollectionFetch;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.type.EntityType;
import org.jboss.logging.Logger;
@ -435,6 +436,12 @@ public class PluralAttributeMappingImpl
return propertyAccess;
}
@Override
public ValueGeneration getValueGeneration() {
// can never be a generated value
return NoValueGeneration.INSTANCE;
}
@Override
public String getFetchableName() {
return getAttributeName();

View File

@ -82,6 +82,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
keyModelPart = BasicAttributeMapping.withSelectableMapping(
keyModelPart,
keyPropertyAccess,
NoValueGeneration.INSTANCE,
keySelectableMapping
);
if ( swapDirection ) {

View File

@ -150,7 +150,9 @@ public class ToOneAttributeMapping
mappedFetchTiming,
mappedFetchStyle,
declaringType,
propertyAccess
propertyAccess,
// can never be a generated value
NoValueGeneration.INSTANCE
);
this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( name );
this.isNullable = bootValue.isNullable();
@ -271,7 +273,8 @@ public class ToOneAttributeMapping
original.getAttributeMetadataAccess(),
original,
original.getDeclaringType(),
original.getPropertyAccess()
original.getPropertyAccess(),
original.getValueGeneration()
);
this.navigableRole = original.navigableRole;
this.sqlAliasStem = original.sqlAliasStem;

View File

@ -107,6 +107,7 @@ import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FilterHelper;
import org.hibernate.internal.util.MutableBoolean;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
@ -169,6 +170,7 @@ import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EntityDiscriminatorMappingImpl;
import org.hibernate.metamodel.mapping.internal.EntityRowIdMappingImpl;
import org.hibernate.metamodel.mapping.internal.EntityVersionMappingImpl;
import org.hibernate.metamodel.mapping.internal.GeneratedValuesProcessor;
import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
@ -242,7 +244,6 @@ import org.hibernate.type.AnyType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.BasicType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
@ -378,8 +379,8 @@ public abstract class AbstractEntityPersister
private String[] sqlUpdateStrings;
private String[] sqlLazyUpdateStrings;
private String sqlInsertGeneratedValuesSelectString;
private String sqlUpdateGeneratedValuesSelectString;
private GeneratedValuesProcessor insertGeneratedValuesProcessor;
private GeneratedValuesProcessor updateGeneratedValuesProcessor;
//Custom SQL (would be better if these were private)
protected boolean[] insertCallable;
@ -2008,12 +2009,8 @@ public abstract class AbstractEntityPersister
return propertyUniqueness;
}
public String generateInsertGeneratedValuesSelectString() {
return generateGeneratedValuesSelectString( GenerationTiming.INSERT );
}
public String generateUpdateGeneratedValuesSelectString() {
return generateGeneratedValuesSelectString( GenerationTiming.ALWAYS );
private GeneratedValuesProcessor createGeneratedValuesProcessor(GenerationTiming timing) {
return new GeneratedValuesProcessor( this, timing, getFactory() );
}
private String generateGeneratedValuesSelectString(final GenerationTiming generationTimingToMatch) {
@ -2899,35 +2896,58 @@ public abstract class AbstractEntityPersister
final int j,
final Object[] oldFields,
final boolean useRowId) {
final Update update = createUpdate().setTableName( getTableName( j ) );
Update update = createUpdate().setTableName( getTableName( j ) );
final MutableBoolean hasColumnsRef = new MutableBoolean();
forEachAttributeMapping( (index, attributeMapping) -> {
if ( isPropertyOfTable( index, j ) ) {
// `attributeMapping` is an attribute of the table we are updating
boolean hasColumns = false;
for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
if ( includeProperty[i] && isPropertyOfTable( i, j )
&& !lobProperties.contains( i ) ) {
// this is a property of the table, which we are updating
update.addColumns(
getPropertyColumnNames( i ),
propertyColumnUpdateable[i], propertyColumnWriters[i]
);
hasColumns = hasColumns || getPropertyColumnSpan( i ) > 0;
if ( ! lobProperties.contains( index ) ) {
// HHH-4635
// Oracle expects all Lob properties to be last in inserts
// and updates. Insert them at the end - see below
if ( includeProperty[ index ] ) {
update.addColumns(
getPropertyColumnNames( index ),
propertyColumnUpdateable[index ],
propertyColumnWriters[index]
);
hasColumnsRef.setValue( true );
}
else {
final ValueGeneration valueGeneration = attributeMapping.getValueGeneration();
if ( valueGeneration.getGenerationTiming().includesUpdate()
&& valueGeneration.getValueGenerator() == null
&& valueGeneration.referenceColumnInSql() ) {
update.addColumns(
getPropertyColumnNames( index ),
new boolean[] { true },
new String[] { valueGeneration.getDatabaseGeneratedReferencedColumnValue() }
);
hasColumnsRef.setValue( true );
}
}
}
}
}
// HHH-4635
// Oracle expects all Lob properties to be last in inserts
// and updates. Insert them at the end.
for ( int i : lobProperties ) {
if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
// this property belongs on the table and is to be inserted
update.addColumns(
getPropertyColumnNames( i ),
propertyColumnUpdateable[i], propertyColumnWriters[i]
);
hasColumns = true;
// HHH-4635
// Oracle expects all Lob properties to be last in inserts
// and updates. Insert them at the end.
for ( int i : lobProperties ) {
if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
// this property belongs on the table and is to be inserted
update.addColumns(
getPropertyColumnNames( i ),
propertyColumnUpdateable[i], propertyColumnWriters[i]
);
hasColumnsRef.setValue( true );
}
}
}
} );
// select the correct row by either pk or row id
if ( useRowId ) {
@ -2943,7 +2963,7 @@ public abstract class AbstractEntityPersister
// check it (unless this is a "generated" version column)!
if ( checkVersion( includeProperty ) ) {
update.setVersionColumnName( getVersionColumnName() );
hasColumns = true;
hasColumnsRef.setValue( true );
}
}
else if ( isAllOrDirtyOptLocking() && oldFields != null ) {
@ -2984,7 +3004,7 @@ public abstract class AbstractEntityPersister
update.setComment( "update " + getEntityName() );
}
return hasColumns ? update.toStatementString() : null;
return hasColumnsRef.getValue() ? update.toStatementString() : null;
}
public final boolean checkVersion(final boolean[] includeProperty) {
@ -3000,6 +3020,9 @@ public abstract class AbstractEntityPersister
return generateInsertString( identityInsert, includeProperty, 0 );
}
private static final boolean[] SINGLE_TRUE = new boolean[] { true };
private static final boolean[] SINGLE_FALSE = new boolean[] { false };
/**
* Generate the SQL that inserts a row
*/
@ -3008,47 +3031,54 @@ public abstract class AbstractEntityPersister
// todo : remove the identityInsert param and variations;
// identity-insert strings are now generated from generateIdentityInsertString()
Insert insert = createInsert().setTableName( getTableName( j ) );
final Insert insert = createInsert().setTableName( getTableName( j ) );
// add normal properties
for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
// the incoming 'includeProperty' array only accounts for insertable defined at the root level, it
// does not account for partially generated composites etc. We also need to account for generation
// values
if ( isPropertyOfTable( i, j ) ) {
if ( !lobProperties.contains( i ) ) {
final InDatabaseValueGenerationStrategy generationStrategy = entityMetamodel.getInDatabaseValueGenerationStrategies()[i];
if ( generationStrategy != null && generationStrategy.getGenerationTiming().includesInsert() ) {
if ( generationStrategy.referenceColumnsInSql() ) {
final String[] values;
if ( generationStrategy.getReferencedColumnValues() == null ) {
values = propertyColumnWriters[i];
}
else {
final int numberOfColumns = propertyColumnWriters[i].length;
values = new String[numberOfColumns];
for ( int x = 0; x < numberOfColumns; x++ ) {
if ( generationStrategy.getReferencedColumnValues()[x] != null ) {
values[x] = generationStrategy.getReferencedColumnValues()[x];
}
else {
values[x] = propertyColumnWriters[i][x];
}
}
}
insert.addColumns( getPropertyColumnNames( i ), propertyColumnInsertable[i], values );
}
}
else if ( includeProperty[i] ) {
forEachAttributeMapping( (index, attributeMapping) -> {
if ( isPropertyOfTable( index, j ) ) {
// `attributeMapping` is an attribute of the table we are updating
if ( ! lobProperties.contains( index ) ) {
// HHH-4635
// Oracle expects all Lob properties to be last in inserts
// and updates. Insert them at the end - see below
if ( includeProperty[ index ] ) {
insert.addColumns(
getPropertyColumnNames( i ),
propertyColumnInsertable[i],
propertyColumnWriters[i]
getPropertyColumnNames( index ),
propertyColumnInsertable[index ],
propertyColumnWriters[index]
);
}
else {
final ValueGeneration valueGeneration = attributeMapping.getValueGeneration();
if ( valueGeneration.getGenerationTiming().includesInsert()
&& valueGeneration.getValueGenerator() == null
&& valueGeneration.referenceColumnInSql() ) {
insert.addColumns(
getPropertyColumnNames( index ),
SINGLE_TRUE,
new String[] { valueGeneration.getDatabaseGeneratedReferencedColumnValue() }
);
}
}
}
}
}
// HHH-4635
// Oracle expects all Lob properties to be last in inserts
// and updates. Insert them at the end.
for ( int i : lobProperties ) {
if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
// this property belongs on the table and is to be inserted
insert.addColumns(
getPropertyColumnNames( i ),
propertyColumnInsertable[i],
propertyColumnWriters[i]
);
}
}
} );
// add the discriminator
if ( j == 0 ) {
@ -4168,12 +4198,6 @@ public abstract class AbstractEntityPersister
if ( sqlLazyUpdateByRowIdString != null ) {
LOG.debugf( " Update by row id (non-lazy fields): %s", sqlLazyUpdateByRowIdString );
}
if ( sqlInsertGeneratedValuesSelectString != null ) {
LOG.debugf( " Insert-generated property select: %s", sqlInsertGeneratedValuesSelectString );
}
if ( sqlUpdateGeneratedValuesSelectString != null ) {
LOG.debugf( " Update-generated property select: %s", sqlUpdateGeneratedValuesSelectString );
}
}
}
@ -4532,18 +4556,18 @@ public abstract class AbstractEntityPersister
generateUpdateString( getNonLazyPropertyUpdateability(), 0, true );
for ( int j = 0; j < joinSpan; j++ ) {
sqlInsertStrings[j] = customSQLInsert[j] == null ?
generateInsertString( getPropertyInsertability(), j ) :
substituteBrackets( customSQLInsert[j]);
sqlUpdateStrings[j] = customSQLUpdate[j] == null ?
generateUpdateString( getPropertyUpdateability(), j, false ) :
substituteBrackets( customSQLUpdate[j]);
sqlLazyUpdateStrings[j] = customSQLUpdate[j] == null ?
generateUpdateString( getNonLazyPropertyUpdateability(), j, false ) :
substituteBrackets( customSQLUpdate[j]);
sqlDeleteStrings[j] = customSQLDelete[j] == null ?
generateDeleteString( j ) :
substituteBrackets( customSQLDelete[j]);
sqlInsertStrings[j] = customSQLInsert[j] == null
? generateInsertString( getPropertyInsertability(), j )
: substituteBrackets( customSQLInsert[j]);
sqlUpdateStrings[j] = customSQLUpdate[j] == null
? generateUpdateString( getPropertyUpdateability(), j, false )
: substituteBrackets( customSQLUpdate[j]);
sqlLazyUpdateStrings[j] = customSQLUpdate[j] == null
? generateUpdateString( getNonLazyPropertyUpdateability(), j, false )
: substituteBrackets( customSQLUpdate[j]);
sqlDeleteStrings[j] = customSQLDelete[j] == null
? generateDeleteString( j )
: substituteBrackets( customSQLDelete[j]);
}
tableHasColumns = new boolean[joinSpan];
@ -4555,12 +4579,6 @@ public abstract class AbstractEntityPersister
sqlSnapshotSelectString = generateSnapshotSelectString();
sqlLazySelectStringsByFetchGroup = generateLazySelectStringsByFetchGroup();
sqlVersionSelectString = generateSelectVersionString();
if ( hasInsertGeneratedProperties() ) {
sqlInsertGeneratedValuesSelectString = generateInsertGeneratedValuesSelectString();
}
if ( hasUpdateGeneratedProperties() ) {
sqlUpdateGeneratedValuesSelectString = generateUpdateGeneratedValuesSelectString();
}
if ( isIdentifierAssignedByInsert() ) {
identityDelegate = ( (PostInsertIdentifierGenerator) getIdentifierGenerator() )
.getInsertGeneratedIdentifierDelegate(
@ -5593,16 +5611,15 @@ public abstract class AbstractEntityPersister
Object entity,
Object[] state,
SharedSessionContractImplementor session) {
if ( !hasInsertGeneratedProperties() ) {
throw new AssertionFailure( "no insert-generated properties" );
if ( insertGeneratedValuesProcessor == null ) {
throw new UnsupportedOperationException( "Entity has no insert-generated properties - `" + getEntityName() + "`" );
}
processGeneratedProperties(
id,
insertGeneratedValuesProcessor.processGeneratedValues(
entity,
id,
state,
session,
sqlInsertGeneratedValuesSelectString,
GenerationTiming.INSERT
session
);
}
@ -5611,112 +5628,17 @@ public abstract class AbstractEntityPersister
Object entity,
Object[] state,
SharedSessionContractImplementor session) {
if ( !hasUpdateGeneratedProperties() ) {
throw new AssertionFailure( "no update-generated properties" );
if ( updateGeneratedValuesProcessor == null ) {
throw new AssertionFailure( "Entity has no update-generated properties - `" + getEntityName() + "`" );
}
processGeneratedProperties(
id,
updateGeneratedValuesProcessor.processGeneratedValues(
entity,
id,
state,
session,
sqlUpdateGeneratedValuesSelectString,
GenerationTiming.ALWAYS
session
);
}
private void processGeneratedProperties(
Object id,
Object entity,
Object[] state,
SharedSessionContractImplementor session,
String selectionSQL,
GenerationTiming matchTiming) {
// force immediate execution of the insert batch (if one)
session.getJdbcCoordinator().executeBatch();
try {
PreparedStatement ps = session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( selectionSQL );
try {
getIdentifierType().nullSafeSet( ps, id, 1, session );
ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( ps );
try {
if ( !rs.next() ) {
throw new HibernateException(
"Unable to locate row for retrieval of generated properties: " +
MessageHelper.infoString( this, id, getFactory() )
);
}
int propertyIndex = -1;
for ( NonIdentifierAttribute attribute : entityMetamodel.getProperties() ) {
propertyIndex++;
if ( isValueGenerationRequired( attribute, matchTiming ) ) {
final Object hydratedState = attribute.getType().hydrate(
rs, getPropertyAliases(
"",
propertyIndex
), session, entity
);
state[propertyIndex] = attribute.getType().resolve( hydratedState, session, entity );
setPropertyValue( entity, propertyIndex, state[propertyIndex] );
}
}
// for ( int i = 0; i < getPropertySpan(); i++ ) {
// if ( includeds[i] != ValueInclusion.NONE ) {
// Object hydratedState = getPropertyTypes()[i].hydrate( rs, getPropertyAliases( "", i ), session, entity );
// state[i] = getPropertyTypes()[i].resolve( hydratedState, session, entity );
// setPropertyValue( entity, i, state[i] );
// }
// }
}
finally {
if ( rs != null ) {
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( rs, ps );
}
}
}
finally {
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( ps );
session.getJdbcCoordinator().afterStatementExecution();
}
}
catch (SQLException e) {
throw getFactory().getJdbcServices().getSqlExceptionHelper().convert(
e,
"unable to select generated column values",
selectionSQL
);
}
}
public static boolean isValueGenerationRequired(NonIdentifierAttribute attribute, GenerationTiming matchTiming) {
if ( attribute.getType() instanceof ComponentType) {
final ComponentType type = (ComponentType) attribute.getType();
for ( ValueGeneration valueGenerationStrategy : type.getPropertyValueGenerationStrategies() ) {
if ( isReadRequired( valueGenerationStrategy, matchTiming ) ) {
return true;
}
}
return false;
}
else {
return isReadRequired( attribute.getValueGenerationStrategy(), matchTiming );
}
}
/**
* Whether the given value generation strategy requires to read the value from the database or not.
*/
private static boolean isReadRequired(ValueGeneration valueGeneration, GenerationTiming matchTiming) {
return valueGeneration != null
&& valueGeneration.getValueGenerator() == null
&& valueGeneration.timingMatches( matchTiming );
}
public String getIdentifierPropertyName() {
return entityMetamodel.getIdentifierProperty().getName();
}
@ -6130,6 +6052,13 @@ public abstract class AbstractEntityPersister
else {
naturalIdMapping = null;
}
if ( hasInsertGeneratedProperties() ) {
insertGeneratedValuesProcessor = createGeneratedValuesProcessor( GenerationTiming.INSERT );
}
if ( hasUpdateGeneratedProperties() ) {
updateGeneratedValuesProcessor = createGeneratedValuesProcessor( GenerationTiming.ALWAYS );
}
}
private NaturalIdMapping generateNaturalIdMapping(MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) {

View File

@ -71,9 +71,10 @@ public class SetterFieldImpl implements Setter {
e,
String.format(
Locale.ROOT,
"Could not set field value [%s] value by reflection : [%s.%s]",
"Could not set value [%s (`%s`)] by reflection",
value,
containerClass,
value == null ? "<null>" : value.getClass().getTypeName(),
containerClass == null ? "<dynamic>" : containerClass.getTypeName(),
propertyName
),
true,

View File

@ -20,6 +20,11 @@ public enum GenerationTiming {
public boolean includesUpdate() {
return false;
}
@Override
public boolean includes(GenerationTiming timing) {
return false;
}
},
INSERT {
@Override
@ -31,6 +36,11 @@ public enum GenerationTiming {
public boolean includesUpdate() {
return false;
}
@Override
public boolean includes(GenerationTiming timing) {
return timing.includesInsert();
}
},
ALWAYS {
@Override
@ -42,10 +52,16 @@ public enum GenerationTiming {
public boolean includesUpdate() {
return true;
}
@Override
public boolean includes(GenerationTiming timing) {
return timing != NEVER;
}
};
public abstract boolean includesInsert();
public abstract boolean includesUpdate();
public abstract boolean includes(GenerationTiming timing);
public static GenerationTiming parseFromName(String name) {
if ( "insert".equalsIgnoreCase( name ) ) {

View File

@ -109,8 +109,7 @@ import org.hibernate.HibernateException;
@SuppressWarnings("unchecked")
public static <T> ValueGenerator<T> get(final Class<T> type) {
final ValueGenerator<?> valueGeneratorSupplier = generators.get(
type );
final ValueGenerator<?> valueGeneratorSupplier = generators.get( type );
if ( Objects.isNull( valueGeneratorSupplier ) ) {
throw new HibernateException(
"Unsupported property type [" + type.getName() + "] for @CreationTimestamp or @UpdateTimestamp generator annotation" );

View File

@ -19,7 +19,7 @@ public interface ValueGeneration extends Serializable {
*
* @return When the value is generated.
*/
public GenerationTiming getGenerationTiming();
GenerationTiming getGenerationTiming();
/**
* Obtain the in-VM value generator.
@ -29,7 +29,7 @@ public interface ValueGeneration extends Serializable {
*
* @return The strategy for performing in-VM value generation
*/
public ValueGenerator<?> getValueGenerator();
ValueGenerator<?> getValueGenerator();
/**
* For values which are generated in the database ({@link #getValueGenerator()} == {@code null}), should the
@ -39,7 +39,7 @@ public interface ValueGeneration extends Serializable {
*
* @return {@code true} indicates the column should be included in the SQL.
*/
public boolean referenceColumnInSql();
boolean referenceColumnInSql();
/**
* For values which are generated in the database ({@link #getValueGenerator} == {@code null}), if the
@ -52,7 +52,7 @@ public interface ValueGeneration extends Serializable {
*
* @return The column value to be used in the SQL.
*/
public String getDatabaseGeneratedReferencedColumnValue();
String getDatabaseGeneratedReferencedColumnValue();
/**
* Does this value generation occur with the given timing?
@ -61,5 +61,6 @@ public interface ValueGeneration extends Serializable {
GenerationTiming generationTiming = getGenerationTiming();
return timing == GenerationTiming.INSERT && generationTiming.includesInsert()
|| timing == GenerationTiming.ALWAYS && generationTiming.includesUpdate();
// return timing.includes( getGenerationTiming() );
}
}

View File

@ -289,7 +289,7 @@ public abstract class AbstractStandardBasicType<T>
protected final T nullSafeGet(ResultSet rs, String name, WrapperOptions options) throws SQLException {
// return remapSqlTypeDescriptor( options ).getExtractor( javaTypeDescriptor ).extract( rs, name, options );
throw new NotYetImplementedFor6Exception( getClass() );
throw new UnsupportedOperationException( "Reading JDBC results by name/alias is no longer supported (" + getClass().getTypeName() + ")" );
}
public Object get(ResultSet rs, String name, SharedSessionContractImplementor session) throws HibernateException, SQLException {

View File

@ -1,10 +1,10 @@
/*
* 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>.
* 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.test.generated;
package org.hibernate.orm.test.mapping.generated;
import org.junit.Test;
import org.hibernate.Session;

View File

@ -0,0 +1,123 @@
/*
* 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.mapping.generated;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.NotImplementedYet;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Baseline testing of typically generated value types - mainly temporal types
*
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = ComplexValueGenerationBaselineTests.NonAuditedEntity.class )
@SessionFactory
@NotImplementedYet( strict = false, reason = "Support for `java.sql.Date` and `java.sql.Time` is currently fubar" )
public class ComplexValueGenerationBaselineTests {
@Test
public void testLoading(SessionFactoryScope scope) {
// some of the generated-value tests show problems loading entities with attributes of
// java.sql.Date type. Make sure we can load such an entity without generation involved
final NonAuditedEntity saved = scope.fromTransaction( (session) -> {
final NonAuditedEntity entity = new NonAuditedEntity( 1 );
session.persist( entity );
return entity;
} );
// lastly, make sure we can load it..
scope.inTransaction( (session) -> {
assertThat( session.get( NonAuditedEntity.class, 1 ) ).isNotNull();
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> session.createQuery( "delete NonAuditedEntity" ).executeUpdate() );
}
@Entity( name = "NonAuditedEntity" )
@Table( name = "ann_generated_complex_base" )
public static class NonAuditedEntity {
@Id
private Integer id;
private String name;
private String lastName;
private Date createdDate;
private Calendar alwaysDate;
private Date vmCreatedDate;
private Calendar vmCreatedCalendar;
private java.sql.Date vmCreatedSqlDate;
private Time vmCreatedSqlTime;
private Timestamp vmCreatedSqlTimestamp;
private Instant vmCreatedSqlInstant;
private LocalDate vmCreatedSqlLocalDate;
private LocalTime vmCreatedSqlLocalTime;
private LocalDateTime vmCreatedSqlLocalDateTime;
private MonthDay vmCreatedSqlMonthDay;
private OffsetDateTime vmCreatedSqlOffsetDateTime;
private OffsetTime vmCreatedSqlOffsetTime;
private Year vmCreatedSqlYear;
private YearMonth vmCreatedSqlYearMonth;
private ZonedDateTime vmCreatedSqlZonedDateTime;
private Timestamp updated;
private NonAuditedEntity() {
}
private NonAuditedEntity(Integer id) {
this.id = id;
name = "it";
createdDate = new Date();
alwaysDate = new GregorianCalendar();
vmCreatedDate = new Date();
vmCreatedCalendar = new GregorianCalendar();
vmCreatedSqlDate = new java.sql.Date( System.currentTimeMillis() );
vmCreatedSqlTime = new Time( System.currentTimeMillis() );
vmCreatedSqlTimestamp = new Timestamp( System.currentTimeMillis() );
vmCreatedSqlInstant = Instant.now();
vmCreatedSqlLocalDate = LocalDate.now();
vmCreatedSqlLocalTime = LocalTime.now();
vmCreatedSqlLocalDateTime = LocalDateTime.now();
vmCreatedSqlMonthDay = MonthDay.now();
vmCreatedSqlOffsetDateTime = OffsetDateTime.now();
vmCreatedSqlOffsetTime = OffsetTime.now();
vmCreatedSqlYear = Year.now();
vmCreatedSqlYearMonth = YearMonth.now();
vmCreatedSqlZonedDateTime = ZonedDateTime.now();
updated = new Timestamp( System.currentTimeMillis() );
}
}
}

View File

@ -0,0 +1,211 @@
/*
* 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.mapping.generated;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
import org.hibernate.annotations.GeneratorType;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.NotImplementedYet;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = ComplexValueGenerationTests.AuditedEntity.class )
@SessionFactory
@NotImplementedYet(
strict = false,
reason = "Support for `java.sql.Date` and `java.sql.Time` is currently fubar"
)
public class ComplexValueGenerationTests {
@Test
public void testGenerations(SessionFactoryScope scope) {
// first test creation
final AuditedEntity saved = scope.fromTransaction( (session) -> {
final AuditedEntity entity = new AuditedEntity( 1 );
assertThat( entity.createdDate ).isNull();
assertThat( entity.alwaysDate ).isNull();
assertThat( entity.vmCreatedDate ).isNull();
assertThat( entity.vmCreatedSqlDate ).isNull();
assertThat( entity.vmCreatedSqlTime ).isNull();
assertThat( entity.vmCreatedSqlTimestamp ).isNull();
assertThat( entity.vmCreatedSqlLocalDate ).isNull();
assertThat( entity.vmCreatedSqlLocalTime ).isNull();
assertThat( entity.vmCreatedSqlLocalDateTime ).isNull();
assertThat( entity.vmCreatedSqlMonthDay ).isNull();
assertThat( entity.vmCreatedSqlOffsetDateTime ).isNull();
assertThat( entity.vmCreatedSqlOffsetTime ).isNull();
assertThat( entity.vmCreatedSqlYear ).isNull();
assertThat( entity.vmCreatedSqlYearMonth ).isNull();
assertThat( entity.vmCreatedSqlZonedDateTime ).isNull();
session.persist( entity );
return entity;
} );
assertThat( saved ).isNotNull();
assertThat( saved.createdDate ).isNotNull();
assertThat( saved.alwaysDate ).isNotNull();
assertThat( saved.vmCreatedDate ).isNotNull();
assertThat( saved.vmCreatedSqlDate ).isNotNull();
assertThat( saved.vmCreatedSqlTime ).isNotNull();
assertThat( saved.vmCreatedSqlTimestamp ).isNotNull();
assertThat( saved.vmCreatedSqlLocalDate ).isNotNull();
assertThat( saved.vmCreatedSqlLocalTime ).isNotNull();
assertThat( saved.vmCreatedSqlLocalDateTime ).isNotNull();
assertThat( saved.vmCreatedSqlMonthDay ).isNotNull();
assertThat( saved.vmCreatedSqlOffsetDateTime ).isNotNull();
assertThat( saved.vmCreatedSqlOffsetTime ).isNotNull();
assertThat( saved.vmCreatedSqlYear ).isNotNull();
assertThat( saved.vmCreatedSqlYearMonth ).isNotNull();
assertThat( saved.vmCreatedSqlZonedDateTime ).isNotNull();
// next, try mutating
saved.lastName = "changed";
final AuditedEntity merged = scope.fromSession( (session) -> {
// next, try mutating
return (AuditedEntity) session.merge( saved );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdDate ).isNotNull();
assertThat( merged.alwaysDate ).isNotNull();
assertThat( merged.vmCreatedDate ).isNotNull();
assertThat( merged.vmCreatedSqlDate ).isNotNull();
assertThat( merged.vmCreatedSqlTime ).isNotNull();
assertThat( merged.vmCreatedSqlTimestamp ).isNotNull();
assertThat( merged.vmCreatedSqlLocalDate ).isNotNull();
assertThat( merged.vmCreatedSqlLocalTime ).isNotNull();
assertThat( merged.vmCreatedSqlLocalDateTime ).isNotNull();
assertThat( merged.vmCreatedSqlMonthDay ).isNotNull();
assertThat( merged.vmCreatedSqlOffsetDateTime ).isNotNull();
assertThat( merged.vmCreatedSqlOffsetTime ).isNotNull();
assertThat( merged.vmCreatedSqlYear ).isNotNull();
assertThat( merged.vmCreatedSqlYearMonth ).isNotNull();
assertThat( merged.vmCreatedSqlZonedDateTime ).isNotNull();
// lastly, make sure we can load it..
scope.inTransaction( (session) -> {
assertThat( session.get( AuditedEntity.class, 1 ) ).isNotNull();
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> session.createQuery( "delete AuditedEntity" ).executeUpdate() );
}
@Entity( name = "AuditedEntity" )
@Table( name = "ann_generated_complex" )
public static class AuditedEntity {
@Id
private Integer id;
@Generated( GenerationTime.INSERT )
@ColumnDefault( "CURRENT_TIMESTAMP" )
@Column( nullable = false )
private Date createdDate;
@Generated( GenerationTime.ALWAYS )
@ColumnDefault( "CURRENT_TIMESTAMP" )
@Column( nullable = false )
private Calendar alwaysDate;
@CreationTimestamp
private Date vmCreatedDate;
@CreationTimestamp
private Calendar vmCreatedCalendar;
@CreationTimestamp
private java.sql.Date vmCreatedSqlDate;
@CreationTimestamp
private Time vmCreatedSqlTime;
@CreationTimestamp
private Timestamp vmCreatedSqlTimestamp;
@CreationTimestamp
private Instant vmCreatedSqlInstant;
@CreationTimestamp
private LocalDate vmCreatedSqlLocalDate;
@CreationTimestamp
private LocalTime vmCreatedSqlLocalTime;
@CreationTimestamp
private LocalDateTime vmCreatedSqlLocalDateTime;
@CreationTimestamp
private MonthDay vmCreatedSqlMonthDay;
@CreationTimestamp
private OffsetDateTime vmCreatedSqlOffsetDateTime;
@CreationTimestamp
private OffsetTime vmCreatedSqlOffsetTime;
@CreationTimestamp
private Year vmCreatedSqlYear;
@CreationTimestamp
private YearMonth vmCreatedSqlYearMonth;
@CreationTimestamp
private ZonedDateTime vmCreatedSqlZonedDateTime;
@UpdateTimestamp
private Timestamp updated;
@GeneratorType( type = DefaultGeneratedValueTest.MyVmValueGenerator.class, when = GenerationTime.INSERT )
private String name;
@SuppressWarnings("unused")
private String lastName;
private AuditedEntity() {
}
private AuditedEntity(Integer id) {
this.id = id;
}
}
}

View File

@ -1,10 +1,10 @@
/*
* 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>.
* 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.test.generated;
package org.hibernate.orm.test.mapping.generated;
/**

View File

@ -1,10 +1,10 @@
/*
* 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>.
* 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.test.generated;
package org.hibernate.orm.test.mapping.generated;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -43,10 +43,12 @@ import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGenerator;
import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.orm.junit.NotImplementedYet;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
@ -58,6 +60,14 @@ import static org.junit.Assert.assertTrue;
@SkipForDialect(value = SybaseDialect.class, comment = "CURRENT_TIMESTAMP not supported as default value in Sybase")
@SkipForDialect(value = MySQLDialect.class, comment = "See HHH-10196", strictMatching = false)
@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class)
@FailureExpected(
jiraKey = "",
message = "Support for `java.sql.Date` and `java.sql.Time` is currently fubar"
)
//@NotImplementedYet(
// strict = false,
// reason = "Support for `java.sql.Date` and `java.sql.Time` is currently fubar"
//)
public class DefaultGeneratedValueIdentityTest extends BaseCoreFunctionalTestCase {
@Override

View File

@ -0,0 +1,261 @@
/*
* 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.mapping.generated;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.Session;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
import org.hibernate.annotations.GeneratorType;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.SybaseDialect;
import org.hibernate.tuple.ValueGenerator;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.NotImplementedYet;
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.AfterEach;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* Test for the generation of column values using different
* {@link org.hibernate.tuple.ValueGeneration} implementations.
*
* @author Steve Ebersole
* @author Gunnar Morling
*/
@SkipForDialect( dialectClass = SybaseDialect.class, reason = "CURRENT_TIMESTAMP not supported as default value in Sybase" )
@SkipForDialect( dialectClass = MySQLDialect.class, reason = "See HHH-10196" )
@NotImplementedYet(
strict = false,
reason = "Support for `java.sql.Date` and `java.sql.Time` is currently fubar"
)
@DomainModel( annotatedClasses = DefaultGeneratedValueTest.TheEntity.class )
@SessionFactory
public class DefaultGeneratedValueTest {
@Test
@TestForIssue( jiraKey = "HHH-2907" )
public void testGeneration(SessionFactoryScope scope) {
final TheEntity created = scope.fromTransaction( (s) -> {
final TheEntity theEntity = new TheEntity( 1 );
assertNull( theEntity.createdDate );
assertNull( theEntity.alwaysDate );
assertNull( theEntity.vmCreatedDate );
assertNull( theEntity.vmCreatedSqlDate );
assertNull( theEntity.vmCreatedSqlTime );
assertNull( theEntity.vmCreatedSqlTimestamp );
assertNull( theEntity.vmCreatedSqlLocalDate );
assertNull( theEntity.vmCreatedSqlLocalTime );
assertNull( theEntity.vmCreatedSqlLocalDateTime );
assertNull( theEntity.vmCreatedSqlMonthDay );
assertNull( theEntity.vmCreatedSqlOffsetDateTime );
assertNull( theEntity.vmCreatedSqlOffsetTime );
assertNull( theEntity.vmCreatedSqlYear );
assertNull( theEntity.vmCreatedSqlYearMonth );
assertNull( theEntity.vmCreatedSqlZonedDateTime );
assertNull( theEntity.name );
s.save( theEntity );
//TODO: Actually the values should be non-null after save
assertNull( theEntity.createdDate );
assertNull( theEntity.alwaysDate );
assertNull( theEntity.vmCreatedDate );
assertNull( theEntity.vmCreatedSqlDate );
assertNull( theEntity.vmCreatedSqlTime );
assertNull( theEntity.vmCreatedSqlTimestamp );
assertNull( theEntity.vmCreatedSqlLocalDate );
assertNull( theEntity.vmCreatedSqlLocalTime );
assertNull( theEntity.vmCreatedSqlLocalDateTime );
assertNull( theEntity.vmCreatedSqlMonthDay );
assertNull( theEntity.vmCreatedSqlOffsetDateTime );
assertNull( theEntity.vmCreatedSqlOffsetTime );
assertNull( theEntity.vmCreatedSqlYear );
assertNull( theEntity.vmCreatedSqlYearMonth );
assertNull( theEntity.vmCreatedSqlZonedDateTime );
assertNull( theEntity.name );
return theEntity;
} );
assertNotNull( created.createdDate );
assertNotNull( created.alwaysDate );
assertEquals( "Bob", created.name );
scope.inTransaction( (s) -> {
final TheEntity theEntity = s.get( TheEntity.class, 1 );
assertNotNull( theEntity.createdDate );
assertNotNull( theEntity.alwaysDate );
assertNotNull( theEntity.vmCreatedDate );
assertNotNull( theEntity.vmCreatedSqlDate );
assertNotNull( theEntity.vmCreatedSqlTime );
assertNotNull( theEntity.vmCreatedSqlTimestamp );
assertNotNull( theEntity.vmCreatedSqlLocalDate );
assertNotNull( theEntity.vmCreatedSqlLocalTime );
assertNotNull( theEntity.vmCreatedSqlLocalDateTime );
assertNotNull( theEntity.vmCreatedSqlMonthDay );
assertNotNull( theEntity.vmCreatedSqlOffsetDateTime );
assertNotNull( theEntity.vmCreatedSqlOffsetTime );
assertNotNull( theEntity.vmCreatedSqlYear );
assertNotNull( theEntity.vmCreatedSqlYearMonth );
assertNotNull( theEntity.vmCreatedSqlZonedDateTime );
assertEquals( "Bob", theEntity.name );
s.delete( theEntity );
} );
}
@Test
@TestForIssue(jiraKey = "HHH-2907")
public void testUpdateTimestampGeneration(SessionFactoryScope scope) {
final TheEntity created = scope.fromTransaction( (s) -> {
TheEntity theEntity = new TheEntity( 1 );
assertNull( theEntity.updated );
s.save( theEntity );
assertNull( theEntity.updated );
return theEntity;
} );
assertNotNull( created.vmCreatedSqlTimestamp );
assertNotNull( created.updated );
scope.inTransaction( (s) -> {
final TheEntity theEntity = s.get( TheEntity.class, 1 );
theEntity.lastName = "Smith";
} );
scope.inTransaction( (s) -> {
final TheEntity theEntity = s.get( TheEntity.class, 1 );
assertEquals( "Creation timestamp should not change on update", created, theEntity.vmCreatedSqlTimestamp );
assertTrue( "Update timestamp should have changed due to update", theEntity.updated.after( created.updated ) );
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (s) -> s.createQuery( "delete TheEntity" ).executeUpdate() );
}
@Entity( name = "TheEntity" )
@Table( name = "T_ENT_GEN_DEF" )
public static class TheEntity {
@Id
private Integer id;
@Generated( GenerationTime.INSERT )
@ColumnDefault( "CURRENT_TIMESTAMP" )
@Column( nullable = false )
private Date createdDate;
@Generated( GenerationTime.ALWAYS )
@ColumnDefault( "CURRENT_TIMESTAMP" )
@Column( nullable = false )
private Calendar alwaysDate;
@CreationTimestamp
private Date vmCreatedDate;
@CreationTimestamp
private Calendar vmCreatedCalendar;
@CreationTimestamp
private java.sql.Date vmCreatedSqlDate;
@CreationTimestamp
private Time vmCreatedSqlTime;
@CreationTimestamp
private Timestamp vmCreatedSqlTimestamp;
@CreationTimestamp
private Instant vmCreatedSqlInstant;
@CreationTimestamp
private LocalDate vmCreatedSqlLocalDate;
@CreationTimestamp
private LocalTime vmCreatedSqlLocalTime;
@CreationTimestamp
private LocalDateTime vmCreatedSqlLocalDateTime;
@CreationTimestamp
private MonthDay vmCreatedSqlMonthDay;
@CreationTimestamp
private OffsetDateTime vmCreatedSqlOffsetDateTime;
@CreationTimestamp
private OffsetTime vmCreatedSqlOffsetTime;
@CreationTimestamp
private Year vmCreatedSqlYear;
@CreationTimestamp
private YearMonth vmCreatedSqlYearMonth;
@CreationTimestamp
private ZonedDateTime vmCreatedSqlZonedDateTime;
@UpdateTimestamp
private Timestamp updated;
@GeneratorType( type = MyVmValueGenerator.class, when = GenerationTime.INSERT )
private String name;
@SuppressWarnings("unused")
private String lastName;
private TheEntity() {
}
private TheEntity(Integer id) {
this.id = id;
}
}
public static class MyVmValueGenerator implements ValueGenerator<String> {
@Override
public String generateValue(Session session, Object owner) {
return "Bob";
}
}
}

View File

@ -0,0 +1,93 @@
/*
* 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.mapping.generated;
import java.time.Instant;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.NotImplementedYet;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = GeneratedAnnotationTests.AuditedEntity.class )
@SessionFactory
@NotImplementedYet(
strict = false,
reason = "Currently `@Generated` will never work unless a db trigger is used to set the 'update ts'"
+ "; see `GeneratedValueGenerator#referenceColumnInSql`" +
" and `GeneratedValueGenerator#getDatabaseGeneratedReferencedColumnValue`"
)
public class GeneratedAnnotationTests {
@Test
public void test(SessionFactoryScope scope) {
final AuditedEntity created = scope.fromTransaction( (session) -> {
final AuditedEntity entity = new AuditedEntity( 1, "tsifr" );
session.persist( entity );
return entity;
} );
assertThat( created.createdAt ).isNotNull();
assertThat( created.lastUpdatedAt ).isNotNull();
assertThat( created.lastUpdatedAt ).isEqualTo(created.createdAt );
created.name = "changed";
// then changing
final AuditedEntity merged = scope.fromTransaction( (session) -> {
return (AuditedEntity) session.merge( created );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdAt ).isNotNull();
assertThat( merged.lastUpdatedAt ).isNotNull();
assertThat( merged.lastUpdatedAt ).isNotEqualTo( merged.createdAt );
// lastly, make sure we can load it..
final AuditedEntity loaded = scope.fromTransaction( (session) -> {
return session.get( AuditedEntity.class, 1 );
} );
assertThat( loaded ).isNotNull();
assertThat( loaded.createdAt ).isEqualTo( merged.createdAt );
assertThat( loaded.lastUpdatedAt ).isEqualTo( merged.lastUpdatedAt );
}
@Entity( name = "gen_ann_baseline" )
@Table( name = "" )
public static class AuditedEntity {
@Id
public Integer id;
public String name;
@Generated( GenerationTime.INSERT )
@ColumnDefault( "current_timestamp" )
public Instant createdAt;
@Generated( GenerationTime.ALWAYS )
@ColumnDefault( "current_timestamp" )
public Instant lastUpdatedAt;
public AuditedEntity() {
}
public AuditedEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -1,12 +1,12 @@
/*
* 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>.
* 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
*/
// $Id: GeneratedPropertyEntity.java 7800 2005-08-10 12:13:00Z steveebersole $
package org.hibernate.test.generated;
package org.hibernate.orm.test.mapping.generated;
/**

View File

@ -0,0 +1,88 @@
/*
* 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.mapping.generated;
import java.time.Instant;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.orm.test.mapping.generated.temporals.CurrentTimestamp;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = GeneratedValueAnnotationTests.AuditedEntity.class )
@SessionFactory
public class GeneratedValueAnnotationTests {
@Test
public void test(SessionFactoryScope scope) {
final AuditedEntity created = scope.fromTransaction( (session) -> {
final AuditedEntity entity = new AuditedEntity( 1, "tsifr" );
session.persist( entity );
return entity;
} );
assertThat( created.createdAt ).isNotNull();
assertThat( created.lastUpdatedAt ).isNotNull();
assertThat( created.lastUpdatedAt ).isEqualTo(created.createdAt );
created.name = "first";
// then changing
final AuditedEntity merged = scope.fromTransaction( (session) -> {
return (AuditedEntity) session.merge( created );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdAt ).isNotNull();
assertThat( merged.createdAt ).isEqualTo( created.createdAt );
assertThat( merged.lastUpdatedAt ).isNotNull();
assertThat( merged.lastUpdatedAt ).isNotEqualTo( merged.createdAt );
assertThat( merged.lastUpdatedAt ).isNotEqualTo( created.createdAt );
// lastly, make sure we can load it..
final AuditedEntity loaded = scope.fromTransaction( (session) -> {
return session.get( AuditedEntity.class, 1 );
} );
assertThat( loaded ).isNotNull();
assertThat( loaded.createdAt ).isEqualTo( merged.createdAt );
assertThat( loaded.lastUpdatedAt ).isEqualTo( merged.lastUpdatedAt );
}
@Entity( name = "gen_ann_baseline" )
@Table( name = "" )
public static class AuditedEntity {
@Id
public Integer id;
public String name;
@CurrentTimestamp( timing = GenerationTiming.INSERT )
public Instant createdAt;
@CurrentTimestamp( timing = GenerationTiming.ALWAYS )
public Instant lastUpdatedAt;
public AuditedEntity() {
}
public AuditedEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -0,0 +1,100 @@
/*
* 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.mapping.generated;
import java.sql.Timestamp;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.orm.test.mapping.generated.temporals.CurrentTimestamp;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for using {@link CreationTimestamp} and {@link UpdateTimestamp}
* annotations with Timestamp-valued attributes
*
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = InDbGenerationsWithAnnotationsTests.AuditedEntity.class )
@SessionFactory
public class InDbGenerationsWithAnnotationsTests {
@Test
public void testGenerations(SessionFactoryScope scope) {
scope.inSession( (session) -> {
// first creation
final AuditedEntity saved = scope.fromTransaction( session, (s) -> {
final AuditedEntity entity = new AuditedEntity( 1, "it" );
session.persist( entity );
return entity;
} );
assertThat( saved ).isNotNull();
assertThat( saved.createdOn ).isNotNull();
assertThat( saved.lastUpdatedOn ).isNotNull();
saved.name = "changed";
// then changing
final AuditedEntity merged = scope.fromTransaction( session, (s) -> {
return (AuditedEntity) session.merge( saved );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdOn ).isNotNull();
assertThat( merged.lastUpdatedOn ).isNotNull();
assertThat( merged.lastUpdatedOn ).isNotEqualTo( merged.createdOn );
// lastly, make sure we can load it..
final AuditedEntity loaded = scope.fromTransaction( session, (s) -> {
return session.get( AuditedEntity.class, 1 );
} );
assertThat( loaded ).isNotNull();
assertThat( loaded.createdOn ).isEqualTo( merged.createdOn );
assertThat( loaded.lastUpdatedOn ).isEqualTo( merged.lastUpdatedOn );
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> session.createQuery( "delete AuditedEntity" ).executeUpdate() );
}
@Entity( name = "AuditedEntity" )
@Table( name = "ann_generated_simple" )
public static class AuditedEntity {
@Id
public Integer id;
@Basic
public String name;
@CurrentTimestamp( timing = GenerationTiming.INSERT )
public Timestamp createdOn;
@CurrentTimestamp( timing = GenerationTiming.ALWAYS )
public Timestamp lastUpdatedOn;
public AuditedEntity() {
}
public AuditedEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -0,0 +1,97 @@
/*
* 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.mapping.generated;
import java.sql.Timestamp;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for using {@link CreationTimestamp} and {@link UpdateTimestamp}
* annotations with Timestamp-valued attributes
*
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = InVmGenerationsWithAnnotationsTests.AuditedEntity.class )
@SessionFactory
public class InVmGenerationsWithAnnotationsTests {
@Test
public void testGenerations(SessionFactoryScope scope) {
scope.inSession( (session) -> {
// first creation
final AuditedEntity saved = scope.fromTransaction( session, (s) -> {
final AuditedEntity entity = new AuditedEntity( 1, "it" );
session.persist( entity );
return entity;
} );
assertThat( saved ).isNotNull();
assertThat( saved.createdOn ).isNotNull();
assertThat( saved.lastUpdatedOn ).isNotNull();
saved.name = "changed";
// then changing
final AuditedEntity merged = scope.fromTransaction( session, (s) -> {
return (AuditedEntity) session.merge( saved );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdOn ).isNotNull();
assertThat( merged.lastUpdatedOn ).isNotNull();
assertThat( merged.lastUpdatedOn ).isNotEqualTo( merged.createdOn );
// lastly, make sure we can load it..
final AuditedEntity loaded = scope.fromTransaction( session, (s) -> {
return session.get( AuditedEntity.class, 1 );
} );
assertThat( loaded ).isNotNull();
assertThat( loaded.createdOn ).isEqualTo( merged.createdOn );
assertThat( loaded.lastUpdatedOn ).isEqualTo( merged.lastUpdatedOn );
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> session.createQuery( "delete AuditedEntity" ).executeUpdate() );
}
@Entity( name = "AuditedEntity" )
@Table( name = "ann_generated_simple" )
public static class AuditedEntity {
@Id
public Integer id;
@Basic
public String name;
@CreationTimestamp
public Timestamp createdOn;
@UpdateTimestamp
public Timestamp lastUpdatedOn;
public AuditedEntity() {
}
public AuditedEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -0,0 +1,98 @@
/*
* 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.mapping.generated;
import java.sql.Date;
import java.sql.Timestamp;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for using {@link CreationTimestamp} and {@link UpdateTimestamp}
* annotations with Timestamp-valued attributes
*
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = InVmGenerationsWithAnnotationsWithMixedSqlTypesTests.AuditedEntity.class )
@SessionFactory
public class InVmGenerationsWithAnnotationsWithMixedSqlTypesTests {
@Test
public void testGenerations(SessionFactoryScope scope) {
scope.inSession( (session) -> {
// first creation
final AuditedEntity saved = scope.fromTransaction( session, (s) -> {
final AuditedEntity entity = new AuditedEntity( 1, "it" );
session.persist( entity );
return entity;
} );
assertThat( saved ).isNotNull();
assertThat( saved.createdOn ).isNotNull();
assertThat( saved.lastUpdatedOn ).isNotNull();
saved.name = "changed";
// then changing
final AuditedEntity merged = scope.fromTransaction( session, (s) -> {
return (AuditedEntity) session.merge( saved );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdOn ).isNotNull();
assertThat( merged.lastUpdatedOn ).isNotNull();
assertThat( merged.lastUpdatedOn ).isNotEqualTo( merged.createdOn );
// lastly, make sure we can load it..
final AuditedEntity loaded = scope.fromTransaction( session, (s) -> {
return session.get( AuditedEntity.class, 1 );
} );
assertThat( loaded ).isNotNull();
assertThat( loaded.createdOn ).isEqualTo( merged.createdOn );
assertThat( loaded.lastUpdatedOn ).isEqualTo( merged.lastUpdatedOn );
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> session.createQuery( "delete AuditedEntity" ).executeUpdate() );
}
@Entity( name = "AuditedEntity" )
@Table( name = "ann_generated_mixed_sql_type" )
public static class AuditedEntity {
@Id
public Integer id;
@Basic
public String name;
@CreationTimestamp
public Date createdOn;
@UpdateTimestamp
public Timestamp lastUpdatedOn;
public AuditedEntity() {
}
public AuditedEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -0,0 +1,97 @@
/*
* 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.mapping.generated;
import java.sql.Date;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for using {@link CreationTimestamp} and {@link UpdateTimestamp}
* annotations with Timestamp-valued attributes
*
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = InVmGenerationsWithAnnotationsWithSqlDateTests.AuditedEntity.class )
@SessionFactory
public class InVmGenerationsWithAnnotationsWithSqlDateTests {
@Test
public void testGenerations(SessionFactoryScope scope) {
scope.inSession( (session) -> {
// first creation
final AuditedEntity saved = scope.fromTransaction( session, (s) -> {
final AuditedEntity entity = new AuditedEntity( 1, "it" );
session.persist( entity );
return entity;
} );
assertThat( saved ).isNotNull();
assertThat( saved.createdOn ).isNotNull();
assertThat( saved.lastUpdatedOn ).isNotNull();
saved.name = "changed";
// then changing
final AuditedEntity merged = scope.fromTransaction( session, (s) -> {
return (AuditedEntity) session.merge( saved );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdOn ).isNotNull();
assertThat( merged.lastUpdatedOn ).isNotNull();
assertThat( merged.lastUpdatedOn ).isNotEqualTo( merged.createdOn );
// lastly, make sure we can load it..
final AuditedEntity loaded = scope.fromTransaction( session, (s) -> {
return session.get( AuditedEntity.class, 1 );
} );
assertThat( loaded ).isNotNull();
assertThat( loaded.createdOn ).isEqualTo( merged.createdOn );
assertThat( loaded.lastUpdatedOn ).isEqualTo( merged.lastUpdatedOn );
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> session.createQuery( "delete AuditedEntity" ).executeUpdate() );
}
@Entity( name = "AuditedEntity" )
@Table( name = "ann_generated_sql_date" )
public static class AuditedEntity {
@Id
public Integer id;
@Basic
public String name;
@CreationTimestamp
public Date createdOn;
@UpdateTimestamp
public Date lastUpdatedOn;
public AuditedEntity() {
}
public AuditedEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -0,0 +1,109 @@
/*
* 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.mapping.generated;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.Calendar;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for using {@link CreationTimestamp} and {@link UpdateTimestamp}
* annotations with Timestamp-valued attributes
*
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = InVmGenerationsWithMultipleAnnotationsTests.AuditedEntity.class )
@SessionFactory
public class InVmGenerationsWithMultipleAnnotationsTests {
@Test
public void testGenerations(SessionFactoryScope scope) {
scope.inSession( (session) -> {
// first creation
final AuditedEntity saved = scope.fromTransaction( session, (s) -> {
final AuditedEntity entity = new AuditedEntity( 1, "it" );
session.persist( entity );
return entity;
} );
assertThat( saved ).isNotNull();
assertThat( saved.createdOn ).isNotNull();
assertThat( saved.lastUpdatedOn ).isNotNull();
saved.name = "changed";
// then changing
final AuditedEntity merged = scope.fromTransaction( session, (s) -> {
return (AuditedEntity) session.merge( saved );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdOn ).isNotNull();
assertThat( merged.lastUpdatedOn ).isNotNull();
assertThat( merged.lastUpdatedOn ).isNotEqualTo( merged.createdOn );
// lastly, make sure we can load it..
final AuditedEntity loaded = scope.fromTransaction( session, (s) -> {
return session.get( AuditedEntity.class, 1 );
} );
assertThat( loaded ).isNotNull();
assertThat( loaded.createdOn ).isEqualTo( merged.createdOn );
assertThat( loaded.lastUpdatedOn ).isEqualTo( merged.lastUpdatedOn );
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> session.createQuery( "delete AuditedEntity" ).executeUpdate() );
}
@Entity( name = "AuditedEntity" )
@Table( name = "ann_generated_multiple" )
public static class AuditedEntity {
@Id
public Integer id;
@Basic
public String name;
@CreationTimestamp
public Date createdOn;
@CreationTimestamp
public Date createdOn2;
@CreationTimestamp
public Timestamp createdOn3;
@CreationTimestamp
public Calendar createdOn4;
@UpdateTimestamp
public Date lastUpdatedOn;
@UpdateTimestamp
public Timestamp lastUpdatedOn2;
public AuditedEntity() {
}
public AuditedEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -1,10 +1,10 @@
/*
* 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>.
* 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.test.generated;
package org.hibernate.orm.test.mapping.generated;
import org.junit.Test;

View File

@ -0,0 +1,78 @@
/*
* 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.mapping.generated;
import java.sql.Time;
import java.sql.Timestamp;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.NotImplementedYet;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = SimpleValueGenerationBaselineTests.NonAuditedEntity.class )
@SessionFactory
@NotImplementedYet( strict = false, reason = "Support for `java.sql.Date` and `java.sql.Time` is currently fubar" )
public class SimpleValueGenerationBaselineTests {
@Test
public void testLoading(SessionFactoryScope scope) {
// some of the generated-value tests show problems loading entities with attributes of
// java.sql.Date type. Make sure we can load such an entity without generation involved
final NonAuditedEntity saved = scope.fromTransaction( (session) -> {
final NonAuditedEntity entity = new NonAuditedEntity( 1 );
session.persist( entity );
return entity;
} );
// lastly, make sure we can load it..
scope.inTransaction( (session) -> {
assertThat( session.get( NonAuditedEntity.class, 1 ) ).isNotNull();
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> session.createQuery( "delete NonAuditedEntity" ).executeUpdate() );
}
@Entity( name = "NonAuditedEntity" )
@Table( name = "ann_generated_complex_base" )
public static class NonAuditedEntity {
@Id
private Integer id;
private String name;
private String lastName;
private java.sql.Date vmCreatedSqlDate;
private Time vmCreatedSqlTime;
private Timestamp vmCreatedSqlTimestamp;
private NonAuditedEntity() {
}
private NonAuditedEntity(Integer id) {
this.id = id;
name = "it";
vmCreatedSqlDate = new java.sql.Date( System.currentTimeMillis() );
vmCreatedSqlTime = new Time( System.currentTimeMillis() );
vmCreatedSqlTimestamp = new Timestamp( System.currentTimeMillis() );
}
}
}

View File

@ -1,10 +1,10 @@
/*
* 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>.
* 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.test.generated;
package org.hibernate.orm.test.mapping.generated;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.dialect.SybaseDialect;

View File

@ -1,10 +1,10 @@
/*
* 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>.
* 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.test.generated;
package org.hibernate.orm.test.mapping.generated;
import org.hibernate.dialect.Oracle9iDialect;
import org.hibernate.testing.RequiresDialect;

View File

@ -1,10 +1,10 @@
/*
* 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>.
* 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.test.generated;
package org.hibernate.orm.test.mapping.generated;
import org.hibernate.dialect.Oracle9iDialect;

View File

@ -0,0 +1,22 @@
/*
* 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.mapping.generated.temporals;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.hibernate.annotations.ValueGenerationType;
import org.hibernate.tuple.GenerationTiming;
/**
* @author Steve Ebersole
*/
@ValueGenerationType(generatedBy = CurrentTimestampGeneration.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentTimestamp {
GenerationTiming timing();
}

View File

@ -0,0 +1,45 @@
/*
* 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.mapping.generated.temporals;
import org.hibernate.tuple.AnnotationValueGeneration;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGenerator;
/**
* @author Steve Ebersole
*/
public class CurrentTimestampGeneration implements AnnotationValueGeneration<CurrentTimestamp> {
private GenerationTiming timing;
private Class<?> propertyType;
@Override
public void initialize(CurrentTimestamp annotation, Class<?> propertyType) {
this.timing = annotation.timing();
this.propertyType = propertyType;
}
@Override
public GenerationTiming getGenerationTiming() {
return timing;
}
@Override
public ValueGenerator<?> getValueGenerator() {
return null;
}
@Override
public boolean referenceColumnInSql() {
return true;
}
@Override
public String getDatabaseGeneratedReferencedColumnValue() {
return "current_timestamp";
}
}

View File

@ -0,0 +1,110 @@
/*
* 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.mapping.generated.temporals;
import java.time.Instant;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = GeneratedInstantTests.GeneratedInstantEntity.class )
@SessionFactory
public class GeneratedInstantTests {
@Test
public void test(SessionFactoryScope scope) {
final GeneratedInstantEntity created = scope.fromTransaction( (session) -> {
final GeneratedInstantEntity entity = new GeneratedInstantEntity( 1, "tsifr" );
session.persist( entity );
return entity;
} );
assertThat( created.createdAt ).isNotNull();
assertThat( created.updatedAt ).isNotNull();
assertThat( created.createdAt ).isEqualTo( created.updatedAt );
// assertThat( created.createdAt2 ).isNotNull();
// assertThat( created.updatedAt2 ).isNotNull();
// assertThat( created.createdAt2 ).isEqualTo( created.updatedAt2 );
created.name = "first";
// then changing
final GeneratedInstantEntity merged = scope.fromTransaction( (session) -> {
return (GeneratedInstantEntity) session.merge( created );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdAt ).isNotNull();
assertThat( merged.updatedAt ).isNotNull();
assertThat( merged.createdAt ).isEqualTo( created.createdAt );
assertThat( merged.updatedAt ).isNotEqualTo( created.updatedAt );
assertThat( merged ).isNotNull();
// assertThat( merged.createdAt2 ).isNotNull();
// assertThat( merged.updatedAt2 ).isNotNull();
// assertThat( merged.createdAt2 ).isEqualTo( created.createdAt2 );
// assertThat( merged.updatedAt2 ).isNotEqualTo( created.updatedAt2 );
// lastly, make sure we can load it..
final GeneratedInstantEntity loaded = scope.fromTransaction( (session) -> {
return session.get( GeneratedInstantEntity.class, 1 );
} );
assertThat( loaded ).isNotNull();
assertThat( loaded.createdAt ).isEqualTo( merged.createdAt );
assertThat( loaded.updatedAt ).isEqualTo( merged.updatedAt );
// assertThat( loaded.createdAt2 ).isEqualTo( merged.createdAt2 );
// assertThat( loaded.updatedAt2 ).isEqualTo( merged.updatedAt2 );
}
@Entity( name = "GeneratedInstantEntity" )
@Table( name = "gen_ann_instant" )
public static class GeneratedInstantEntity {
@Id
public Integer id;
public String name;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Legacy `Generated`
@CurrentTimestamp( timing = GenerationTiming.INSERT )
public Instant createdAt;
@CurrentTimestamp( timing = GenerationTiming.ALWAYS )
public Instant updatedAt;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// `GeneratedValue`
// @ProposedGenerated( timing = GenerationTiming.INSERT, sqlDefaultValue = "current_timestamp" )
// public Instant createdAt2;
// @ProposedGenerated( timing = GenerationTiming.ALWAYS, sqlDefaultValue = "current_timestamp" )
// public Instant updatedAt2;
public GeneratedInstantEntity() {
}
public GeneratedInstantEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -0,0 +1,112 @@
/*
* 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.mapping.generated.temporals;
import java.time.Instant;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for defining multiple generated values per entity
*
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = MultipleGeneratedValuesTests.GeneratedInstantEntity.class )
@SessionFactory
public class MultipleGeneratedValuesTests {
@Test
public void test(SessionFactoryScope scope) {
final GeneratedInstantEntity created = scope.fromTransaction( (session) -> {
final GeneratedInstantEntity entity = new GeneratedInstantEntity( 1, "tsifr" );
session.persist( entity );
return entity;
} );
assertThat( created.createdAt ).isNotNull();
assertThat( created.updatedAt ).isNotNull();
assertThat( created.createdAt ).isEqualTo( created.updatedAt );
assertThat( created.createdAt2 ).isNotNull();
assertThat( created.updatedAt2 ).isNotNull();
assertThat( created.createdAt2 ).isEqualTo( created.updatedAt2 );
created.name = "first";
// then changing
final GeneratedInstantEntity merged = scope.fromTransaction( (session) -> {
return (GeneratedInstantEntity) session.merge( created );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdAt ).isNotNull();
assertThat( merged.updatedAt ).isNotNull();
assertThat( merged.createdAt ).isEqualTo( created.createdAt );
assertThat( merged.updatedAt ).isNotEqualTo( created.updatedAt );
assertThat( merged ).isNotNull();
assertThat( merged.createdAt2 ).isNotNull();
assertThat( merged.updatedAt2 ).isNotNull();
assertThat( merged.createdAt2 ).isEqualTo( created.createdAt2 );
assertThat( merged.updatedAt2 ).isNotEqualTo( created.updatedAt2 );
// lastly, make sure we can load it..
final GeneratedInstantEntity loaded = scope.fromTransaction( (session) -> {
return session.get( GeneratedInstantEntity.class, 1 );
} );
assertThat( loaded ).isNotNull();
assertThat( loaded.createdAt ).isEqualTo( merged.createdAt );
assertThat( loaded.updatedAt ).isEqualTo( merged.updatedAt );
assertThat( loaded.createdAt2 ).isEqualTo( merged.createdAt2 );
assertThat( loaded.updatedAt2 ).isEqualTo( merged.updatedAt2 );
}
@Entity( name = "GeneratedInstantEntity" )
@Table( name = "gen_ann_instant" )
public static class GeneratedInstantEntity {
@Id
public Integer id;
public String name;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Legacy `Generated`
@CurrentTimestamp( timing = GenerationTiming.INSERT )
public Instant createdAt;
@CurrentTimestamp( timing = GenerationTiming.ALWAYS )
public Instant updatedAt;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// `GeneratedValue`
@ProposedGenerated( timing = GenerationTiming.INSERT, sqlDefaultValue = "current_timestamp" )
public Instant createdAt2;
@ProposedGenerated( timing = GenerationTiming.ALWAYS, sqlDefaultValue = "current_timestamp" )
public Instant updatedAt2;
public GeneratedInstantEntity() {
}
public GeneratedInstantEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.mapping.generated.temporals;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.hibernate.annotations.ValueGenerationType;
import org.hibernate.tuple.GenerationTiming;
/**
* Proposal for making {@link org.hibernate.annotations.Generated} work for update (they don't work in 5.x either)
*
* @author Steve Ebersole
*
* @see ProposedGeneratedValueGeneration
*/
@ValueGenerationType( generatedBy = ProposedGeneratedValueGeneration.class )
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD})
public @interface ProposedGenerated {
/**
* When the generation should occur
*/
GenerationTiming timing();
/**
* Value to use as the value for the column reference in the SQL.
*
* For example "default" would indicate to use that keyword to trigger
* applying the defaults defined at the column level.
*
* "current_timestamp" might be used to call for the database's function
* of that name be used as the value
*/
String sqlDefaultValue() default "";
}

View File

@ -0,0 +1,92 @@
/*
* 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.mapping.generated.temporals;
import java.time.Instant;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ProposedGenerated}, a proposed update to {@link org.hibernate.annotations.Generated}
*
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = ProposedGeneratedTests.GeneratedInstantEntity.class )
@SessionFactory
public class ProposedGeneratedTests {
@Test
public void test(SessionFactoryScope scope) {
final GeneratedInstantEntity created = scope.fromTransaction( (session) -> {
final GeneratedInstantEntity entity = new GeneratedInstantEntity( 1, "tsifr" );
session.persist( entity );
return entity;
} );
assertThat( created.createdAt ).isNotNull();
assertThat( created.updatedAt ).isNotNull();
assertThat( created.createdAt ).isEqualTo( created.updatedAt );
created.name = "first";
// then changing
final GeneratedInstantEntity merged = scope.fromTransaction( (session) -> {
return (GeneratedInstantEntity) session.merge( created );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdAt ).isNotNull();
assertThat( merged.updatedAt ).isNotNull();
assertThat( merged.createdAt ).isEqualTo( created.createdAt );
assertThat( merged.updatedAt ).isNotEqualTo( created.updatedAt );
assertThat( merged ).isNotNull();
// lastly, make sure we can load it..
final GeneratedInstantEntity loaded = scope.fromTransaction( (session) -> {
return session.get( GeneratedInstantEntity.class, 1 );
} );
assertThat( loaded ).isNotNull();
assertThat( loaded.createdAt ).isEqualTo( merged.createdAt );
assertThat( loaded.updatedAt ).isEqualTo( merged.updatedAt );
}
@Entity( name = "GeneratedInstantEntity" )
@Table( name = "gen_ann_instant" )
public static class GeneratedInstantEntity {
@Id
public Integer id;
public String name;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Proposed update to the legacy `Generated` annotation
@ProposedGenerated( timing = GenerationTiming.INSERT, sqlDefaultValue = "current_timestamp" )
public Instant createdAt;
@ProposedGenerated( timing = GenerationTiming.ALWAYS, sqlDefaultValue = "current_timestamp" )
public Instant updatedAt;
public GeneratedInstantEntity() {
}
public GeneratedInstantEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.mapping.generated.temporals;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.tuple.AnnotationValueGeneration;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGenerator;
/**
* Proposal for making `@GeneratedValueGeneration` work for update (they don't work in 5.x either)
*
* @see ProposedGenerated
*
* @author Steve Ebersole
*/
public class ProposedGeneratedValueGeneration implements AnnotationValueGeneration<ProposedGenerated> {
private GenerationTiming timing;
private String defaultValue;
@Override
public void initialize(ProposedGenerated annotation, Class propertyType) {
timing = annotation.timing();
final String defaultValue = annotation.sqlDefaultValue();
this.defaultValue = StringHelper.isEmpty( defaultValue )
? null
: defaultValue;
}
@Override
public GenerationTiming getGenerationTiming() {
return timing;
}
@Override
public ValueGenerator<?> getValueGenerator() {
return null;
}
@Override
public boolean referenceColumnInSql() {
return defaultValue != null;
}
@Override
public String getDatabaseGeneratedReferencedColumnValue() {
return defaultValue;
}
}

View File

@ -1,269 +0,0 @@
/*
* 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.test.generated;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.Session;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
import org.hibernate.annotations.GeneratorType;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.SybaseDialect;
import org.hibernate.tuple.ValueGenerator;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* Test for the generation of column values using different
* {@link org.hibernate.tuple.ValueGeneration} implementations.
*
* @author Steve Ebersole
* @author Gunnar Morling
*/
@SkipForDialect(value = SybaseDialect.class, comment = "CURRENT_TIMESTAMP not supported as default value in Sybase")
@SkipForDialect(value = MySQLDialect.class, comment = "See HHH-10196", strictMatching = false)
public class DefaultGeneratedValueTest extends BaseCoreFunctionalTestCase {
@Test
@TestForIssue( jiraKey = "HHH-2907" )
public void testGeneration() {
Session s = openSession();
s.beginTransaction();
TheEntity theEntity = new TheEntity( 1 );
assertNull( theEntity.createdDate );
assertNull( theEntity.alwaysDate );
assertNull( theEntity.vmCreatedDate );
assertNull( theEntity.vmCreatedSqlDate );
assertNull( theEntity.vmCreatedSqlTime );
assertNull( theEntity.vmCreatedSqlTimestamp );
assertNull( theEntity.vmCreatedSqlLocalDate );
assertNull( theEntity.vmCreatedSqlLocalTime );
assertNull( theEntity.vmCreatedSqlLocalDateTime );
assertNull( theEntity.vmCreatedSqlMonthDay );
assertNull( theEntity.vmCreatedSqlOffsetDateTime );
assertNull( theEntity.vmCreatedSqlOffsetTime );
assertNull( theEntity.vmCreatedSqlYear );
assertNull( theEntity.vmCreatedSqlYearMonth );
assertNull( theEntity.vmCreatedSqlZonedDateTime );
assertNull( theEntity.name );
s.save( theEntity );
//TODO: Actually the values should be non-null after save
assertNull( theEntity.createdDate );
assertNull( theEntity.alwaysDate );
assertNull( theEntity.vmCreatedDate );
assertNull( theEntity.vmCreatedSqlDate );
assertNull( theEntity.vmCreatedSqlTime );
assertNull( theEntity.vmCreatedSqlTimestamp );
assertNull( theEntity.vmCreatedSqlLocalDate );
assertNull( theEntity.vmCreatedSqlLocalTime );
assertNull( theEntity.vmCreatedSqlLocalDateTime );
assertNull( theEntity.vmCreatedSqlMonthDay );
assertNull( theEntity.vmCreatedSqlOffsetDateTime );
assertNull( theEntity.vmCreatedSqlOffsetTime );
assertNull( theEntity.vmCreatedSqlYear );
assertNull( theEntity.vmCreatedSqlYearMonth );
assertNull( theEntity.vmCreatedSqlZonedDateTime );
assertNull( theEntity.name );
s.getTransaction().commit();
s.close();
assertNotNull( theEntity.createdDate );
assertNotNull( theEntity.alwaysDate );
assertEquals( "Bob", theEntity.name );
s = openSession();
s.beginTransaction();
theEntity = (TheEntity) s.get( TheEntity.class, 1 );
assertNotNull( theEntity.createdDate );
assertNotNull( theEntity.alwaysDate );
assertNotNull( theEntity.vmCreatedDate );
assertNotNull( theEntity.vmCreatedSqlDate );
assertNotNull( theEntity.vmCreatedSqlTime );
assertNotNull( theEntity.vmCreatedSqlTimestamp );
assertNotNull( theEntity.vmCreatedSqlLocalDate );
assertNotNull( theEntity.vmCreatedSqlLocalTime );
assertNotNull( theEntity.vmCreatedSqlLocalDateTime );
assertNotNull( theEntity.vmCreatedSqlMonthDay );
assertNotNull( theEntity.vmCreatedSqlOffsetDateTime );
assertNotNull( theEntity.vmCreatedSqlOffsetTime );
assertNotNull( theEntity.vmCreatedSqlYear );
assertNotNull( theEntity.vmCreatedSqlYearMonth );
assertNotNull( theEntity.vmCreatedSqlZonedDateTime );
assertEquals( "Bob", theEntity.name );
theEntity.lastName = "Smith";
s.delete( theEntity );
s.getTransaction().commit();
s.close();
}
@Test
@TestForIssue(jiraKey = "HHH-2907")
public void testUpdateTimestampGeneration() {
Session s = openSession();
s.beginTransaction();
TheEntity theEntity = new TheEntity( 1 );
assertNull( theEntity.updated );
s.save( theEntity );
//TODO: Actually the value should be non-null after save
assertNull( theEntity.updated );
s.getTransaction().commit();
s.close();
Timestamp created = theEntity.vmCreatedSqlTimestamp;
Timestamp updated = theEntity.updated;
assertNotNull( updated );
assertNotNull( created );
s = openSession();
s.beginTransaction();
theEntity = (TheEntity) s.get( TheEntity.class, 1 );
theEntity.lastName = "Smith";
try {
Thread.sleep( 1 );
}
catch (InterruptedException ignore) {}
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
theEntity = (TheEntity) s.get( TheEntity.class, 1 );
assertEquals( "Creation timestamp should not change on update", created, theEntity.vmCreatedSqlTimestamp );
assertTrue( "Update timestamp should have changed due to update", theEntity.updated.after( updated ) );
s.delete( theEntity );
s.getTransaction().commit();
s.close();
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { TheEntity.class };
}
@Entity( name = "TheEntity" )
@Table( name = "T_ENT_GEN_DEF" )
private static class TheEntity {
@Id
private Integer id;
@Generated( GenerationTime.INSERT )
@ColumnDefault( "CURRENT_TIMESTAMP" )
@Column( nullable = false )
private Date createdDate;
@Generated( GenerationTime.ALWAYS )
@ColumnDefault( "CURRENT_TIMESTAMP" )
@Column( nullable = false )
private Calendar alwaysDate;
@CreationTimestamp
private Date vmCreatedDate;
@CreationTimestamp
private Calendar vmCreatedCalendar;
@CreationTimestamp
private java.sql.Date vmCreatedSqlDate;
@CreationTimestamp
private Time vmCreatedSqlTime;
@CreationTimestamp
private Timestamp vmCreatedSqlTimestamp;
@CreationTimestamp
private Instant vmCreatedSqlInstant;
@CreationTimestamp
private LocalDate vmCreatedSqlLocalDate;
@CreationTimestamp
private LocalTime vmCreatedSqlLocalTime;
@CreationTimestamp
private LocalDateTime vmCreatedSqlLocalDateTime;
@CreationTimestamp
private MonthDay vmCreatedSqlMonthDay;
@CreationTimestamp
private OffsetDateTime vmCreatedSqlOffsetDateTime;
@CreationTimestamp
private OffsetTime vmCreatedSqlOffsetTime;
@CreationTimestamp
private Year vmCreatedSqlYear;
@CreationTimestamp
private YearMonth vmCreatedSqlYearMonth;
@CreationTimestamp
private ZonedDateTime vmCreatedSqlZonedDateTime;
@UpdateTimestamp
private Timestamp updated;
@GeneratorType( type = MyVmValueGenerator.class, when = GenerationTime.INSERT )
private String name;
@SuppressWarnings("unused")
private String lastName;
private TheEntity() {
}
private TheEntity(Integer id) {
this.id = id;
}
}
public static class MyVmValueGenerator implements ValueGenerator<String> {
@Override
public String generateValue(Session session, Object owner) {
return "Bob";
}
}
}