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:
parent
b8afa46d8f
commit
230c787b3c
|
@ -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)" );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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): "
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -195,6 +195,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp
|
|||
attributeMapping = BasicAttributeMapping.withSelectableMapping(
|
||||
original,
|
||||
original.getPropertyAccess(),
|
||||
original.getValueGeneration(),
|
||||
selectableMapping
|
||||
);
|
||||
currentIndex++;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,8 @@ public class DiscriminatedAssociationAttributeMapping
|
|||
fetchTiming,
|
||||
FetchStyle.SELECT,
|
||||
declaringType,
|
||||
propertyAccess
|
||||
propertyAccess,
|
||||
null
|
||||
);
|
||||
this.navigableRole = attributeRole;
|
||||
|
||||
|
|
|
@ -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() );
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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" );
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -82,6 +82,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
|
|||
keyModelPart = BasicAttributeMapping.withSelectableMapping(
|
||||
keyModelPart,
|
||||
keyPropertyAccess,
|
||||
NoValueGeneration.INSTANCE,
|
||||
keySelectableMapping
|
||||
);
|
||||
if ( swapDirection ) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 ) ) {
|
||||
|
|
|
@ -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" );
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
||||
/**
|
|
@ -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
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
||||
/**
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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();
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 "";
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue