fixed source of NPE wrt accessing an entity's version attribute descriptor when no versioning is defined;

added `@org.hibernate.testing.orm.junit.SessionFactory#exportSchema`
This commit is contained in:
Steve Ebersole 2019-09-26 14:14:19 -05:00
parent 12ca8a2a81
commit b9f4562680
6 changed files with 151 additions and 12 deletions

View File

@ -141,10 +141,10 @@ import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NaturalIdMapping; import org.hibernate.metamodel.mapping.NaturalIdMapping;
import org.hibernate.metamodel.mapping.Queryable; import org.hibernate.metamodel.mapping.Queryable;
import org.hibernate.metamodel.mapping.StateArrayContributorMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType; import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.EntityRepresentationStrategy; import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
@ -159,10 +159,7 @@ import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.property.access.spi.Setter; import org.hibernate.property.access.spi.Setter;
import org.hibernate.query.ComparisonOperator; import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.sql.SqlAstCreationState;
import org.hibernate.query.sqm.sql.SqlExpressionResolver; import org.hibernate.query.sqm.sql.SqlExpressionResolver;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.sql.Alias; import org.hibernate.sql.Alias;
import org.hibernate.sql.Delete; import org.hibernate.sql.Delete;
import org.hibernate.sql.Insert; import org.hibernate.sql.Insert;
@ -174,7 +171,6 @@ import org.hibernate.sql.SimpleSelect;
import org.hibernate.sql.Template; import org.hibernate.sql.Template;
import org.hibernate.sql.Update; import org.hibernate.sql.Update;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.FromClauseAccess;
import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasBase;
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAliasStemHelper; import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
@ -5357,7 +5353,12 @@ public abstract class AbstractEntityPersister
@Override @Override
public Object getVersion(Object object) { public Object getVersion(Object object) {
return getVersionMapping().getAttributeMetadataAccess().resolveAttributeMetadata( this ).getPropertyAccess().getGetter().get( object ); if ( getVersionMapping() == null ) {
return null;
}
final StateArrayContributorMetadata attrMetadata = getVersionMapping().getAttributeMetadataAccess().resolveAttributeMetadata( this );
assert attrMetadata != null;
return attrMetadata.getPropertyAccess().getGetter().get( object );
} }
@Override @Override

View File

@ -0,0 +1,87 @@
/*
* 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.loading;
import org.hibernate.Hibernate;
import org.hibernate.boot.MetadataSources;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryFunctionalTesting;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.hamcrest.MatcherAssert;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author Steve Ebersole
*/
@DomainModel(
standardModels = StandardDomainModel.GAMBIT
)
@SessionFactory
@SessionFactoryFunctionalTesting
@SuppressWarnings("WeakerAccess")
public class LoadingSmokeTests {
@Test
public void testBasicLoad(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final BasicEntity loaded = session.byId( BasicEntity.class ).getReference( 1 );
assertThat( loaded, notNullValue() );
assertFalse( Hibernate.isInitialized( loaded ) );
}
);
}
@Test
@FailureExpected( reason = "read-by-position not yet implemented for loading" )
public void testBasicGet(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final BasicEntity gotten = session.byId( BasicEntity.class ).load( 1 );
assertThat( gotten, notNullValue() );
assertTrue( Hibernate.isInitialized( gotten ) );
}
);
}
@BeforeAll
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.persist( new BasicEntity( 1, "first" ) );
session.persist( new BasicEntity( 2, "second" ) );
}
);
}
@AfterAll
public void deleteTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.doWork(
connection -> {
connection.prepareStatement( "delete from BasicEntity" ).execute();
}
);
}
);
}
}

View File

