* Added JpaCompliance for @OrderBy handling

* Added `NonTransientException` checked during MappingModelCreationProcess "post init callback" handling to indicate non-recoverable errors
* Redesigned `MappingModelCreationProcess#executePostInitCallbacks` to account for `NonTransientException`
This commit is contained in:
Steve Ebersole 2020-04-30 09:13:37 -05:00
parent d180cb63d2
commit 41093ae66c
22 changed files with 484 additions and 88 deletions

View File

@ -102,4 +102,8 @@
|90005700
|org.hibernate.envers.boot.EnversBootLogger
|90005701
|90005800
|org.hibernate.metamodel.mapping.MappingModelCreationLogger
|===

View File

@ -728,6 +728,11 @@ public interface SessionFactoryBuilder {
*/
SessionFactoryBuilder enableJpaQueryCompliance(boolean enabled);
/**
* @see JpaCompliance#isJpaQueryComplianceEnabled()
*/
SessionFactoryBuilder enableJpaOrderByMappingCompliance(boolean enabled);
/**
* @see JpaCompliance#isJpaTransactionComplianceEnabled()
*/

View File

@ -416,6 +416,12 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
return this;
}
@Override
public SessionFactoryBuilder enableJpaOrderByMappingCompliance(boolean enabled) {
this.optionsBuilder.enableJpaOrderByMappingCompliance( enabled );
return this;
}
@Override
public SessionFactoryBuilder enableJpaTransactionCompliance(boolean enabled) {
this.optionsBuilder.enableJpaTransactionCompliance( enabled );

View File

@ -1478,6 +1478,14 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
mutableJpaCompliance().setCachingCompliance( enabled );
}
public void enableJpaOrderByMappingCompliance(boolean enabled) {
mutableJpaCompliance().setOrderByMappingCompliance( enabled );
}
public void enableGeneratorNameScopeCompliance(boolean enabled) {
mutableJpaCompliance().setGeneratorNameScopeCompliance( enabled );
}
public void disableRefreshDetachedEntity() {
this.allowRefreshDetachedEntity = false;
}

View File

