HHH-15774 @CurrentTimestamp takes over from @Source
... and make value generation work better with version properties.
This commit is contained in:
parent
a9ac98b364
commit
993aae6095
|
@ -112,12 +112,7 @@ include::{sourcedir}/OptimisticLockingTest.java[tags=locking-optimistic-version-
|
|||
----
|
||||
====
|
||||
|
||||
Hibernate can retrieve the timestamp value from the database or the JVM, by reading the value you specify for the `@org.hibernate.annotations.Source` annotation.
|
||||
The value can be either `org.hibernate.annotations.SourceType.DB` or `org.hibernate.annotations.SourceType.VM`.
|
||||
The default behavior is to use the database, and database is also used if you don't specify the annotation at all.
|
||||
|
||||
The timestamp can also be generated by the database instead of Hibernate
|
||||
if you use the `@org.hibernate.annotations.Generated(GenerationTime.ALWAYS)` or the `@Source` annotation.
|
||||
The timestamp can also be generated by the database, instead of by the VM, using the `@CurrentTimestamp` annotation, or even `@Generated(value = ALWAYS, sql = "current_timestamp")`.
|
||||
|
||||
[[locking-optimistic-version-timestamp-source-mapping-example]]
|
||||
.Database-generated version timestamp mapping
|
||||
|
|
|
@ -6,17 +6,15 @@
|
|||
*/
|
||||
package org.hibernate.userguide.locking;
|
||||
|
||||
import java.util.Date;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Version;
|
||||
|
||||
import org.hibernate.annotations.Source;
|
||||
import org.hibernate.annotations.SourceType;
|
||||
import org.hibernate.annotations.CurrentTimestamp;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
@ -47,6 +45,24 @@ public class VersionSourceTest extends BaseEntityManagerFunctionalTestCase {
|
|||
assertNotNull(person.getVersion());
|
||||
//end::locking-optimistic-version-timestamp-source-persist-example[]
|
||||
});
|
||||
sleep();
|
||||
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||
Person person = entityManager.find(Person.class, 1L);
|
||||
person.setFirstName("Jane");
|
||||
});
|
||||
sleep();
|
||||
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||
Person person = entityManager.find(Person.class, 1L);
|
||||
person.setFirstName("John");
|
||||
});
|
||||
}
|
||||
|
||||
private static void sleep() {
|
||||
try {
|
||||
Thread.sleep(300);
|
||||
}
|
||||
catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
//tag::locking-optimistic-version-timestamp-source-mapping-example[]
|
||||
|
@ -60,9 +76,8 @@ public class VersionSourceTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
private String lastName;
|
||||
|
||||
@Version
|
||||
@Source(value = SourceType.DB)
|
||||
private Date version;
|
||||
@Version @CurrentTimestamp
|
||||
private LocalDateTime version;
|
||||
//end::locking-optimistic-version-timestamp-source-mapping-example[]
|
||||
|
||||
public Long getId() {
|
||||
|
@ -89,11 +104,8 @@ public class VersionSourceTest extends BaseEntityManagerFunctionalTestCase {
|
|||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public Date getVersion() {
|
||||
public LocalDateTime getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
//tag::locking-optimistic-version-timestamp-source-mapping-example[]
|
||||
}
|
||||
//end::locking-optimistic-version-timestamp-source-mapping-example[]
|
||||
}
|
||||
|
|
|
@ -127,10 +127,14 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
|
|||
final Object instance = getInstance();
|
||||
persister.processInsertGeneratedProperties( getId(), instance, getState(), getSession() );
|
||||
if ( persister.isVersionPropertyGenerated() ) {
|
||||
version = Versioning.getVersion( getState(), persister);
|
||||
version = Versioning.getVersion( getState(), persister );
|
||||
}
|
||||
entry.postUpdate( instance, getState(), version );
|
||||
}
|
||||
else if ( persister.isVersionPropertyGenerated() ) {
|
||||
version = Versioning.getVersion( getState(), persister );
|
||||
entry.postInsert( version );
|
||||
}
|
||||
}
|
||||
|
||||
private void putCacheIfNecessary() {
|
||||
|
|
|
@ -234,12 +234,12 @@ public class EntityUpdateAction extends EntityAction {
|
|||
// this entity defines property generation, so process those generated
|
||||
// values...
|
||||
persister.processUpdateGeneratedProperties( id, instance, state, session );
|
||||
if ( persister.isVersionPropertyGenerated() ) {
|
||||
nextVersion = Versioning.getVersion( state, persister);
|
||||
}
|
||||
}
|
||||
// have the entity entry doAfterTransactionCompletion post-update processing, passing it the
|
||||
// update state and the new version (if one).
|
||||
if ( persister.isVersionPropertyGenerated() ) {
|
||||
nextVersion = Versioning.getVersion( state, persister );
|
||||
}
|
||||
entry.postUpdate( instance, state, nextVersion );
|
||||
}
|
||||
|
||||
|
|
|
@ -16,33 +16,25 @@ import java.lang.annotation.Target;
|
|||
import org.hibernate.tuple.CreationTimestampGeneration;
|
||||
|
||||
/**
|
||||
* Marks a property as the creation timestamp of the containing entity.
|
||||
* The property value is set to the current VM datetime when the owning
|
||||
* entity is made persistent for the first time.
|
||||
* Specifies that the annotated field of property is a generated <em>creation timestamp</em>.
|
||||
* The timestamp is generated just once, when an entity instance is inserted in the database.
|
||||
* <p>
|
||||
* The annotated field or property must be of one of the following types:
|
||||
* <ul>
|
||||
* <li>{@link java.util.Date}</li>
|
||||
* <li>{@link java.util.Calendar}</li>
|
||||
* <li>{@link java.sql.Date}</li>
|
||||
* <li>{@link java.sql.Time}</li>
|
||||
* <li>{@link java.sql.Timestamp}</li>
|
||||
* <li>{@link java.time.Instant}</li>
|
||||
* <li>{@link java.time.LocalDate}</li>
|
||||
* <li>{@link java.time.LocalDateTime}</li>
|
||||
* <li>{@link java.time.LocalTime}</li>
|
||||
* <li>{@link java.time.MonthDay}</li>
|
||||
* <li>{@link java.time.OffsetDateTime}</li>
|
||||
* <li>{@link java.time.OffsetTime}</li>
|
||||
* <li>{@link java.time.Year}</li>
|
||||
* <li>{@link java.time.YearMonth}</li>
|
||||
* <li>{@link java.time.ZonedDateTime}</li>
|
||||
* </ul>
|
||||
* By default, the timestamp is generated by {@linkplain java.time.Clock#instant() in memory},
|
||||
* but this may be changed by explicitly specifying the {@link #source}.
|
||||
* Otherwise, this annotation is a synonym for
|
||||
* {@link CurrentTimestamp @CurrentTimestamp(timing=INSERT,source=VM)}.
|
||||
*
|
||||
* @author Gunnar Morling
|
||||
*
|
||||
* @see CurrentTimestamp
|
||||
*/
|
||||
@ValueGenerationType(generatedBy = CreationTimestampGeneration.class)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ FIELD, METHOD })
|
||||
public @interface CreationTimestamp {
|
||||
/**
|
||||
* Specifies how the timestamp is generated. By default, it is generated
|
||||
* in memory, which saves a round trip to the database.
|
||||
*/
|
||||
SourceType source() default SourceType.VM;
|
||||
}
|
||||
|
|
|
@ -15,17 +15,62 @@ import java.lang.annotation.Target;
|
|||
import org.hibernate.tuple.GenerationTiming;
|
||||
|
||||
/**
|
||||
* Specifies that the database {@code current_timestamp} function is used to
|
||||
* generate values of the annotated attribute, based on {@link #timing()}.
|
||||
* Specifies that the annotated field of property is a generated timestamp,
|
||||
* and also specifies the {@linkplain #timing() timing} of the timestamp
|
||||
* generation, and whether it is generated in Java or by the database:
|
||||
* <ul>
|
||||
* <li>{@link SourceType#VM source = VM} indicates that the virtual machine
|
||||
* {@linkplain java.time.Clock#instant() current instant}
|
||||
* is used, and
|
||||
* <li>{@link SourceType#DB source = DB} indicates that the database
|
||||
* {@code current_timestamp} function should be used.
|
||||
* </ul>
|
||||
* By default, the timestamp is generated by the database, which requires
|
||||
* an extra round trip to the database to fetch the generated value.
|
||||
* <p>
|
||||
* This annotation may be used in combination with the JPA-defined
|
||||
* {@link jakarta.persistence.Version} annotation.
|
||||
* <p>
|
||||
* The annotated property may be of any one of the following types:
|
||||
* {@link java.util.Date},
|
||||
* {@link java.util.Calendar},
|
||||
* {@link java.sql.Date},
|
||||
* {@link java.sql.Time},
|
||||
* {@link java.sql.Timestamp},
|
||||
* {@link java.time.Instant},
|
||||
* {@link java.time.LocalDate},
|
||||
* {@link java.time.LocalDateTime},
|
||||
* {@link java.time.LocalTime},
|
||||
* {@link java.time.MonthDay},
|
||||
* {@link java.time.OffsetDateTime},
|
||||
* {@link java.time.OffsetTime},
|
||||
* {@link java.time.Year},
|
||||
* {@link java.time.YearMonth}, or
|
||||
* {@link java.time.ZonedDateTime}.
|
||||
*
|
||||
* @see CurrentTimestampGeneration
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @see UpdateTimestamp
|
||||
* @see CreationTimestamp
|
||||
*/
|
||||
@ValueGenerationType(generatedBy = CurrentTimestampGeneration.class)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target( { ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE } )
|
||||
@Inherited
|
||||
public @interface CurrentTimestamp {
|
||||
GenerationTiming timing();
|
||||
/**
|
||||
* Determines when the timestamp is generated. But default, it is updated
|
||||
* when any SQL {@code insert} or {@code update} statement is executed.
|
||||
* If it should be generated just once, on the initial SQL {@code insert},
|
||||
* explicitly specify {@link GenerationTiming#INSERT timing = INSERT}.
|
||||
*/
|
||||
GenerationTiming timing() default GenerationTiming.ALWAYS;
|
||||
|
||||
/**
|
||||
* Specifies how the timestamp is generated. By default, it is generated
|
||||
* by the database, and fetched using a subsequent {@code select} statement.
|
||||
*/
|
||||
SourceType source() default SourceType.DB;
|
||||
}
|
||||
|
|
|
@ -6,13 +6,15 @@
|
|||
*/
|
||||
package org.hibernate.annotations;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.tuple.AnnotationValueGeneration;
|
||||
import org.hibernate.tuple.GenerationTiming;
|
||||
import org.hibernate.tuple.TimestampGenerators;
|
||||
import org.hibernate.tuple.ValueGenerator;
|
||||
|
||||
/**
|
||||
* Value generation strategy for using the database `current_timestamp` function to generate
|
||||
* the values
|
||||
* Value generation strategy using the database {@code current_timestamp}
|
||||
* function or the JVM current instant.
|
||||
*
|
||||
* @see CurrentTimestamp
|
||||
*
|
||||
|
@ -20,10 +22,15 @@ import org.hibernate.tuple.ValueGenerator;
|
|||
*/
|
||||
public class CurrentTimestampGeneration implements AnnotationValueGeneration<CurrentTimestamp> {
|
||||
private GenerationTiming timing;
|
||||
private ValueGenerator<?> generator;
|
||||
|
||||
@Override
|
||||
public void initialize(CurrentTimestamp annotation, Class<?> propertyType) {
|
||||
this.timing = annotation.timing();
|
||||
if ( annotation.source() == SourceType.VM ) {
|
||||
// ValueGenerator is only used for in-VM generation
|
||||
generator = TimestampGenerators.get( propertyType );
|
||||
}
|
||||
timing = annotation.timing();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -33,8 +40,7 @@ public class CurrentTimestampGeneration implements AnnotationValueGeneration<Cur
|
|||
|
||||
@Override
|
||||
public ValueGenerator<?> getValueGenerator() {
|
||||
// ValueGenerator is only used for in-VM generations
|
||||
return null;
|
||||
return generator;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -46,4 +52,9 @@ public class CurrentTimestampGeneration implements AnnotationValueGeneration<Cur
|
|||
public String getDatabaseGeneratedReferencedColumnValue() {
|
||||
return "current_timestamp";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDatabaseGeneratedReferencedColumnValue(Dialect dialect) {
|
||||
return dialect.currentTimestamp();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.annotations;
|
||||
|
||||
import org.hibernate.Remove;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
@ -24,9 +26,24 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
* <li>{@link SourceType#DB} indicates that the database
|
||||
* {@code current_timestamp} function should be used.
|
||||
* </ul>
|
||||
* For example, the following timestamp is generated by the
|
||||
* database:
|
||||
* <pre>
|
||||
* {@code
|
||||
* @Version @Source(DB)
|
||||
* private LocalDateTime version;
|
||||
* }
|
||||
* </pre>
|
||||
* This annotation is always used in conjunction with the JPA
|
||||
* {@link jakarta.persistence.Version @Version} annotation.
|
||||
*
|
||||
* @author Hardy Ferentschik
|
||||
*
|
||||
* @see jakarta.persistence.Version
|
||||
*
|
||||
* @deprecated use {@link CurrentTimestamp} instead
|
||||
*/
|
||||
@Deprecated(since = "6.2") @Remove
|
||||
@Target({ METHOD, FIELD })
|
||||
@Retention(RUNTIME)
|
||||
public @interface Source {
|
||||
|
|
|
@ -7,34 +7,30 @@
|
|||
package org.hibernate.annotations;
|
||||
|
||||
/**
|
||||
* Specifies the source of a generated {@link java.sql.Timestamp}.
|
||||
* <p>
|
||||
* Timestamps are generated by calling methods of
|
||||
* {@link org.hibernate.type.descriptor.java.VersionJavaType}.
|
||||
* Specifies the source of a generated value, either the virtual machine,
|
||||
* or the database.
|
||||
*
|
||||
* @see CurrentTimestamp#source()
|
||||
* @see UpdateTimestamp#source()
|
||||
* @see CreationTimestamp#source()
|
||||
* @see Source
|
||||
*
|
||||
* @author Hardy Ferentschik
|
||||
*/
|
||||
public enum SourceType {
|
||||
/**
|
||||
* The virtual machine {@linkplain java.time.Clock#instant()
|
||||
* current instance} is the source of the generated timestamp.
|
||||
* Indicates that values are generated in the Java virtual machine.
|
||||
* <p>
|
||||
* For a generated timestamp, {@link java.time.Clock#instant()} might
|
||||
* be the source.
|
||||
*/
|
||||
VM,
|
||||
|
||||
/**
|
||||
* The database {@code current_timestamp} function (or equivalent)
|
||||
* is the source of the generated timestamp.
|
||||
* Indicates that values are generated by the database.
|
||||
* <p>
|
||||
* For a generated timestamp, the {@code current_timestamp} function
|
||||
* might be the source.
|
||||
*/
|
||||
DB;
|
||||
|
||||
/**
|
||||
* The corresponding {@link org.hibernate.type.BasicType} name.
|
||||
*
|
||||
* @return The corresponding type name.
|
||||
*/
|
||||
public String typeName() {
|
||||
return this == SourceType.DB ? "dbtimestamp" : "timestamp";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,28 +16,15 @@ import java.lang.annotation.Target;
|
|||
import org.hibernate.tuple.UpdateTimestampGeneration;
|
||||
|
||||
/**
|
||||
* Marks a property as the update timestamp of the containing entity.
|
||||
* The property value will be set to the current VM datetime whenever
|
||||
* the owning entity is updated.
|
||||
* Specifies that the annotated field of property is a generated <em>update timestamp.</em>
|
||||
* The timestamp is regenerated every time an entity instance is updated in the database.
|
||||
* <p>
|
||||
* The annotated field or property must be of one of the following types:
|
||||
* <ul>
|
||||
* <li>{@link java.util.Date}</li>
|
||||
* <li>{@link java.util.Calendar}</li>
|
||||
* <li>{@link java.sql.Date}</li>
|
||||
* <li>{@link java.sql.Time}</li>
|
||||
* <li>{@link java.sql.Timestamp}</li>
|
||||
* <li>{@link java.time.Instant}</li>
|
||||
* <li>{@link java.time.LocalDate}</li>
|
||||
* <li>{@link java.time.LocalDateTime}</li>
|
||||
* <li>{@link java.time.LocalTime}</li>
|
||||
* <li>{@link java.time.MonthDay}</li>
|
||||
* <li>{@link java.time.OffsetDateTime}</li>
|
||||
* <li>{@link java.time.OffsetTime}</li>
|
||||
* <li>{@link java.time.Year}</li>
|
||||
* <li>{@link java.time.YearMonth}</li>
|
||||
* <li>{@link java.time.ZonedDateTime}</li>
|
||||
* </ul>
|
||||
* By default, the timestamp is generated {@linkplain java.time.Clock#instant() in memory},
|
||||
* but this may be changed by explicitly specifying the {@link #source}.
|
||||
* Otherwise, this annotation is a synonym for
|
||||
* {@link CurrentTimestamp @CurrentTimestamp(source=VM)}.
|
||||
*
|
||||
* @see CurrentTimestamp
|
||||
*
|
||||
* @author Gunnar Morling
|
||||
*/
|
||||
|
@ -45,4 +32,9 @@ import org.hibernate.tuple.UpdateTimestampGeneration;
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ FIELD, METHOD })
|
||||
public @interface UpdateTimestamp {
|
||||
/**
|
||||
* Specifies how the timestamp is generated. By default, it is generated
|
||||
* in memory, which saves a round trip to the database.
|
||||
*/
|
||||
SourceType source() default SourceType.VM;
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ import org.hibernate.annotations.ParamDef;
|
|||
import org.hibernate.annotations.Parameter;
|
||||
import org.hibernate.annotations.Parent;
|
||||
import org.hibernate.annotations.Source;
|
||||
import org.hibernate.annotations.SourceType;
|
||||
import org.hibernate.annotations.TimeZoneStorage;
|
||||
import org.hibernate.annotations.common.reflection.ReflectionManager;
|
||||
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
||||
|
@ -1800,7 +1801,8 @@ public final class AnnotationBinder {
|
|||
propertyBinder.getBasicValueBinder().setVersion( true );
|
||||
if ( property.isAnnotationPresent( Source.class ) ) {
|
||||
final Source source = property.getAnnotation( Source.class );
|
||||
propertyBinder.getBasicValueBinder().setTimestampVersionType( source.value().typeName() );
|
||||
propertyBinder.getBasicValueBinder()
|
||||
.setTimestampVersionType( source.value() == SourceType.DB ? "dbtimestamp" : "timestamp" );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttrib
|
|||
* @author <a href="mailto:sanne@hibernate.org">Sanne Grinovero </a>
|
||||
*/
|
||||
public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
|
||||
|
||||
protected final Object id;
|
||||
protected Object[] loadedState;
|
||||
protected Object version;
|
||||
|
@ -98,13 +99,13 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
|
|||
if ( status != Status.READ_ONLY ) {
|
||||
this.loadedState = loadedState;
|
||||
}
|
||||
this.id=id;
|
||||
this.rowId=rowId;
|
||||
this.id = id;
|
||||
this.rowId = rowId;
|
||||
setCompressedValue( BooleanState.EXISTS_IN_DATABASE, existsInDatabase );
|
||||
this.version=version;
|
||||
this.version = version;
|
||||
setCompressedValue( EnumState.LOCK_MODE, lockMode );
|
||||
setCompressedValue( BooleanState.IS_BEING_REPLICATED, disableVersionIncrement );
|
||||
this.persister=persister;
|
||||
this.persister = persister;
|
||||
this.persistenceContext = persistenceContext;
|
||||
}
|
||||
|
||||
|
@ -220,6 +221,11 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
|
|||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInsert(Object version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityPersister getPersister() {
|
||||
return persister;
|
||||
|
|
|
@ -49,6 +49,8 @@ public interface EntityEntry {
|
|||
|
||||
Object getVersion();
|
||||
|
||||
void postInsert(Object version);
|
||||
|
||||
EntityPersister getPersister();
|
||||
|
||||
/**
|
||||
|
|
|
@ -136,11 +136,11 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
|
|||
final EntityKey key = source.generateEntityKey( id, persister );
|
||||
persistenceContext.removeEntity( key );
|
||||
if ( persister.hasCollections() ) {
|
||||
new EvictVisitor(source, object).process(object, persister );
|
||||
new EvictVisitor( source, object ).process( object, persister );
|
||||
}
|
||||
}
|
||||
|
||||
evictEntity(object, persister, id, source);
|
||||
evictEntity( object, persister, id, source);
|
||||
evictCachedCollections( persister, id, source);
|
||||
|
||||
final Object result = source.getLoadQueryInfluencers().fromInternalFetchProfile(
|
||||
|
@ -157,7 +157,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
|
|||
// we need to grab the version value from the entity, otherwise
|
||||
// we have issues with generated-version entities that may have
|
||||
// multiple actions queued during the same flush
|
||||
previousVersion = persister.getVersion(object);
|
||||
previousVersion = persister.getVersion( object );
|
||||
}
|
||||
final EntityDataAccess cache = persister.getCacheAccessStrategy();
|
||||
final Object ck = cache.generateCacheKey(
|
||||
|
@ -166,7 +166,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
|
|||
source.getFactory(),
|
||||
source.getTenantIdentifier()
|
||||
);
|
||||
final SoftLock lock = cache.lockItem(source, ck, previousVersion );
|
||||
final SoftLock lock = cache.lockItem( source, ck, previousVersion );
|
||||
cache.remove( source, ck );
|
||||
source.getActionQueue().registerProcess( (success, session) -> cache.unlockItem( session, ck, lock ) );
|
||||
}
|
||||
|
|
|
@ -1937,7 +1937,7 @@ public abstract class AbstractEntityPersister
|
|||
throw new AssertionFailure( "cannot force version increment on non-versioned entity" );
|
||||
}
|
||||
|
||||
if ( isVersionPropertyGenerated() ) {
|
||||
if ( entityMetamodel.isVersionGeneratedByDatabase() ) {
|
||||
// the difficulty here is exactly what we update in order to
|
||||
// force the version to be incremented in the db...
|
||||
throw new HibernateException( "LockMode.FORCE is currently not supported for generated version properties" );
|
||||
|
@ -2848,8 +2848,7 @@ public abstract class AbstractEntityPersister
|
|||
}
|
||||
|
||||
public final boolean checkVersion(final boolean[] includeProperty) {
|
||||
return includeProperty[getVersionProperty()]
|
||||
|| entityMetamodel.isVersionGenerated();
|
||||
return includeProperty[getVersionProperty()] || entityMetamodel.isVersionGeneratedByDatabase();
|
||||
}
|
||||
|
||||
public boolean useGetGeneratedKeys() {
|
||||
|
@ -4042,7 +4041,9 @@ public abstract class AbstractEntityPersister
|
|||
|
||||
@Override
|
||||
public boolean isVersionPropertyGenerated() {
|
||||
return isVersioned() && getEntityMetamodel().isVersionGenerated();
|
||||
return isVersioned()
|
||||
&& ( getEntityMetamodel().isVersionGeneratedByDatabase()
|
||||
|| getEntityMetamodel().isVersionGeneratedInMemory() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -198,8 +198,8 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
|
||||
final boolean[] propertyLaziness = entityPersister().getPropertyLaziness();
|
||||
for ( int i = 0; i < propertyLaziness.length; i++ ) {
|
||||
// add also all the non lazy properties because dynamic update is false
|
||||
if ( propertyLaziness[i] == false ) {
|
||||
// add also all the non-lazy properties because dynamic update is false
|
||||
if ( !propertyLaziness[i] ) {
|
||||
attributeUpdateability[i] = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,11 +7,15 @@
|
|||
package org.hibernate.tuple;
|
||||
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.SourceType;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
|
||||
/**
|
||||
* Value generation implementation for {@link CreationTimestamp}.
|
||||
*
|
||||
* @author Gunnar Morling
|
||||
*
|
||||
* @see org.hibernate.annotations.CurrentTimestampGeneration
|
||||
*/
|
||||
public class CreationTimestampGeneration implements AnnotationValueGeneration<CreationTimestamp> {
|
||||
|
||||
|
@ -19,7 +23,9 @@ public class CreationTimestampGeneration implements AnnotationValueGeneration<Cr
|
|||
|
||||
@Override
|
||||
public void initialize(CreationTimestamp annotation, Class<?> propertyType) {
|
||||
generator = TimestampGenerators.get(propertyType);
|
||||
if ( annotation.source() == SourceType.VM ) {
|
||||
generator = TimestampGenerators.get( propertyType );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,6 +45,11 @@ public class CreationTimestampGeneration implements AnnotationValueGeneration<Cr
|
|||
|
||||
@Override
|
||||
public String getDatabaseGeneratedReferencedColumnValue() {
|
||||
return null;
|
||||
return "current_timestamp";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDatabaseGeneratedReferencedColumnValue(Dialect dialect) {
|
||||
return dialect.currentTimestamp();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.hibernate.HibernateException;
|
|||
*
|
||||
* @author Gunnar Morling
|
||||
*/
|
||||
/* package */ final class TimestampGenerators {
|
||||
public final class TimestampGenerators {
|
||||
|
||||
private static final Map<Class<?>, ValueGenerator<?>> generators;
|
||||
|
||||
|
|
|
@ -6,12 +6,16 @@
|
|||
*/
|
||||
package org.hibernate.tuple;
|
||||
|
||||
import org.hibernate.annotations.SourceType;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
|
||||
/**
|
||||
* Value generation implementation for {@link UpdateTimestamp}.
|
||||
*
|
||||
* @author Gunnar Morling
|
||||
*
|
||||
* @see org.hibernate.annotations.CurrentTimestampGeneration
|
||||
*/
|
||||
public class UpdateTimestampGeneration implements AnnotationValueGeneration<UpdateTimestamp> {
|
||||
|
||||
|
@ -19,7 +23,9 @@ public class UpdateTimestampGeneration implements AnnotationValueGeneration<Upda
|
|||
|
||||
@Override
|
||||
public void initialize(UpdateTimestamp annotation, Class<?> propertyType) {
|
||||
generator = TimestampGenerators.get(propertyType);
|
||||
if ( annotation.source() == SourceType.VM ) {
|
||||
generator = TimestampGenerators.get( propertyType );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,6 +45,11 @@ public class UpdateTimestampGeneration implements AnnotationValueGeneration<Upda
|
|||
|
||||
@Override
|
||||
public String getDatabaseGeneratedReferencedColumnValue() {
|
||||
return null;
|
||||
return "current_timestamp";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDatabaseGeneratedReferencedColumnValue(Dialect dialect) {
|
||||
return dialect.currentTimestamp();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -826,11 +826,16 @@ public class EntityMetamodel implements Serializable {
|
|||
return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER;
|
||||
}
|
||||
|
||||
public boolean isVersionGenerated() {
|
||||
public boolean isVersionGeneratedByDatabase() {
|
||||
final InDatabaseValueGenerationStrategy strategy = inDatabaseValueGenerationStrategies[ versionPropertyIndex ];
|
||||
return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER;
|
||||
}
|
||||
|
||||
public boolean isVersionGeneratedInMemory() {
|
||||
final InMemoryValueGenerationStrategy strategy = inMemoryValueGenerationStrategies[ versionPropertyIndex ];
|
||||
return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER;
|
||||
}
|
||||
|
||||
public int[] getNaturalIdentifierProperties() {
|
||||
return naturalIdPropertyNumbers;
|
||||
}
|
||||
|
|
|
@ -9,23 +9,24 @@ package org.hibernate.type;
|
|||
import java.sql.Timestamp;
|
||||
import java.util.Date;
|
||||
|
||||
import org.hibernate.Remove;
|
||||
import org.hibernate.type.descriptor.java.DbTimestampJavaType;
|
||||
import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.TimestampJdbcType;
|
||||
|
||||
/**
|
||||
* {@code dbtimestamp}: A type that maps between {@link java.sql.Types#TIMESTAMP TIMESTAMP} and {@link Timestamp}.
|
||||
* It maps to the database's current timestamp, rather than the jvm's
|
||||
* current timestamp.
|
||||
* <p>
|
||||
* Note: May/may-not cause issues on dialects which do not properly support
|
||||
* a true notion of timestamp (Oracle < 8, for example, where only its DATE
|
||||
* datatype is supported). Depends on the frequency of DML operations...
|
||||
* A type that maps between {@link java.sql.Types#TIMESTAMP TIMESTAMP}
|
||||
* and {@link Timestamp}. Maps to the SQL {@code current_timestamp},
|
||||
* rather than the JVM system datetime.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @deprecated Use {@link org.hibernate.tuple.ValueGeneration} instead
|
||||
*/
|
||||
@Deprecated
|
||||
// Note: May/may not cause issues on dialects which do not properly support
|
||||
// a true notion of timestamp (Oracle < 8, for example, where only its
|
||||
// DATE datatype is supported). Depends on the frequency of DML operations.
|
||||
@Deprecated(since = "6.0") @Remove
|
||||
public class DbTimestampType extends AbstractSingleColumnStandardBasicType<Date> {
|
||||
|
||||
public static final DbTimestampType INSTANCE = new DbTimestampType();
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.sql.SQLException;
|
|||
import java.sql.Timestamp;
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.hibernate.Remove;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
|
@ -32,7 +33,7 @@ import jakarta.persistence.TemporalType;
|
|||
* @author Christian Beikov
|
||||
* @deprecated Use {@link org.hibernate.tuple.ValueGeneration} instead
|
||||
*/
|
||||
@Deprecated
|
||||
@Deprecated(since="6.0") @Remove
|
||||
public class DbTimestampJavaType<T> implements VersionJavaType<T>, TemporalJavaType<T> {
|
||||
|
||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
|
||||
|
|
Loading…
Reference in New Issue