Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,16 @@ public GeneratedValues update(
SharedSessionContractImplementor session) {
final EntityVersionMapping versionMapping = entityPersister().getVersionMapping();
if ( versionMapping != null ) {
final Supplier<GeneratedValues> generatedValuesAccess = handlePotentialImplicitForcedVersionIncrement(
entity,
id,
values,
oldVersion,
incomingDirtyAttributeIndexes,
session,
versionMapping
);
final var generatedValuesAccess =
handlePotentialImplicitForcedVersionIncrement(
entity,
id,
values,
oldVersion,
incomingDirtyAttributeIndexes,
session,
versionMapping
);
if ( generatedValuesAccess != null ) {
return generatedValuesAccess.get();
}
Expand Down Expand Up @@ -389,57 +390,71 @@ protected Supplier<GeneratedValues> handlePotentialImplicitForcedVersionIncremen
int[] incomingDirtyAttributeIndexes,
SharedSessionContractImplementor session,
EntityVersionMapping versionMapping) {
// handle case where the only value being updated is the version.
// we handle this case specially from `#coordinateUpdate` to leverage
// `#doVersionUpdate`
final boolean isSimpleVersionUpdate;
// Handle a case where the only value being updated is the version.
// We treat this case specially in `#coordinateUpdate` to leverage
// `#doVersionUpdate`.
final Object newVersion;

if ( incomingDirtyAttributeIndexes != null ) {
if ( incomingDirtyAttributeIndexes.length == 1
&& versionMapping.getVersionAttribute() == entityPersister().getAttributeMapping( incomingDirtyAttributeIndexes[0] ) ) {
// special case of only the version attribute itself as dirty
isSimpleVersionUpdate = true;
newVersion = values[ incomingDirtyAttributeIndexes[0]];
}
else if ( incomingDirtyAttributeIndexes.length == 0 && oldVersion != null ) {
isSimpleVersionUpdate = !versionMapping.areEqual(
values[ versionMapping.getVersionAttribute().getStateArrayPosition() ],
oldVersion,
session
);
newVersion = values[ versionMapping.getVersionAttribute().getStateArrayPosition()];
}
else {
isSimpleVersionUpdate = false;
newVersion = null;
}
}
else {
isSimpleVersionUpdate = false;
newVersion = null;
if ( hasUpdateGeneratedValues() ) {
// if we have any fields generated by the UPDATE event,
// then we have to include the generated fields in the
// update statement
return null;
}

if ( isSimpleVersionUpdate ) {
// we have just the version being updated - use the special handling
assert newVersion != null;
final GeneratedValues generatedValues = doVersionUpdate( entity, id, newVersion, oldVersion, session );
return () -> generatedValues;
else if ( incomingDirtyAttributeIndexes != null ) {
switch ( incomingDirtyAttributeIndexes.length ) {
case 1:
final int dirtyAttributeIndex = incomingDirtyAttributeIndexes[0];
final var versionAttribute = versionMapping.getVersionAttribute();
final var dirtyAttribute = entityPersister().getAttributeMapping( dirtyAttributeIndex );
if ( versionAttribute == dirtyAttribute ) {
// only the version attribute itself is dirty
newVersion = values[dirtyAttributeIndex];
}
else {
// the dirty field is some other field
return null;
}
break;
case 0:
if ( oldVersion != null ) {
newVersion = values[versionMapping.getVersionAttribute().getStateArrayPosition()];
if ( versionMapping.areEqual( newVersion, oldVersion, session ) ) {
return null;
}
}
else {
return null;
}
break;
default:
return null;
}
}
else {
return null;
}

// we have just the version being updated - use the special handling
assert newVersion != null;
final GeneratedValues generatedValues = doVersionUpdate( entity, id, newVersion, oldVersion, session );
return () -> generatedValues;
}

private boolean hasUpdateGeneratedValues() {
final var entityMetamodel = entityPersister().getEntityMetamodel();
return entityMetamodel.hasUpdateGeneratedValues()
|| entityMetamodel.hasPreUpdateGeneratedValues();
}

private static boolean isValueGenerated(Generator generator) {
return generator != null
&& generator.generatesOnUpdate()
&& generator.generatedOnExecution();
&& generator.generatesOnUpdate()
&& generator.generatedOnExecution();
}

private static boolean isValueGenerationInSql(Generator generator, Dialect dialect) {
assert isValueGenerated( generator );
return ( (OnExecutionGenerator) generator ).referenceColumnsInSql(dialect);
return ( (OnExecutionGenerator) generator ).referenceColumnsInSql( dialect );
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.mapping.generated;

import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.junit.jupiter.api.Test;

import java.time.LocalDateTime;
import java.util.Set;

import static org.hibernate.annotations.SourceType.DB;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Jpa(annotatedClasses = GeneratedByDbOnForcedIncrementTest.WithUpdateTimestamp.class)
class GeneratedByDbOnForcedIncrementTest {
@Test void test(EntityManagerFactoryScope scope) throws InterruptedException {
var persisted = scope.fromTransaction( em -> {
var entity = new WithUpdateTimestamp();
em.persist( entity );
return entity;
} );
Thread.sleep( 100 );
var updated = scope.fromTransaction( em -> {
var entity = em.find( WithUpdateTimestamp.class, 0L );
entity.names.add( "Gavin" );
return entity;
} );
assertTrue( persisted.updated.isBefore( updated.updated ) );
}
@Entity
static class WithUpdateTimestamp {
@Id long id;
@Version long version;
@UpdateTimestamp(source = DB)
LocalDateTime updated;
@ElementCollection
Set<String> names;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.mapping.generated;

import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.junit.jupiter.api.Test;

import java.time.LocalDateTime;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertTrue;

@Jpa(annotatedClasses = GeneratedOnForcedIncrementTest.WithUpdateTimestamp.class)
class GeneratedOnForcedIncrementTest {
@Test void test(EntityManagerFactoryScope scope) throws InterruptedException {
var persisted = scope.fromTransaction( em -> {
var entity = new WithUpdateTimestamp();
em.persist( entity );
return entity;
} );
Thread.sleep( 100 );
var updated = scope.fromTransaction( em -> {
var entity = em.find( WithUpdateTimestamp.class, 0L );
entity.names.add( "Gavin" );
return entity;
} );
assertTrue( persisted.updated.isBefore( updated.updated ) );
}
@Entity
static class WithUpdateTimestamp {
@Id long id;
@Version long version;
@UpdateTimestamp
LocalDateTime updated;
@ElementCollection
Set<String> names;
}
}
Loading