HHH-13042 HHH-13044 HHH-13053 - Fix to short-circuit delayed identifier insert forcing them to insert early.
This commit is contained in:
parent
1e8ccb6cce
commit
d7d6e2882f
|
@ -12,6 +12,7 @@ import org.hibernate.AssertionFailure;
|
||||||
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
|
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
|
||||||
import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
|
import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
|
||||||
import org.hibernate.action.spi.Executable;
|
import org.hibernate.action.spi.Executable;
|
||||||
|
import org.hibernate.engine.spi.EntityEntry;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.event.service.spi.EventListenerGroup;
|
import org.hibernate.event.service.spi.EventListenerGroup;
|
||||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||||
|
@ -100,7 +101,8 @@ public abstract class EntityAction
|
||||||
*/
|
*/
|
||||||
public final Serializable getId() {
|
public final Serializable getId() {
|
||||||
if ( id instanceof DelayedPostInsertIdentifier ) {
|
if ( id instanceof DelayedPostInsertIdentifier ) {
|
||||||
final Serializable eeId = session.getPersistenceContext().getEntry( instance ).getId();
|
final EntityEntry entry = session.getPersistenceContext().getEntry( instance );
|
||||||
|
final Serializable eeId = entry == null ? null : entry.getId();
|
||||||
return eeId instanceof DelayedPostInsertIdentifier ? null : eeId;
|
return eeId instanceof DelayedPostInsertIdentifier ? null : eeId;
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
|
|
|
@ -83,6 +83,7 @@ import static org.hibernate.cfg.AvailableSettings.CUSTOM_ENTITY_DIRTINESS_STRATE
|
||||||
import static org.hibernate.cfg.AvailableSettings.DEFAULT_BATCH_FETCH_SIZE;
|
import static org.hibernate.cfg.AvailableSettings.DEFAULT_BATCH_FETCH_SIZE;
|
||||||
import static org.hibernate.cfg.AvailableSettings.DEFAULT_ENTITY_MODE;
|
import static org.hibernate.cfg.AvailableSettings.DEFAULT_ENTITY_MODE;
|
||||||
import static org.hibernate.cfg.AvailableSettings.DELAY_ENTITY_LOADER_CREATIONS;
|
import static org.hibernate.cfg.AvailableSettings.DELAY_ENTITY_LOADER_CREATIONS;
|
||||||
|
import static org.hibernate.cfg.AvailableSettings.DISABLE_DELAYED_IDENTIFIER_POST_INSERTS;
|
||||||
import static org.hibernate.cfg.AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS;
|
import static org.hibernate.cfg.AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS;
|
||||||
import static org.hibernate.cfg.AvailableSettings.FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH;
|
import static org.hibernate.cfg.AvailableSettings.FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH;
|
||||||
import static org.hibernate.cfg.AvailableSettings.FLUSH_BEFORE_COMPLETION;
|
import static org.hibernate.cfg.AvailableSettings.FLUSH_BEFORE_COMPLETION;
|
||||||
|
@ -193,7 +194,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
||||||
private NullPrecedence defaultNullPrecedence;
|
private NullPrecedence defaultNullPrecedence;
|
||||||
private boolean orderUpdatesEnabled;
|
private boolean orderUpdatesEnabled;
|
||||||
private boolean orderInsertsEnabled;
|
private boolean orderInsertsEnabled;
|
||||||
|
private boolean postInsertIdentifierDelayed;
|
||||||
|
|
||||||
// multi-tenancy
|
// multi-tenancy
|
||||||
private MultiTenancyStrategy multiTenancyStrategy;
|
private MultiTenancyStrategy multiTenancyStrategy;
|
||||||
|
@ -341,6 +342,9 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
||||||
this.defaultNullPrecedence = NullPrecedence.parse( defaultNullPrecedence );
|
this.defaultNullPrecedence = NullPrecedence.parse( defaultNullPrecedence );
|
||||||
this.orderUpdatesEnabled = ConfigurationHelper.getBoolean( ORDER_UPDATES, configurationSettings );
|
this.orderUpdatesEnabled = ConfigurationHelper.getBoolean( ORDER_UPDATES, configurationSettings );
|
||||||
this.orderInsertsEnabled = ConfigurationHelper.getBoolean( ORDER_INSERTS, configurationSettings );
|
this.orderInsertsEnabled = ConfigurationHelper.getBoolean( ORDER_INSERTS, configurationSettings );
|
||||||
|
this.postInsertIdentifierDelayed = !ConfigurationHelper.getBoolean(
|
||||||
|
DISABLE_DELAYED_IDENTIFIER_POST_INSERTS, configurationSettings, false
|
||||||
|
);
|
||||||
|
|
||||||
this.jtaTrackByThread = cfgService.getSetting( JTA_TRACK_BY_THREAD, BOOLEAN, true );
|
this.jtaTrackByThread = cfgService.getSetting( JTA_TRACK_BY_THREAD, BOOLEAN, true );
|
||||||
|
|
||||||
|
@ -1044,6 +1048,11 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
||||||
return queryStatisticsMaxSize;
|
return queryStatisticsMaxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPostInsertIdentifierDelayableEnabled() {
|
||||||
|
return postInsertIdentifierDelayed;
|
||||||
|
}
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// In-flight mutation access
|
// In-flight mutation access
|
||||||
|
|
||||||
|
@ -1175,6 +1184,10 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
||||||
this.orderUpdatesEnabled = enabled;
|
this.orderUpdatesEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void enableDelayedIdentityInserts(boolean enabled) {
|
||||||
|
this.postInsertIdentifierDelayed = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
public void applyMultiTenancyStrategy(MultiTenancyStrategy strategy) {
|
public void applyMultiTenancyStrategy(MultiTenancyStrategy strategy) {
|
||||||
this.multiTenancyStrategy = strategy;
|
this.multiTenancyStrategy = strategy;
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,4 +295,8 @@ public interface SessionFactoryOptions {
|
||||||
default int getQueryStatisticsMaxSize() {
|
default int getQueryStatisticsMaxSize() {
|
||||||
return Statistics.DEFAULT_QUERY_STATISTICS_MAX_SIZE;
|
return Statistics.DEFAULT_QUERY_STATISTICS_MAX_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default boolean isPostInsertIdentifierDelayableEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1992,4 +1992,19 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
||||||
* @since 5.4
|
* @since 5.4
|
||||||
*/
|
*/
|
||||||
String SEQUENCE_INCREMENT_SIZE_MISMATCH_STRATEGY = "hibernate.id.sequence.increment_size_mismatch_strategy";
|
String SEQUENCE_INCREMENT_SIZE_MISMATCH_STRATEGY = "hibernate.id.sequence.increment_size_mismatch_strategy";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This setting controls whether the default behavior for delayed identity inserts is disabled.
|
||||||
|
* </p>
|
||||||
|
* Hibernate defines a set of rules that are used to determine if an insert statement that has
|
||||||
|
* an identity-based column can be delayed until flush/commit or if the statement must be
|
||||||
|
* executed early because access to the identity column is needed.
|
||||||
|
* </p>
|
||||||
|
* In the event that those defined rules are insufficient for a given mapping scenario, this
|
||||||
|
* setting can be set to {@code true} to permanently disable delayed identity inserts for all
|
||||||
|
* transactions as a short-term workaround.
|
||||||
|
* </p>
|
||||||
|
* The default value is {@code false}.
|
||||||
|
*/
|
||||||
|
String DISABLE_DELAYED_IDENTIFIER_POST_INSERTS = "hibernate.id.disable_delayed_identifier_post_inserts";
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,7 +248,7 @@ public abstract class AbstractSaveEventListener
|
||||||
|
|
||||||
Serializable id = key == null ? null : key.getIdentifier();
|
Serializable id = key == null ? null : key.getIdentifier();
|
||||||
|
|
||||||
boolean shouldDelayIdentityInserts = shouldDelayIdentityInserts( requiresImmediateIdAccess, source );
|
boolean shouldDelayIdentityInserts = shouldDelayIdentityInserts( requiresImmediateIdAccess, source, persister );
|
||||||
|
|
||||||
// Put a placeholder in entries, so we don't recurse back and try to save() the
|
// Put a placeholder in entries, so we don't recurse back and try to save() the
|
||||||
// same object again. QUESTION: should this be done before onSave() is called?
|
// same object again. QUESTION: should this be done before onSave() is called?
|
||||||
|
@ -320,14 +320,19 @@ public abstract class AbstractSaveEventListener
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean shouldDelayIdentityInserts(boolean requiresImmediateIdAccess, EventSource source) {
|
private static boolean shouldDelayIdentityInserts(boolean requiresImmediateIdAccess, EventSource source, EntityPersister persister) {
|
||||||
return shouldDelayIdentityInserts( requiresImmediateIdAccess, isPartOfTransaction( source ), source.getHibernateFlushMode() );
|
return shouldDelayIdentityInserts( requiresImmediateIdAccess, isPartOfTransaction( source ), source.getHibernateFlushMode(), persister );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean shouldDelayIdentityInserts(
|
private static boolean shouldDelayIdentityInserts(
|
||||||
boolean requiresImmediateIdAccess,
|
boolean requiresImmediateIdAccess,
|
||||||
boolean partOfTransaction,
|
boolean partOfTransaction,
|
||||||
FlushMode flushMode) {
|
FlushMode flushMode,
|
||||||
|
EntityPersister persister) {
|
||||||
|
if ( !persister.getFactory().getSessionFactoryOptions().isPostInsertIdentifierDelayableEnabled() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ( requiresImmediateIdAccess ) {
|
if ( requiresImmediateIdAccess ) {
|
||||||
// todo : make this configurable? as a way to support this behavior with Session#save etc
|
// todo : make this configurable? as a way to support this behavior with Session#save etc
|
||||||
return false;
|
return false;
|
||||||
|
@ -336,8 +341,19 @@ public abstract class AbstractSaveEventListener
|
||||||
// otherwise we should delay the IDENTITY insertions if either:
|
// otherwise we should delay the IDENTITY insertions if either:
|
||||||
// 1) we are not part of a transaction
|
// 1) we are not part of a transaction
|
||||||
// 2) we are in FlushMode MANUAL or COMMIT (not AUTO nor ALWAYS)
|
// 2) we are in FlushMode MANUAL or COMMIT (not AUTO nor ALWAYS)
|
||||||
return !partOfTransaction || flushMode == MANUAL || flushMode == COMMIT;
|
if ( !partOfTransaction || flushMode == MANUAL || flushMode == COMMIT ) {
|
||||||
|
if ( persister.canIdentityInsertBeDelayed() ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOG.debugf(
|
||||||
|
"Identity insert for entity [%s] should be delayed; however the persister requested early insert.",
|
||||||
|
persister.getEntityName()
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isPartOfTransaction(EventSource source) {
|
private static boolean isPartOfTransaction(EventSource source) {
|
||||||
|
|
|
@ -76,9 +76,11 @@ import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.engine.spi.ValueInclusion;
|
import org.hibernate.engine.spi.ValueInclusion;
|
||||||
|
import org.hibernate.id.ForeignGenerator;
|
||||||
import org.hibernate.id.IdentifierGenerator;
|
import org.hibernate.id.IdentifierGenerator;
|
||||||
import org.hibernate.id.PostInsertIdentifierGenerator;
|
import org.hibernate.id.PostInsertIdentifierGenerator;
|
||||||
import org.hibernate.id.PostInsertIdentityPersister;
|
import org.hibernate.id.PostInsertIdentityPersister;
|
||||||
|
import org.hibernate.id.enhanced.SequenceStyleGenerator;
|
||||||
import org.hibernate.id.insert.Binder;
|
import org.hibernate.id.insert.Binder;
|
||||||
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
|
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
|
@ -106,6 +108,7 @@ import org.hibernate.mapping.Table;
|
||||||
import org.hibernate.metadata.ClassMetadata;
|
import org.hibernate.metadata.ClassMetadata;
|
||||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
|
import org.hibernate.persister.collection.QueryableCollection;
|
||||||
import org.hibernate.persister.spi.PersisterCreationContext;
|
import org.hibernate.persister.spi.PersisterCreationContext;
|
||||||
import org.hibernate.persister.walking.internal.EntityIdentifierDefinitionHelper;
|
import org.hibernate.persister.walking.internal.EntityIdentifierDefinitionHelper;
|
||||||
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
|
@ -294,6 +297,8 @@ public abstract class AbstractEntityPersister
|
||||||
|
|
||||||
private final boolean useReferenceCacheEntries;
|
private final boolean useReferenceCacheEntries;
|
||||||
|
|
||||||
|
private boolean canIdentityInsertBeDelayed;
|
||||||
|
|
||||||
protected void addDiscriminatorToInsert(Insert insert) {
|
protected void addDiscriminatorToInsert(Insert insert) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -956,6 +961,11 @@ public abstract class AbstractEntityPersister
|
||||||
return useReferenceCacheEntries;
|
return useReferenceCacheEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canIdentityInsertBeDelayed() {
|
||||||
|
return canIdentityInsertBeDelayed;
|
||||||
|
}
|
||||||
|
|
||||||
protected static String getTemplateFromString(String string, SessionFactoryImplementor factory) {
|
protected static String getTemplateFromString(String string, SessionFactoryImplementor factory) {
|
||||||
return string == null ?
|
return string == null ?
|
||||||
null :
|
null :
|
||||||
|
@ -4148,6 +4158,58 @@ public abstract class AbstractEntityPersister
|
||||||
return new SubstituteBracketSQLQueryParser( sql, getFactory() ).process();
|
return new SubstituteBracketSQLQueryParser( sql, getFactory() ).process();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void resolveIdentityInsertDelayable() {
|
||||||
|
// By default they can
|
||||||
|
// The remainder of this method checks use cases where we shouldn't permit it.
|
||||||
|
canIdentityInsertBeDelayed = true;
|
||||||
|
|
||||||
|
if ( getEntityMetamodel().getIdentifierProperty().isIdentifierAssignedByInsert() ) {
|
||||||
|
// if the persister's identifier is assigned by insert, we need to see if we must force non-delay mode.
|
||||||
|
for ( NonIdentifierAttribute attribute : getEntityMetamodel().getProperties() ) {
|
||||||
|
if ( isTypeSelfReferencing( attribute.getType() ) ) {
|
||||||
|
canIdentityInsertBeDelayed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTypeSelfReferencing(Type propertyType) {
|
||||||
|
if ( propertyType.isAssociationType() ) {
|
||||||
|
if ( propertyType.isEntityType() ) {
|
||||||
|
Class<?> entityClass = propertyType.getReturnedClass();
|
||||||
|
if ( getMappedClass().equals( propertyType.getReturnedClass() ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( propertyType.isCollectionType() ) {
|
||||||
|
// Association is a collection where owner needs identifier up-front
|
||||||
|
final CollectionType collection = (CollectionType) propertyType;
|
||||||
|
final CollectionPersister collectionPersister = getFactory().getMetamodel().collectionPersister( collection.getRole() );
|
||||||
|
if ( collectionPersister.isInverse() ) {
|
||||||
|
final EntityPersister entityPersister = collectionPersister.getOwnerEntityPersister();
|
||||||
|
if ( collectionPersister.getOwnerEntityPersister().equals( this ) ) {
|
||||||
|
final QueryableCollection queryableCollection = (QueryableCollection) collectionPersister;
|
||||||
|
final IdentifierGenerator identifierGenerator = queryableCollection.getElementPersister().getIdentifierGenerator();
|
||||||
|
// todo - perhaps this can be simplified
|
||||||
|
if ( ( identifierGenerator instanceof ForeignGenerator ) || ( identifierGenerator instanceof SequenceStyleGenerator ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( propertyType.isComponentType() ) {
|
||||||
|
final CompositeType componentType = (CompositeType) propertyType;
|
||||||
|
for ( Type componentSubType : componentType.getSubtypes() ) {
|
||||||
|
if ( isTypeSelfReferencing( componentSubType ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public final void postInstantiate() throws MappingException {
|
public final void postInstantiate() throws MappingException {
|
||||||
doLateInit();
|
doLateInit();
|
||||||
|
|
||||||
|
@ -4155,6 +4217,8 @@ public abstract class AbstractEntityPersister
|
||||||
createUniqueKeyLoaders();
|
createUniqueKeyLoaders();
|
||||||
createQueryLoader();
|
createQueryLoader();
|
||||||
|
|
||||||
|
resolveIdentityInsertDelayable();
|
||||||
|
|
||||||
doPostInstantiate();
|
doPostInstantiate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -811,4 +811,8 @@ public interface EntityPersister extends EntityDefinition {
|
||||||
int[] resolveAttributeIndexes(String[] attributeNames);
|
int[] resolveAttributeIndexes(String[] attributeNames);
|
||||||
|
|
||||||
boolean canUseReferenceCacheEntries();
|
boolean canUseReferenceCacheEntries();
|
||||||
|
|
||||||
|
default boolean canIdentityInsertBeDelayed() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2014 JBoss Inc
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
* the License. You may obtain a copy of the License at
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
||||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.flush;
|
package org.hibernate.test.flush;
|
||||||
|
|
||||||
import javax.persistence.CascadeType;
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Embeddable;
|
||||||
|
import javax.persistence.Embedded;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
|
@ -40,7 +36,7 @@ public class TestFlushModeWithIdentitySelfReferenceTest extends BaseCoreFunction
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?>[] getAnnotatedClasses() {
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
return new Class<?>[] { SelfRefEntity.class };
|
return new Class<?>[] { SelfRefEntity.class, SelfRefEntityWithEmbeddable.class };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -49,6 +45,7 @@ public class TestFlushModeWithIdentitySelfReferenceTest extends BaseCoreFunction
|
||||||
try {
|
try {
|
||||||
session.setHibernateFlushMode( FlushMode.COMMIT );
|
session.setHibernateFlushMode( FlushMode.COMMIT );
|
||||||
loadAndAssert( session, createAndInsertEntity( session ) );
|
loadAndAssert( session, createAndInsertEntity( session ) );
|
||||||
|
loadAndInsert( session, createAndInsertEntityEmbeddable( session ) );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
session.close();
|
session.close();
|
||||||
|
@ -61,6 +58,7 @@ public class TestFlushModeWithIdentitySelfReferenceTest extends BaseCoreFunction
|
||||||
try {
|
try {
|
||||||
session.setHibernateFlushMode( FlushMode.MANUAL );
|
session.setHibernateFlushMode( FlushMode.MANUAL );
|
||||||
loadAndAssert( session, createAndInsertEntity( session ) );
|
loadAndAssert( session, createAndInsertEntity( session ) );
|
||||||
|
loadAndInsert( session, createAndInsertEntityEmbeddable( session ) );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
session.close();
|
session.close();
|
||||||
|
@ -73,6 +71,7 @@ public class TestFlushModeWithIdentitySelfReferenceTest extends BaseCoreFunction
|
||||||
try {
|
try {
|
||||||
session.setHibernateFlushMode( FlushMode.AUTO );
|
session.setHibernateFlushMode( FlushMode.AUTO );
|
||||||
loadAndAssert( session, createAndInsertEntity( session ) );
|
loadAndAssert( session, createAndInsertEntity( session ) );
|
||||||
|
loadAndInsert( session, createAndInsertEntityEmbeddable( session ) );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
session.close();
|
session.close();
|
||||||
|
@ -86,6 +85,7 @@ public class TestFlushModeWithIdentitySelfReferenceTest extends BaseCoreFunction
|
||||||
SelfRefEntity entity = new SelfRefEntity();
|
SelfRefEntity entity = new SelfRefEntity();
|
||||||
entity.setSelfRefEntity( entity );
|
entity.setSelfRefEntity( entity );
|
||||||
entity.setData( "test" );
|
entity.setData( "test" );
|
||||||
|
|
||||||
entity = (SelfRefEntity) session.merge( entity );
|
entity = (SelfRefEntity) session.merge( entity );
|
||||||
|
|
||||||
// only during manual flush do we want to force a flush prior to commit
|
// only during manual flush do we want to force a flush prior to commit
|
||||||
|
@ -105,6 +105,37 @@ public class TestFlushModeWithIdentitySelfReferenceTest extends BaseCoreFunction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SelfRefEntityWithEmbeddable createAndInsertEntityEmbeddable(Session session) {
|
||||||
|
try {
|
||||||
|
session.getTransaction().begin();
|
||||||
|
|
||||||
|
SelfRefEntityWithEmbeddable entity = new SelfRefEntityWithEmbeddable();
|
||||||
|
entity.setData( "test" );
|
||||||
|
|
||||||
|
SelfRefEntityInfo info = new SelfRefEntityInfo();
|
||||||
|
info.setSeflRefEntityEmbedded( entity );
|
||||||
|
|
||||||
|
entity.setInfo( info );
|
||||||
|
|
||||||
|
entity = (SelfRefEntityWithEmbeddable) session.merge( entity );
|
||||||
|
|
||||||
|
// only during manual flush do we want to force a flush prior to commit
|
||||||
|
if ( session.getHibernateFlushMode().equals( FlushMode.MANUAL ) ) {
|
||||||
|
session.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getTransaction().commit();
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
catch ( Exception e ) {
|
||||||
|
if ( session.getTransaction().isActive() ) {
|
||||||
|
session.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void loadAndAssert(Session session, SelfRefEntity mergedEntity) {
|
private void loadAndAssert(Session session, SelfRefEntity mergedEntity) {
|
||||||
final SelfRefEntity loadedEntity = session.get( SelfRefEntity.class, mergedEntity.getId() );
|
final SelfRefEntity loadedEntity = session.get( SelfRefEntity.class, mergedEntity.getId() );
|
||||||
assertNotNull( "Expected to find the merged entity but did not.", loadedEntity );
|
assertNotNull( "Expected to find the merged entity but did not.", loadedEntity );
|
||||||
|
@ -112,6 +143,13 @@ public class TestFlushModeWithIdentitySelfReferenceTest extends BaseCoreFunction
|
||||||
assertNotNull( "Expected a non-null self reference", loadedEntity.getSelfRefEntity() );
|
assertNotNull( "Expected a non-null self reference", loadedEntity.getSelfRefEntity() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadAndInsert(Session session, SelfRefEntityWithEmbeddable mergedEntity) {
|
||||||
|
final SelfRefEntityWithEmbeddable loadedEntity = session.get( SelfRefEntityWithEmbeddable.class, mergedEntity.getId() );
|
||||||
|
assertNotNull( "Expected to find the merged entity but did not.", loadedEntity );
|
||||||
|
assertEquals( "test", loadedEntity.getData() );
|
||||||
|
assertNotNull( "Expected a non-null self reference in embeddable", loadedEntity.getInfo().getSeflRefEntityEmbedded() );
|
||||||
|
}
|
||||||
|
|
||||||
@Entity(name = "SelfRefEntity")
|
@Entity(name = "SelfRefEntity")
|
||||||
public static class SelfRefEntity {
|
public static class SelfRefEntity {
|
||||||
@Id
|
@Id
|
||||||
|
@ -145,4 +183,52 @@ public class TestFlushModeWithIdentitySelfReferenceTest extends BaseCoreFunction
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Entity(name = "SelfRefEntityWithEmbeddable")
|
||||||
|
public static class SelfRefEntityWithEmbeddable {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Integer id;
|
||||||
|
@Embedded
|
||||||
|
private SelfRefEntityInfo info;
|
||||||
|
private String data;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelfRefEntityInfo getInfo() {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInfo(SelfRefEntityInfo info) {
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(String data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
public static class SelfRefEntityInfo {
|
||||||
|
@OneToOne(cascade = CascadeType.ALL, optional = true)
|
||||||
|
private SelfRefEntityWithEmbeddable seflRefEntityEmbedded;
|
||||||
|
|
||||||
|
public SelfRefEntityWithEmbeddable getSeflRefEntityEmbedded() {
|
||||||
|
return seflRefEntityEmbedded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSeflRefEntityEmbedded(SelfRefEntityWithEmbeddable seflRefEntityEmbedded) {
|
||||||
|
this.seflRefEntityEmbedded = seflRefEntityEmbedded;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue