Support for type coercion for values passed as ids and as query parameter bindings
- widening coercions - valid (no over/under flow) narrowing coercions - JpaCompliance setting
This commit is contained in:
parent
d95806b516
commit
eb9bb2d82f
|
@ -2134,6 +2134,13 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
|||
*/
|
||||
String JPA_ID_GENERATOR_GLOBAL_SCOPE_COMPLIANCE = "hibernate.jpa.compliance.global_id_generators";
|
||||
|
||||
/**
|
||||
* @see JpaCompliance#isLoadByIdComplianceEnabled()
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
String JPA_LOAD_BY_ID_COMPLIANCE = "hibernate.jpa.compliance.load_by_id";
|
||||
|
||||
/**
|
||||
* True/False setting indicating if the value stored in the table used by the {@link javax.persistence.TableGenerator}
|
||||
* is the last value generated or the next value to be used.
|
||||
|
|
|
@ -20,8 +20,9 @@ public class JpaComplianceImpl implements JpaCompliance {
|
|||
private final boolean transactionCompliance;
|
||||
private final boolean closedCompliance;
|
||||
private final boolean cachingCompliance;
|
||||
private final boolean loadByIdCompliance;
|
||||
|
||||
private JpaComplianceImpl(
|
||||
public JpaComplianceImpl(
|
||||
boolean listCompliance,
|
||||
boolean orderByMappingCompliance,
|
||||
boolean proxyCompliance,
|
||||
|
@ -29,7 +30,8 @@ public class JpaComplianceImpl implements JpaCompliance {
|
|||
boolean queryCompliance,
|
||||
boolean transactionCompliance,
|
||||
boolean closedCompliance,
|
||||
boolean cachingCompliance) {
|
||||
boolean cachingCompliance,
|
||||
boolean loadByIdCompliance) {
|
||||
this.queryCompliance = queryCompliance;
|
||||
this.transactionCompliance = transactionCompliance;
|
||||
this.listCompliance = listCompliance;
|
||||
|
@ -38,6 +40,7 @@ public class JpaComplianceImpl implements JpaCompliance {
|
|||
this.cachingCompliance = cachingCompliance;
|
||||
this.globalGeneratorNameScopeCompliance = globalGeneratorNameScopeCompliance;
|
||||
this.orderByMappingCompliance = orderByMappingCompliance;
|
||||
this.loadByIdCompliance = loadByIdCompliance;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -80,6 +83,11 @@ public class JpaComplianceImpl implements JpaCompliance {
|
|||
return orderByMappingCompliance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoadByIdComplianceEnabled() {
|
||||
return loadByIdCompliance;
|
||||
}
|
||||
|
||||
public static class JpaComplianceBuilder {
|
||||
private boolean queryCompliance;
|
||||
private boolean listCompliance;
|
||||
|
@ -89,6 +97,7 @@ public class JpaComplianceImpl implements JpaCompliance {
|
|||
private boolean cachingCompliance;
|
||||
private boolean transactionCompliance;
|
||||
private boolean closedCompliance;
|
||||
private boolean loadByIdCompliance;
|
||||
|
||||
public JpaComplianceBuilder() {
|
||||
}
|
||||
|
@ -133,6 +142,11 @@ public class JpaComplianceImpl implements JpaCompliance {
|
|||
return this;
|
||||
}
|
||||
|
||||
public JpaComplianceBuilder setLoadByIdCompliance(boolean loadByIdCompliance) {
|
||||
this.loadByIdCompliance = loadByIdCompliance;
|
||||
return this;
|
||||
}
|
||||
|
||||
JpaCompliance createJpaCompliance() {
|
||||
return new JpaComplianceImpl(
|
||||
listCompliance,
|
||||
|
@ -142,7 +156,8 @@ public class JpaComplianceImpl implements JpaCompliance {
|
|||
queryCompliance,
|
||||
transactionCompliance,
|
||||
closedCompliance,
|
||||
cachingCompliance
|
||||
cachingCompliance,
|
||||
loadByIdCompliance
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ public class MutableJpaComplianceImpl implements MutableJpaCompliance {
|
|||
private boolean transactionCompliance;
|
||||
private boolean closedCompliance;
|
||||
private boolean cachingCompliance;
|
||||
private boolean loadByIdCompliance;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public MutableJpaComplianceImpl(Map configurationSettings, boolean jpaByDefault) {
|
||||
|
@ -74,6 +75,12 @@ public class MutableJpaComplianceImpl implements MutableJpaCompliance {
|
|||
configurationSettings,
|
||||
jpaByDefault
|
||||
);
|
||||
|
||||
loadByIdCompliance = ConfigurationHelper.getBoolean(
|
||||
AvailableSettings.JPA_LOAD_BY_ID_COMPLIANCE,
|
||||
configurationSettings,
|
||||
jpaByDefault
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -158,7 +165,15 @@ public class MutableJpaComplianceImpl implements MutableJpaCompliance {
|
|||
this.cachingCompliance = cachingCompliance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoadByIdCompliance(boolean enabled) {
|
||||
this.loadByIdCompliance = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoadByIdComplianceEnabled() {
|
||||
return loadByIdCompliance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaCompliance immutableCopy() {
|
||||
|
@ -170,7 +185,8 @@ public class MutableJpaComplianceImpl implements MutableJpaCompliance {
|
|||
.setQueryCompliance( queryCompliance )
|
||||
.setTransactionCompliance( transactionCompliance )
|
||||
.setClosedCompliance( closedCompliance )
|
||||
.setCachingCompliance( cachingCompliance );
|
||||
.setCachingCompliance( cachingCompliance )
|
||||
.setLoadByIdCompliance( loadByIdCompliance );
|
||||
return builder.createJpaCompliance();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,4 +105,18 @@ public interface JpaCompliance {
|
|||
* this enabled, Hibernate will throw a compliance error when a non-attribute-reference is used.
|
||||
*/
|
||||
boolean isJpaOrderByMappingComplianceEnabled();
|
||||
|
||||
/**
|
||||
* JPA says that the id passed to {@link javax.persistence.EntityManager#getReference} and
|
||||
* {@link javax.persistence.EntityManager#find} should be the exact expected type, allowing
|
||||
* no type coercion.
|
||||
*
|
||||
* Historically, Hibernate behaved the same way. Since 6.0 however, Hibernate has the ability to
|
||||
* coerce the passed type to the expected type.
|
||||
*
|
||||
* This setting controls whether such a coercion should be allowed.
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
boolean isLoadByIdComplianceEnabled();
|
||||
}
|
||||
|
|
|
@ -26,5 +26,7 @@ public interface MutableJpaCompliance extends JpaCompliance {
|
|||
|
||||
void setGeneratorNameScopeCompliance(boolean generatorScopeCompliance);
|
||||
|
||||
void setLoadByIdCompliance(boolean enabled);
|
||||
|
||||
JpaCompliance immutableCopy();
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.hibernate.event.spi.LoadEventListener;
|
|||
import org.hibernate.graph.GraphSemantic;
|
||||
import org.hibernate.graph.RootGraph;
|
||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||
import org.hibernate.jpa.spi.JpaCompliance;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
|
@ -116,7 +117,10 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T>, Jav
|
|||
final EventSource eventSource = (EventSource) session;
|
||||
final LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers();
|
||||
|
||||
id = entityPersister.getIdentifierMapping().getJavaTypeDescriptor().coerce( id, this );
|
||||
final JpaCompliance jpaCompliance = session.getFactory().getSessionFactoryOptions().getJpaCompliance();
|
||||
if ( ! jpaCompliance.isLoadByIdComplianceEnabled() ) {
|
||||
id = entityPersister.getIdentifierMapping().getJavaTypeDescriptor().coerce( id, this );
|
||||
}
|
||||
|
||||
if ( this.lockOptions != null ) {
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, eventSource, loadQueryInfluencers.getReadOnly() );
|
||||
|
@ -158,7 +162,10 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T>, Jav
|
|||
final EventSource eventSource = (EventSource) session;
|
||||
final LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers();
|
||||
|
||||
id = entityPersister.getIdentifierMapping().getJavaTypeDescriptor().coerce( id, this );
|
||||
final JpaCompliance jpaCompliance = session.getFactory().getSessionFactoryOptions().getJpaCompliance();
|
||||
if ( ! jpaCompliance.isLoadByIdComplianceEnabled() ) {
|
||||
id = entityPersister.getIdentifierMapping().getJavaTypeDescriptor().coerce( id, this );
|
||||
}
|
||||
|
||||
if ( this.lockOptions != null ) {
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, eventSource, loadQueryInfluencers.getReadOnly() );
|
||||
|
|
|
@ -53,4 +53,9 @@ public class JpaComplianceStub implements JpaCompliance {
|
|||
public boolean isJpaOrderByMappingComplianceEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoadByIdComplianceEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ package org.hibernate.orm.test.jpa.emops;
|
|||
|
||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Jpa;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
@ -17,11 +16,14 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||
* @author Emmanuel Bernard
|
||||
*/
|
||||
|
||||
@Jpa(annotatedClasses = {
|
||||
Competitor.class,
|
||||
Race.class,
|
||||
Mail.class
|
||||
})
|
||||
@Jpa(
|
||||
annotatedClasses = {
|
||||
Competitor.class,
|
||||
Race.class,
|
||||
Mail.class
|
||||
},
|
||||
loadByIdComplianceEnabled = true
|
||||
)
|
||||
public class GetReferenceTest {
|
||||
@Test
|
||||
public void testWrongIdType(EntityManagerFactoryScope scope) {
|
||||
|
@ -51,4 +53,32 @@ public class GetReferenceTest {
|
|||
}
|
||||
);
|
||||
}
|
||||
@Test
|
||||
public void testWrongIdTypeFind(EntityManagerFactoryScope scope) {
|
||||
scope.inEntityManager(
|
||||
entityManager -> {
|
||||
try {
|
||||
entityManager.find( Competitor.class, "30" );
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
//success
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
fail("Wrong exception: " + e );
|
||||
}
|
||||
|
||||
try {
|
||||
entityManager.find( Mail.class, 1 );
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
//success
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
fail("Wrong exception: " + e );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,8 +48,9 @@ import org.jboss.logging.Logger;
|
|||
* including argument injection (or see {@link SessionFactoryScopeAware})
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
* @see SessionFactoryScope
|
||||
*
|
||||
* @see DomainModelExtension
|
||||
* @see SessionFactoryExtension
|
||||
*/
|
||||
public class EntityManagerFactoryExtension
|
||||
implements TestInstancePostProcessor, AfterAllCallback, TestExecutionExceptionHandler {
|
||||
|
@ -79,8 +80,7 @@ public class EntityManagerFactoryExtension
|
|||
context.getElement().get(),
|
||||
Jpa.class
|
||||
);
|
||||
final Jpa emfAnn = emfAnnWrapper.orElseThrow( () -> new RuntimeException(
|
||||
"Could not locate @EntityManagerFactory" ) );
|
||||
final Jpa emfAnn = emfAnnWrapper.orElseThrow( () -> new RuntimeException( "Could not locate @EntityManagerFactory" ) );
|
||||
|
||||
final PersistenceUnitInfoImpl pui = new PersistenceUnitInfoImpl( emfAnn.persistenceUnitName() );
|
||||
|
||||
|
@ -89,6 +89,17 @@ public class EntityManagerFactoryExtension
|
|||
pui.setValidationMode( emfAnn.validationMode() );
|
||||
pui.setExcludeUnlistedClasses( emfAnn.excludeUnlistedClasses() );
|
||||
|
||||
// JpaCompliance
|
||||
pui.getProperties().put( AvailableSettings.JPA_QUERY_COMPLIANCE, emfAnn.queryComplianceEnabled() );
|
||||
pui.getProperties().put( AvailableSettings.JPA_TRANSACTION_COMPLIANCE, emfAnn.transactionComplianceEnabled() );
|
||||
pui.getProperties().put( AvailableSettings.JPA_LIST_COMPLIANCE, emfAnn.listMappingComplianceEnabled() );
|
||||
pui.getProperties().put( AvailableSettings.JPA_CLOSED_COMPLIANCE, emfAnn.closedComplianceEnabled() );
|
||||
pui.getProperties().put( AvailableSettings.JPA_PROXY_COMPLIANCE, emfAnn.proxyComplianceEnabled() );
|
||||
pui.getProperties().put( AvailableSettings.JPA_CACHING_COMPLIANCE, emfAnn.cacheComplianceEnabled() );
|
||||
pui.getProperties().put( AvailableSettings.JPA_ID_GENERATOR_GLOBAL_SCOPE_COMPLIANCE, emfAnn.generatorScopeComplianceEnabled() );
|
||||
pui.getProperties().put( AvailableSettings.JPA_ORDER_BY_MAPPING_COMPLIANCE, emfAnn.orderByMappingComplianceEnabled() );
|
||||
pui.getProperties().put( AvailableSettings.JPA_LOAD_BY_ID_COMPLIANCE, emfAnn.loadByIdComplianceEnabled() );
|
||||
|
||||
final Setting[] properties = emfAnn.properties();
|
||||
for ( int i = 0; i < properties.length; i++ ) {
|
||||
final Setting property = properties[i];
|
||||
|
@ -301,8 +312,6 @@ public class EntityManagerFactoryExtension
|
|||
}
|
||||
|
||||
protected javax.persistence.EntityManagerFactory createEntityManagerFactory() {
|
||||
|
||||
|
||||
final EntityManagerFactoryBuilder emfBuilder = Bootstrap.getEntityManagerFactoryBuilder(
|
||||
new PersistenceUnitInfoDescriptor( persistenceUnitInfo ),
|
||||
integrationSettings
|
||||
|
|
|
@ -16,6 +16,8 @@ import javax.persistence.SharedCacheMode;
|
|||
import javax.persistence.ValidationMode;
|
||||
import javax.persistence.spi.PersistenceUnitTransactionType;
|
||||
|
||||
import org.hibernate.jpa.spi.JpaCompliance;
|
||||
|
||||
import org.hibernate.testing.orm.domain.DomainModelDescriptor;
|
||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||
import org.hibernate.testing.orm.jpa.NonStringValueSettingProvider;
|
||||
|
@ -38,6 +40,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||
|
||||
@ExtendWith( FailureExpectedExtension.class )
|
||||
public @interface Jpa {
|
||||
String persistenceUnitName() default "test-pu";
|
||||
|
||||
/**
|
||||
* Used to mimic container integration
|
||||
|
@ -46,8 +49,6 @@ public @interface Jpa {
|
|||
|
||||
Class<? extends NonStringValueSettingProvider>[] nonStringValueSettingProviders() default {};
|
||||
|
||||
String persistenceUnitName() default "test-pu";
|
||||
|
||||
// todo : multiple persistence units?
|
||||
|
||||
/**
|
||||
|
@ -62,6 +63,51 @@ public @interface Jpa {
|
|||
SharedCacheMode sharedCacheMode() default SharedCacheMode.UNSPECIFIED;
|
||||
ValidationMode validationMode() default ValidationMode.NONE;
|
||||
|
||||
/**
|
||||
* @see JpaCompliance#isJpaQueryComplianceEnabled()
|
||||
*/
|
||||
boolean queryComplianceEnabled() default false;
|
||||
|
||||
/**
|
||||
* @see JpaCompliance#isJpaTransactionComplianceEnabled()
|
||||
*/
|
||||
boolean transactionComplianceEnabled() default false;
|
||||
|
||||
/**
|
||||
* @see JpaCompliance#isJpaClosedComplianceEnabled()
|
||||
*/
|
||||
boolean closedComplianceEnabled() default false;
|
||||
|
||||
/**
|
||||
* @see JpaCompliance#isJpaListComplianceEnabled()
|
||||
*/
|
||||
boolean listMappingComplianceEnabled() default false;
|
||||
|
||||
/**
|
||||
* @see JpaCompliance#isJpaOrderByMappingComplianceEnabled()
|
||||
*/
|
||||
boolean orderByMappingComplianceEnabled() default false;
|
||||
|
||||
/**
|
||||
* @see JpaCompliance#isJpaProxyComplianceEnabled()
|
||||
*/
|
||||
boolean proxyComplianceEnabled() default false;
|
||||
|
||||
/**
|
||||
* @see JpaCompliance#isJpaCacheComplianceEnabled()
|
||||
*/
|
||||
boolean cacheComplianceEnabled() default false;
|
||||
|
||||
/**
|
||||
* @see JpaCompliance#isGlobalGeneratorScopeEnabled()
|
||||
*/
|
||||
boolean generatorScopeComplianceEnabled() default false;
|
||||
|
||||
/**
|
||||
* @see JpaCompliance#isLoadByIdComplianceEnabled()
|
||||
*/
|
||||
boolean loadByIdComplianceEnabled() default false;
|
||||
|
||||
boolean excludeUnlistedClasses() default false;
|
||||
|
||||
StandardDomainModel[] standardModels() default {};
|
||||
|
|
Loading…
Reference in New Issue