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.Queryable;
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.MappingModelCreationHelper;
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.spi.EntityRepresentationStrategy;
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.query.ComparisonOperator;
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.tree.domain.SqmPath;
import org.hibernate.sql.Alias;
import org.hibernate.sql.Delete;
import org.hibernate.sql.Insert;
@ -174,7 +171,6 @@ import org.hibernate.sql.SimpleSelect;
import org.hibernate.sql.Template;
import org.hibernate.sql.Update;
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.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
@ -5357,7 +5353,12 @@ public abstract class AbstractEntityPersister
@Override
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

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 );
}
public static DomainModelScope findMetamodelScope(Object testInstance, ExtensionContext context) {
public static DomainModelScope findDomainModelScope(Object testInstance, ExtensionContext context) {
final ExtensionContext.Store store = locateExtensionStore( testInstance, context );
final DomainModelScope existing = (DomainModelScope) store.get( MODEL_KEY );
if ( existing != null ) {
@ -120,7 +120,7 @@ public class DomainModelExtension
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) {
findMetamodelScope( testInstance, context );
findDomainModelScope( testInstance, context );
}
@Override

View File

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

View File

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

View File

@ -6,17 +6,26 @@
*/
package org.hibernate.testing.orm.junit;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import org.hibernate.Interceptor;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.Transaction;
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.SessionImplementor;
import org.hibernate.internal.util.StringHelper;
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.ExtensionContext;
@ -49,6 +58,8 @@ public class SessionFactoryExtension
SessionFactoryProducer producer = null;
final DomainModelScope domainModelScope = DomainModelExtension.findDomainModelScope( testInstance, context );
if ( testInstance instanceof SessionFactoryProducer ) {
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) {
throw new RuntimeException( "Could not build SessionFactory", e );
@ -98,9 +115,8 @@ public class SessionFactoryExtension
throw new IllegalStateException( "Could not determine SessionFactory producer" );
}
final SessionFactoryScopeImpl sfScope = new SessionFactoryScopeImpl(
DomainModelExtension.findMetamodelScope( testInstance, context ),
domainModelScope,
producer
);
@ -113,6 +129,40 @@ public class SessionFactoryExtension
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
public void postProcessTestInstance(Object testInstance, ExtensionContext context) {
log.tracef( "#postProcessTestInstance(%s, %s)", testInstance, context.getDisplayName() );