@ -394,6 +394,12 @@ public abstract class AbstractDelegatingSessionFactoryBuilder<T extends SessionF
return getThis();
}
@Override
public SessionFactoryBuilder enableJpaOrderByMappingCompliance(boolean enabled) {
delegate.enableJpaOrderByMappingCompliance( enabled );
return getThis();
}
@Override
public SessionFactoryBuilder enableJpaTransactionCompliance(boolean enabled) {
delegate.enableJpaTransactionCompliance( enabled );

View File

@ -2006,6 +2006,19 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
*/
String JPA_LIST_COMPLIANCE = "hibernate.jpa.compliance.list";
/**
* Controls whether Hibernate should recognize what it considers a "bag"
* ({@link org.hibernate.collection.internal.PersistentBag}) as a List
* ({@link org.hibernate.collection.internal.PersistentList}) or as a bag.
*
* If enabled, we will recognize it as a List where {@link javax.persistence.OrderColumn}
* is just missing (and its defaults will apply).
*
* @see JpaCompliance#isJpaOrderByMappingComplianceEnabled() ()
* @since 6.0
*/
String JPA_ORDER_BY_MAPPING_COMPLIANCE = "hibernate.jpa.compliance.orderby";
/**
* JPA defines specific exceptions on specific methods when called on
* {@link javax.persistence.EntityManager} and {@link javax.persistence.EntityManagerFactory}

View File

@ -0,0 +1,15 @@
/*
* 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.jpa;
/**
* Contract for exceptions describing JPA compliance violations
*
* @author Steve Ebersole
*/
public interface JpaComplianceViolation {
}

View File

@ -12,22 +12,24 @@ import org.hibernate.jpa.spi.JpaCompliance;
* @author Andrea Boriero
*/
public class JpaComplianceImpl implements JpaCompliance {
private boolean queryCompliance;
private boolean transactionCompliance;
private boolean listCompliance;
private boolean closedCompliance;
private boolean proxyCompliance;
private boolean cachingCompliance;
private boolean globalGeneratorNameScopeCompliance;
private final boolean listCompliance;
private final boolean orderByMappingCompliance;
private final boolean proxyCompliance;
private final boolean globalGeneratorNameScopeCompliance;
private final boolean queryCompliance;
private final boolean transactionCompliance;
private final boolean closedCompliance;
private final boolean cachingCompliance;
private JpaComplianceImpl(
boolean listCompliance,
boolean orderByMappingCompliance,
boolean proxyCompliance,
boolean globalGeneratorNameScopeCompliance,
boolean queryCompliance,
boolean transactionCompliance,
boolean listCompliance,
boolean closedCompliance,
boolean proxyCompliance,
boolean cachingCompliance,
boolean globalGeneratorNameScopeCompliance) {
boolean cachingCompliance) {
this.queryCompliance = queryCompliance;
this.transactionCompliance = transactionCompliance;
this.listCompliance = listCompliance;
@ -35,6 +37,7 @@ public class JpaComplianceImpl implements JpaCompliance {
this.proxyCompliance = proxyCompliance;
this.cachingCompliance = cachingCompliance;
this.globalGeneratorNameScopeCompliance = globalGeneratorNameScopeCompliance;
this.orderByMappingCompliance = orderByMappingCompliance;
}
@Override
@ -72,18 +75,39 @@ public class JpaComplianceImpl implements JpaCompliance {
return globalGeneratorNameScopeCompliance;
}
@Override
public boolean isJpaOrderByMappingComplianceEnabled() {
return orderByMappingCompliance;
}
public static class JpaComplianceBuilder {
private boolean queryCompliance;
private boolean transactionCompliance;
private boolean listCompliance;
private boolean closedCompliance;
private boolean orderByMappingCompliance;
private boolean proxyCompliance;
private boolean cachingCompliance;
private boolean globalGeneratorNameScopeCompliance;
private boolean cachingCompliance;
private boolean transactionCompliance;
private boolean closedCompliance;
public JpaComplianceBuilder() {
}
public JpaComplianceBuilder setListCompliance(boolean listCompliance) {
this.listCompliance = listCompliance;
return this;
}
public JpaComplianceBuilder setOrderByMappingCompliance(boolean orderByMappingCompliance) {
this.orderByMappingCompliance = orderByMappingCompliance;
return this;
}
public JpaComplianceBuilder setProxyCompliance(boolean proxyCompliance) {
this.proxyCompliance = proxyCompliance;
return this;
}
public JpaComplianceBuilder setQueryCompliance(boolean queryCompliance) {
this.queryCompliance = queryCompliance;
return this;
@ -94,21 +118,11 @@ public class JpaComplianceImpl implements JpaCompliance {
return this;
}
public JpaComplianceBuilder setListCompliance(boolean listCompliance) {
this.listCompliance = listCompliance;
return this;
}
public JpaComplianceBuilder setClosedCompliance(boolean closedCompliance) {
this.closedCompliance = closedCompliance;
return this;
}
public JpaComplianceBuilder setProxyCompliance(boolean proxyCompliance) {
this.proxyCompliance = proxyCompliance;
return this;
}
public JpaComplianceBuilder setCachingCompliance(boolean cachingCompliance) {
this.cachingCompliance = cachingCompliance;
return this;
@ -121,13 +135,14 @@ public class JpaComplianceImpl implements JpaCompliance {
JpaCompliance createJpaCompliance() {
return new JpaComplianceImpl(
listCompliance,
orderByMappingCompliance,
proxyCompliance,
globalGeneratorNameScopeCompliance,
queryCompliance,
transactionCompliance,
listCompliance,
closedCompliance,
proxyCompliance,
cachingCompliance,
globalGeneratorNameScopeCompliance
cachingCompliance
);
}
}

View File

@ -17,50 +17,60 @@ import org.hibernate.jpa.spi.MutableJpaCompliance;
* @author Steve Ebersole
*/
public class MutableJpaComplianceImpl implements MutableJpaCompliance {
private boolean listCompliance;
private boolean orderByMappingCompliance;
private boolean proxyCompliance;
private boolean generatorNameScopeCompliance;
private boolean queryCompliance;
private boolean transactionCompliance;
private boolean listCompliance;
private boolean closedCompliance;
private boolean proxyCompliance;
private boolean cachingCompliance;
private final boolean globalGeneratorNameScopeCompliance;
@SuppressWarnings("ConstantConditions")
public MutableJpaComplianceImpl(Map configurationSettings, boolean jpaByDefault) {
final Object legacyQueryCompliance = configurationSettings.get( AvailableSettings.JPAQL_STRICT_COMPLIANCE );
queryCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_QUERY_COMPLIANCE,
configurationSettings,
ConfigurationHelper.toBoolean( legacyQueryCompliance, jpaByDefault )
);
transactionCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_TRANSACTION_COMPLIANCE,
configurationSettings,
jpaByDefault
);
listCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_LIST_COMPLIANCE,
configurationSettings,
jpaByDefault
);
closedCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_CLOSED_COMPLIANCE,
configurationSettings,
jpaByDefault
);
proxyCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_PROXY_COMPLIANCE,
configurationSettings,
jpaByDefault
);
cachingCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_CACHING_COMPLIANCE,
generatorNameScopeCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_ID_GENERATOR_GLOBAL_SCOPE_COMPLIANCE,
configurationSettings,
jpaByDefault
);
globalGeneratorNameScopeCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_ID_GENERATOR_GLOBAL_SCOPE_COMPLIANCE,
orderByMappingCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_ORDER_BY_MAPPING_COMPLIANCE,
configurationSettings,
jpaByDefault
);
queryCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_QUERY_COMPLIANCE,
configurationSettings,
ConfigurationHelper.toBoolean( legacyQueryCompliance, jpaByDefault )
);
transactionCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_TRANSACTION_COMPLIANCE,
configurationSettings,
jpaByDefault
);
closedCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_CLOSED_COMPLIANCE,
configurationSettings,
jpaByDefault
);
cachingCompliance = ConfigurationHelper.getBoolean(
AvailableSettings.JPA_CACHING_COMPLIANCE,
configurationSettings,
jpaByDefault
);
@ -98,46 +108,69 @@ public class MutableJpaComplianceImpl implements MutableJpaCompliance {
@Override
public boolean isGlobalGeneratorScopeEnabled() {
return globalGeneratorNameScopeCompliance;
return generatorNameScopeCompliance;
}
@Override
public boolean isJpaOrderByMappingComplianceEnabled() {
return orderByMappingCompliance;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Mutators
public void setQueryCompliance(boolean queryCompliance) {
this.queryCompliance = queryCompliance;
}
public void setTransactionCompliance(boolean transactionCompliance) {
this.transactionCompliance = transactionCompliance;
}
@Override
public void setListCompliance(boolean listCompliance) {
this.listCompliance = listCompliance;
}
public void setClosedCompliance(boolean closedCompliance) {
this.closedCompliance = closedCompliance;
@Override
public void setOrderByMappingCompliance(boolean orderByMappingCompliance) {
this.orderByMappingCompliance = orderByMappingCompliance;
}
@Override
public void setProxyCompliance(boolean proxyCompliance) {
this.proxyCompliance = proxyCompliance;
}
@Override
public void setGeneratorNameScopeCompliance(boolean enabled) {
this.generatorNameScopeCompliance = enabled;
}
public void setQueryCompliance(boolean queryCompliance) {
this.queryCompliance = queryCompliance;
}
@Override
public void setTransactionCompliance(boolean transactionCompliance) {
this.transactionCompliance = transactionCompliance;
}
@Override
public void setClosedCompliance(boolean closedCompliance) {
this.closedCompliance = closedCompliance;
}
@Override
public void setCachingCompliance(boolean cachingCompliance) {
this.cachingCompliance = cachingCompliance;
}
@Override
public JpaCompliance immutableCopy() {
JpaComplianceImpl.JpaComplianceBuilder builder = new JpaComplianceImpl.JpaComplianceBuilder();
builder.setQueryCompliance( queryCompliance )
.setTransactionCompliance( transactionCompliance )
.setListCompliance( listCompliance )
.setClosedCompliance( closedCompliance )
builder = builder.setListCompliance( listCompliance )
.setProxyCompliance( proxyCompliance )
.setCachingCompliance( cachingCompliance )
.setGlobalGeneratorNameCompliance( globalGeneratorNameScopeCompliance );
.setOrderByMappingCompliance( orderByMappingCompliance )
.setGlobalGeneratorNameCompliance( generatorNameScopeCompliance )
.setQueryCompliance( queryCompliance )
.setTransactionCompliance( transactionCompliance )
.setClosedCompliance( closedCompliance )
.setCachingCompliance( cachingCompliance );
return builder.createJpaCompliance();
}
}

View File

@ -97,4 +97,12 @@ public interface JpaCompliance {
* @return {@code true} indicates the generator name scope is considered global.
*/
boolean isGlobalGeneratorScopeEnabled();
/**
* Should we strictly handle {@link javax.persistence.OrderBy} expressions?
*
* JPA says the order-items can only be attribute references whereas Hibernate supports a wide range of items. With
* this enabled, Hibernate will throw a compliance error when a non-attribute-reference is used.
*/
boolean isJpaOrderByMappingComplianceEnabled();
}

View File

@ -10,17 +10,21 @@ package org.hibernate.jpa.spi;
* @author Steve Ebersole
*/
public interface MutableJpaCompliance extends JpaCompliance {
void setListCompliance(boolean listCompliance);
void setOrderByMappingCompliance(boolean orderByCompliance);
void setProxyCompliance(boolean proxyCompliance);
void setQueryCompliance(boolean queryCompliance);
void setTransactionCompliance(boolean transactionCompliance);
void setListCompliance(boolean listCompliance);
void setClosedCompliance(boolean closedCompliance);
void setProxyCompliance(boolean proxyCompliance);
void setCachingCompliance(boolean cachingCompliance);
void setGeneratorNameScopeCompliance(boolean generatorScopeCompliance);
JpaCompliance immutableCopy();
}

View File

@ -0,0 +1,30 @@
/*
* 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.metamodel.mapping;
import org.hibernate.query.QueryLogger;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.MessageLogger;
import org.jboss.logging.annotations.ValidIdRange;
/**
* Logger used during mapping-model creation
*
* @author Steve Ebersole
*/
@MessageLogger( projectCode = "HHH" )
@ValidIdRange( min = 90005701, max = 90005800 )
public interface MappingModelCreationLogger extends BasicLogger {
String LOGGER_NAME = "org.hibernate.orm.model.mapping.creation";
MappingModelCreationLogger LOGGER = Logger.getMessageLogger( MappingModelCreationLogger.class, LOGGER_NAME );
boolean TRACE_ENABLED = LOGGER.isTraceEnabled();
boolean DEBUG_ENABLED = LOGGER.isDebugEnabled();
}

View File

@ -0,0 +1,16 @@
/*
* 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.metamodel.mapping;
/**
* Marker interface for exceptions thrown during mapping-model creation which
* are not transient errors - they will never succeed
*
* @author Steve Ebersole
*/
public interface NonTransientException {
}

View File

@ -61,6 +61,7 @@ import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NonTransientException;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
@ -793,7 +794,11 @@ public class MappingModelCreationHelper {
catch (NotYetImplementedFor6Exception nye) {
throw nye;
}
catch (Exception wait) {
catch (Exception e) {
if ( e instanceof NonTransientException ) {
throw e;
}
return false;
}
}

View File

@ -10,7 +10,9 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.hibernate.metamodel.mapping.MappingModelCreationLogger;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NonTransientException;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.EntityPersister;
@ -69,25 +71,40 @@ public class MappingModelCreationProcess {
entityPersister.prepareMappingModel( this );
}
executePostInitCallbakcs( postInitCallbacks );
MappingModelCreationLogger.LOGGER.debugf( "Starting generic post-init callbacks" );
executePostInitCallbacks( postInitCallbacks );
executePostInitCallbakcs( foreignKeyPostInitCallbacks );
MappingModelCreationLogger.LOGGER.debugf( "Starting foreign-key post-init callbacks" );
executePostInitCallbacks( foreignKeyPostInitCallbacks );
}
private void executePostInitCallbakcs(List<PostInitCallback> postInitCallbacks) {
private void executePostInitCallbacks(List<PostInitCallback> postInitCallbacks) {
while ( postInitCallbacks != null && !postInitCallbacks.isEmpty() ) {
// copy to avoid CCME
final ArrayList<PostInitCallback> copy = new ArrayList<>( new ArrayList<>( postInitCallbacks ) );
for ( PostInitCallback callback : copy ) {
final boolean completed = callback.process();
if ( completed ) {
postInitCallbacks.remove( callback );
// copy to avoid CCME
final ArrayList<PostInitCallback> copy = new ArrayList<>( postInitCallbacks );
for ( int i = 0; i < copy.size(); i++ ) {
final PostInitCallback callback = copy.get( i );
try {
final boolean completed = callback.process();
if ( completed ) {
postInitCallbacks.remove( callback );
}
}
catch (Exception e) {
if ( e instanceof NonTransientException ) {
MappingModelCreationLogger.LOGGER.debugf( "Mapping-model creation encountered non-transient error : %s", e );
throw e;
}
MappingModelCreationLogger.LOGGER.debugf( "Mapping-model creation encountered (possibly) transient error : %s", e );
}
}
if ( copy.size() == postInitCallbacks.size() ) {
// none of the processes could complete fully, this is an error
// none of the remaining callbacks could complete fully, this is an error
throw new IllegalStateException( "No post-init callbacks could complete" );
}
}

View File

@ -17,6 +17,7 @@ import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.jpa.spi.JpaCompliance;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.IndexedCollection;
import org.hibernate.mapping.List;
@ -246,6 +247,10 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
if ( hasOrder || hasManyToManyOrder ) {
final TranslationContext context = new TranslationContext() {
@Override
public JpaCompliance getJpaCompliance() {
return collectionDescriptor.getFactory().getSessionFactoryOptions().getJpaCompliance();
}
};
if ( hasOrder ) {

View File

@ -6,10 +6,13 @@
*/
package org.hibernate.metamodel.mapping.ordering;
import org.hibernate.jpa.spi.JpaCompliance;
/**
* Access to information needed while translating a collection's order-by fragment
*
* @author Steve Ebersole
*/
public interface TranslationContext {
JpaCompliance getJpaCompliance();
}

View File

@ -0,0 +1,24 @@
/*
* 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.metamodel.mapping.ordering.ast;
import org.hibernate.HibernateException;
import org.hibernate.jpa.JpaComplianceViolation;
import org.hibernate.metamodel.mapping.NonTransientException;
/**
* @author Steve Ebersole
*/
public class OrderByComplianceViolation extends HibernateException implements JpaComplianceViolation, NonTransientException {
public OrderByComplianceViolation(String message) {
super( message );
}
public OrderByComplianceViolation(String message, Throwable cause) {
super( message, cause );
}
}

View File

@ -17,16 +17,12 @@ import org.hibernate.grammars.ordering.OrderingParserBaseVisitor;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.ordering.TranslationContext;
import org.jboss.logging.Logger;
import org.antlr.v4.runtime.tree.TerminalNode;
/**
* @author Steve Ebersole
*/
public class ParseTreeVisitor extends OrderingParserBaseVisitor {
private static final Logger log = Logger.getLogger( ParseTreeVisitor.class );
public class ParseTreeVisitor extends OrderingParserBaseVisitor<Object> {
private final PathConsumer pathConsumer;
private final TranslationContext translationContext;
@ -60,7 +56,21 @@ public class ParseTreeVisitor extends OrderingParserBaseVisitor {
assert parsedSpec != null;
assert parsedSpec.expression() != null;
final OrderingSpecification result = new OrderingSpecification( visitExpression( parsedSpec.expression() ) );
final OrderingExpression orderingExpression = visitExpression( parsedSpec.expression() );
if ( translationContext.getJpaCompliance().isJpaOrderByMappingComplianceEnabled() ) {
if ( orderingExpression instanceof DomainPath ) {
// nothing to do
}
else {
throw new OrderByComplianceViolation(
"`@OrderBy` expression (" + parsedSpec.expression().getText()
+ ") resolved to `" + orderingExpression
+ "` which is not a domain-model reference which violates the JPA specification"
);
}
}
final OrderingSpecification result = new OrderingSpecification( orderingExpression );
if ( parsedSpec.collationSpecification() != null ) {
result.setCollation( parsedSpec.collationSpecification().identifier().getText() );

View File

@ -0,0 +1,56 @@
/*
* 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.jpa;
import org.hibernate.jpa.spi.JpaCompliance;
/**
* Stubbed impl of JpaCompliance
*
* @author Steve Ebersole
*/
public class JpaComplianceStub implements JpaCompliance {
@Override
public boolean isJpaQueryComplianceEnabled() {
return false;
}
@Override
public boolean isJpaTransactionComplianceEnabled() {
return false;
}
@Override
public boolean isJpaListComplianceEnabled() {
return false;
}
@Override
public boolean isJpaClosedComplianceEnabled() {
return false;
}
@Override
public boolean isJpaProxyComplianceEnabled() {
return false;
}
@Override
public boolean isJpaCacheComplianceEnabled() {
return false;
}
@Override
public boolean isGlobalGeneratorScopeEnabled() {
return false;
}
@Override
public boolean isJpaOrderByMappingComplianceEnabled() {
return false;
}
}

View File

@ -47,6 +47,7 @@ public class JpaComplianceTestingImpl implements JpaCompliance {
private boolean proxyCompliance;
private boolean cacheCompliance;
private boolean idGeneratorNameScopeCompliance;
private boolean orderByCompliance;
@Override
public boolean isJpaQueryComplianceEnabled() {
@ -63,6 +64,11 @@ public class JpaComplianceTestingImpl implements JpaCompliance {
return listCompliance;
}
@Override
public boolean isJpaOrderByMappingComplianceEnabled() {
return orderByCompliance;
}
@Override
public boolean isJpaClosedComplianceEnabled() {
return closedCompliance;

View File

@ -0,0 +1,107 @@
/*
* 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.bootstrap.binding.annotations.plural.orderby.compliance;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.ordering.ast.OrderByComplianceViolation;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.DomainModelScope;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.transaction.TransactionUtil2;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.fail;
/**
* @author Steve Ebersole
*/
public class OrderByMappingComplianceTest {
@Test
public void testComplianceChecking() {
check( false );
try {
check( true );
fail( "Expecting an exception" );
}
catch (OrderByComplianceViolation expected) {
// nothing to do
}
}
private void check(boolean complianceEnabled) {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.HBM2DDL_AUTO, "create-drop" )
.build();
try {
final Metadata bootModel = new MetadataSources( ssr )
.addAnnotatedClass( Order.class )
.addAnnotatedClass( LineItem.class )
.buildMetadata();
final SessionFactory sf = bootModel.getSessionFactoryBuilder()
.enableJpaOrderByMappingCompliance( complianceEnabled )
.build();
try {
TransactionUtil2.inTransaction(
(SessionFactoryImplementor) sf,
session -> session.createQuery( "from Order" ).list()
);
}
finally {
try {
sf.close();
}
catch (Exception ignore) {
}
}
}
finally {
StandardServiceRegistryBuilder.destroy( ssr );
}
}
@Entity( name = "Order" )
@Table( name = "orders" )
public static class Order {
@Id
private Integer id;
private String invoice;
@OneToMany
@OrderBy( "qty" )
private Set<LineItem> lineItems;
}
@Entity( name = "LineItem" )
@Table( name = "line_items" )
public static class LineItem {
@Id
private Integer id;
private String sku;
@Column( name = "qty" )
private int quantity;
}
}