@ -32,7 +32,7 @@ public class DomainModelExtension
return JUnitHelper.locateExtensionStore( ServiceRegistryExtension.class, context, testInstance ); return JUnitHelper.locateExtensionStore( ServiceRegistryExtension.class, context, testInstance );
} }
public static DomainModelScope findMetamodelScope(Object testInstance, ExtensionContext context) { public static DomainModelScope findDomainModelScope(Object testInstance, ExtensionContext context) {
final ExtensionContext.Store store = locateExtensionStore( testInstance, context ); final ExtensionContext.Store store = locateExtensionStore( testInstance, context );
final DomainModelScope existing = (DomainModelScope) store.get( MODEL_KEY ); final DomainModelScope existing = (DomainModelScope) store.get( MODEL_KEY );
if ( existing != null ) { if ( existing != null ) {
@ -120,7 +120,7 @@ public class DomainModelExtension
@Override @Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) { public void postProcessTestInstance(Object testInstance, ExtensionContext context) {
findMetamodelScope( testInstance, context ); findDomainModelScope( testInstance, context );
} }
@Override @Override

View File

@ -28,7 +28,7 @@ public class DomainModelParameterResolver implements ParameterResolver {
public Object resolveParameter( public Object resolveParameter(
ParameterContext parameterContext, ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException { ExtensionContext extensionContext) throws ParameterResolutionException {
final DomainModelScope modelScope = DomainModelExtension.findMetamodelScope( final DomainModelScope modelScope = DomainModelExtension.findDomainModelScope(
extensionContext.getRequiredTestInstance(), extensionContext.getRequiredTestInstance(),
extensionContext extensionContext
); );

View File

@ -42,6 +42,7 @@ public @interface SessionFactory {
String sessionFactoryName() default ""; String sessionFactoryName() default "";
boolean generateStatistics() default false; boolean generateStatistics() default false;
boolean exportSchema() default true;
Class<? extends Interceptor> interceptorClass() default Interceptor.class; Class<? extends Interceptor> interceptorClass() default Interceptor.class;

View File

@ -6,17 +6,26 @@
*/ */
package org.hibernate.testing.orm.junit; package org.hibernate.testing.orm.junit;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import org.hibernate.Interceptor; import org.hibernate.Interceptor;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.Transaction; import org.hibernate.Transaction;
import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.resource.jdbc.spi.StatementInspector; import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.tool.schema.Action;
import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator;
import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.ActionGrouping;
import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
@ -49,6 +58,8 @@ public class SessionFactoryExtension
SessionFactoryProducer producer = null; SessionFactoryProducer producer = null;
final DomainModelScope domainModelScope = DomainModelExtension.findDomainModelScope( testInstance, context );
if ( testInstance instanceof SessionFactoryProducer ) { if ( testInstance instanceof SessionFactoryProducer ) {
producer = (SessionFactoryProducer) testInstance; producer = (SessionFactoryProducer) testInstance;
} }
@ -85,7 +96,13 @@ public class SessionFactoryExtension
); );
} }
return (SessionFactoryImplementor) sessionFactoryBuilder.build(); final SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) sessionFactoryBuilder.build();
if ( sessionFactoryConfig.exportSchema() ) {
prepareSchemaExport( sessionFactory, model );
}
return sessionFactory;
} }
catch (Exception e) { catch (Exception e) {
throw new RuntimeException( "Could not build SessionFactory", e ); throw new RuntimeException( "Could not build SessionFactory", e );
@ -98,9 +115,8 @@ public class SessionFactoryExtension
throw new IllegalStateException( "Could not determine SessionFactory producer" ); throw new IllegalStateException( "Could not determine SessionFactory producer" );
} }
final SessionFactoryScopeImpl sfScope = new SessionFactoryScopeImpl( final SessionFactoryScopeImpl sfScope = new SessionFactoryScopeImpl(
DomainModelExtension.findMetamodelScope( testInstance, context ), domainModelScope,
producer producer
); );
@ -113,6 +129,40 @@ public class SessionFactoryExtension
return sfScope; return sfScope;
} }
private static void prepareSchemaExport(
SessionFactoryImplementor sessionFactory,
MetadataImplementor model) {
final Map<String, Object> baseProperties = sessionFactory.getProperties();
final ActionGrouping actions = ActionGrouping.interpret( baseProperties );
// if there are explicit setting for auto schema tooling then skip the annotation
if ( actions.getDatabaseAction() != Action.NONE || actions.getScriptAction() != Action.NONE ) {
// the properties contained explicit settings for auto schema tooling - skip the annotation
return;
}
final HashMap settings = new HashMap<>( baseProperties );
//noinspection unchecked
settings.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, Action.CREATE_DROP );
final StandardServiceRegistry serviceRegistry = model.getMetadataBuildingOptions().getServiceRegistry();
SchemaManagementToolCoordinator.process(
model,
serviceRegistry,
settings,
action -> sessionFactory.addObserver(
new SessionFactoryObserver() {
@Override
public void sessionFactoryClosing(org.hibernate.SessionFactory factory) {
action.perform( serviceRegistry );
}
}
)
);
}
@Override @Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) { public void postProcessTestInstance(Object testInstance, ExtensionContext context) {
log.tracef( "#postProcessTestInstance(%s, %s)", testInstance, context.getDisplayName() ); log.tracef( "#postProcessTestInstance(%s, %s)", testInstance, context.getDisplayName() );