HHH-12034 - According to JPA, a Proxy should be loaded even when accessing the identifier

This commit is contained in:
Vlad Mihalcea 2018-01-22 18:19:51 +02:00
parent 13f1e0597a
commit 3fb0ca09e7
10 changed files with 228 additions and 22 deletions

View File

@ -578,6 +578,7 @@ public static class SessionFactoryOptionsStateStandardImpl implements SessionFac
private Map<String, SQLFunction> sqlFunctions;
private boolean failOnPaginationOverCollectionFetchEnabled;
private boolean jpaProxyComplianceEnabled;
public SessionFactoryOptionsStateStandardImpl(StandardServiceRegistry serviceRegistry) {
this.serviceRegistry = serviceRegistry;
@ -798,6 +799,12 @@ else if ( jdbcTimeZoneValue != null ) {
configurationSettings,
false
);
this.jpaProxyComplianceEnabled = ConfigurationHelper.getBoolean(
JPA_PROXY_COMPLIANCE,
configurationSettings,
false
);
}
private static Interceptor determineInterceptor(Map configurationSettings, StrategySelector strategySelector) {
@ -1256,6 +1263,11 @@ public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
public boolean isFailOnPaginationOverCollectionFetchEnabled() {
return this.failOnPaginationOverCollectionFetchEnabled;
}
@Override
public boolean isJpaProxyComplianceEnabled() {
return this.jpaProxyComplianceEnabled;
}
}
private static Supplier<? extends Interceptor> interceptorSupplier(Class<? extends Interceptor> clazz) {
@ -1605,8 +1617,14 @@ public boolean isQueryParametersValidationEnabled() {
public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
return options.getCriteriaLiteralHandlingMode();
}
@Override
public boolean isFailOnPaginationOverCollectionFetchEnabled() {
return options.isFailOnPaginationOverCollectionFetchEnabled();
}
@Override
public boolean isJpaProxyComplianceEnabled() {
return options.isJpaProxyComplianceEnabled();
}
}

View File

@ -132,6 +132,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
private boolean queryParametersValidationEnabled;
private LiteralHandlingMode criteriaLiteralHandlingMode;
private final boolean failOnPaginationOverCollectionFetchEnabled;
private final boolean jpaProxyComplianceEnabled;
public SessionFactoryOptionsImpl(SessionFactoryOptionsState state) {
this.serviceRegistry = state.getServiceRegistry();
@ -216,6 +217,8 @@ public SessionFactoryOptionsImpl(SessionFactoryOptionsState state) {
this.criteriaLiteralHandlingMode = state.getCriteriaLiteralHandlingMode();
this.failOnPaginationOverCollectionFetchEnabled = state.isFailOnPaginationOverCollectionFetchEnabled();
this.jpaProxyComplianceEnabled = state.isJpaProxyComplianceEnabled();
}
@Override
@ -566,4 +569,9 @@ public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
public boolean isFailOnPaginationOverCollectionFetchEnabled() {
return failOnPaginationOverCollectionFetchEnabled;
}
@Override
public boolean isJpaProxyComplianceEnabled() {
return jpaProxyComplianceEnabled;
}
}

View File

@ -207,4 +207,8 @@ default LiteralHandlingMode getCriteriaLiteralHandlingMode() {
default boolean isFailOnPaginationOverCollectionFetchEnabled() {
return false;
}
default boolean isJpaProxyComplianceEnabled() {
return false;
}
}

View File

@ -400,4 +400,9 @@ public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
public boolean isFailOnPaginationOverCollectionFetchEnabled() {
return delegate.isFailOnPaginationOverCollectionFetchEnabled();
}
@Override
public boolean isJpaProxyComplianceEnabled() {
return delegate.isJpaProxyComplianceEnabled();
}
}

View File

@ -252,4 +252,8 @@ default LiteralHandlingMode getCriteriaLiteralHandlingMode() {
default boolean isFailOnPaginationOverCollectionFetchEnabled() {
return false;
}
default boolean isJpaProxyComplianceEnabled() {
return false;
}
}

View File

@ -886,6 +886,17 @@ public interface AvailableSettings {
*/
String LOG_JDBC_WARNINGS = "hibernate.jdbc.log.warnings";
/**
* Identifies an explicit {@link org.hibernate.resource.beans.container.spi.BeanContainer}
* to be used.
*
* Note that for CDI-based containers setting this is not necessary - simply
* pass the BeanManager to use via {@link #CDI_BEAN_MANAGER} and
* optionally specify {@link #DELAY_CDI_ACCESS}. This setting is more meant to
* integrate non-CDI bean containers such as Spring.
*/
String BEAN_CONTAINER = "hibernate.resource.beans.container";
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -1714,9 +1725,25 @@ public interface AvailableSettings {
*/
String CRITERIA_LITERAL_HANDLING_MODE = "hibernate.criteria.literal_handling_mode";
/**
* JPA spec says that an {@link javax.persistence.EntityNotFoundException}
* should be thrown when accessing an entity Proxy which does not have an associated
* table row in the database.
*
* Traditionally, Hibernate does not initialize an entity Proxy when accessing its
* identifier since we already know the identifier value, hence we can save a database roundtrip.
*
* If enabled Hibernate will initialize the entity Proxy even when accessing its identifier.
*
* @since 5.2.13
*/
String JPA_PROXY_COMPLIANCE = "hibernate.jpa.compliance.proxy";
/**
* Raises an exception when in-memory pagination over collection fetch is about to be performed.
* Disabled by default. Set to true to enable.
*
* @since 5.2.13
*/
String FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH = "hibernate.query.fail_on_pagination_over_collection_fetch";
}

View File

@ -29,7 +29,7 @@
* @author Gavin King
*/
public abstract class AbstractLazyInitializer implements LazyInitializer {
private static final Logger log = Logger.getLogger( AbstractLazyInitializer.class );
private static final Logger log = org.jboss.logging.Logger.getLogger( AbstractLazyInitializer.class );
private String entityName;
private Serializable id;
@ -43,6 +43,8 @@ public abstract class AbstractLazyInitializer implements LazyInitializer {
private String sessionFactoryUuid;
private boolean allowLoadOutsideTransaction;
private boolean initializeProxyWhenAccessingIdentifier;
/**
* For serialization from the non-pojo initializers (HHH-3309)
*/
@ -65,6 +67,8 @@ protected AbstractLazyInitializer(String entityName, Serializable id, SharedSess
}
else {
setSession( session );
initializeProxyWhenAccessingIdentifier = session.getFactory().getSessionFactoryOptions()
.isJpaProxyComplianceEnabled();
}
}
@ -75,6 +79,9 @@ public final String getEntityName() {
@Override
public final Serializable getIdentifier() {
if ( isUninitialized() && initializeProxyWhenAccessingIdentifier ) {
initialize();
}
return id;
}

View File

@ -67,7 +67,7 @@ public Object invoke(
result = this.invoke( thisMethod, args, proxy );
}
catch ( Throwable t ) {
throw new Exception( t.getCause() );
throw t instanceof RuntimeException ? t : new Exception( t.getCause() );
}
if ( result == INVOKE_IMPLEMENTATION ) {
Object target = getImplementation();

View File

@ -0,0 +1,153 @@
/*
* 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.test.ops;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.AbstractHANADialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Gavin King
* @author Hardy Ferentschik
*/
public class GetLoadJpaComplianceTest extends BaseEntityManagerFunctionalTestCase {
@Override
@SuppressWarnings( {"unchecked"})
protected void addConfigOptions(Map options) {
options.put( AvailableSettings.JPA_PROXY_COMPLIANCE, true );
}
@Test
@TestForIssue( jiraKey = "HHH-12034")
public void testLoadIdNotFound_FieldBasedAccess() {
EntityManager em = getOrCreateEntityManager();
try {
em.getTransaction().begin();
Session s = (Session) em.getDelegate();
assertNull( s.get( Workload.class, 999 ) );
Workload proxy = s.load( Workload.class, 999 );
assertFalse( Hibernate.isInitialized( proxy ) );
proxy.getId();
fail( "Should have failed because there is no Employee Entity with id == 999" );
}
catch (EntityNotFoundException ex) {
// expected
}
finally {
em.getTransaction().rollback();
em.close();
}
}
@Test
@TestForIssue( jiraKey = "HHH-12034")
public void testReferenceIdNotFound_FieldBasedAccess() {
EntityManager em = getOrCreateEntityManager();
try {
em.getTransaction().begin();
assertNull( em.find( Workload.class, 999 ) );
Workload proxy = em.getReference( Workload.class, 999 );
assertFalse( Hibernate.isInitialized( proxy ) );
proxy.getId();
fail( "Should have failed because there is no Workload Entity with id == 999" );
}
catch (EntityNotFoundException ex) {
// expected
}
finally {
em.getTransaction().rollback();
em.close();
}
}
@Test
@TestForIssue( jiraKey = "HHH-12034")
public void testLoadIdNotFound_PropertyBasedAccess() {
EntityManager em = getOrCreateEntityManager();
try {
em.getTransaction().begin();
Session s = (Session) em.getDelegate();
assertNull( s.get( Employee.class, 999 ) );
Employee proxy = s.load( Employee.class, 999 );
assertFalse( Hibernate.isInitialized( proxy ) );
proxy.getId();
fail( "Should have failed because there is no Employee Entity with id == 999" );
}
catch (EntityNotFoundException ex) {
// expected
}
finally {
em.getTransaction().rollback();
em.close();
}
}
@Test
@TestForIssue( jiraKey = "HHH-12034")
public void testReferenceIdNotFound_PropertyBasedAccess() {
EntityManager em = getOrCreateEntityManager();
try {
em.getTransaction().begin();
assertNull( em.find( Employee.class, 999 ) );
Employee proxy = em.getReference( Employee.class, 999 );
assertFalse( Hibernate.isInitialized( proxy ) );
proxy.getId();
fail( "Should have failed because there is no Employee Entity with id == 999" );
}
catch (EntityNotFoundException ex) {
// expected
}
finally {
em.getTransaction().rollback();
em.close();
}
}
@Override
protected String[] getMappings() {
return new String[] {
"org/hibernate/jpa/test/ops/Node.hbm.xml",
"org/hibernate/jpa/test/ops/Employer.hbm.xml"
};
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Workload.class };
}
}

View File

@ -179,7 +179,6 @@ public void testLoadGetId() {
@Test
@TestForIssue( jiraKey = "HHH-12034")
@FailureExpected( jiraKey = "HHH-12034" )
public void testLoadIdNotFound_FieldBasedAccess() {
EntityManager em = getOrCreateEntityManager();
try {
@ -192,10 +191,6 @@ public void testLoadIdNotFound_FieldBasedAccess() {
assertFalse( Hibernate.isInitialized( proxy ) );
proxy.getId();
fail( "Should have failed because there is no Workload Entity with id == 999" );
}
catch (EntityNotFoundException ex) {
// expected
}
finally {
em.getTransaction().rollback();
@ -205,7 +200,6 @@ public void testLoadIdNotFound_FieldBasedAccess() {
@Test
@TestForIssue( jiraKey = "HHH-12034")
@FailureExpected( jiraKey = "HHH-12034" )
public void testReferenceIdNotFound_FieldBasedAccess() {
EntityManager em = getOrCreateEntityManager();
try {
@ -217,10 +211,6 @@ public void testReferenceIdNotFound_FieldBasedAccess() {
assertFalse( Hibernate.isInitialized( proxy ) );
proxy.getId();
fail( "Should have failed because there is no Workload Entity with id == 999" );
}
catch (EntityNotFoundException ex) {
// expected
}
finally {
em.getTransaction().rollback();
@ -230,7 +220,6 @@ public void testReferenceIdNotFound_FieldBasedAccess() {
@Test
@TestForIssue( jiraKey = "HHH-12034")
@FailureExpected( jiraKey = "HHH-12034" )
public void testLoadIdNotFound_PropertyBasedAccess() {
EntityManager em = getOrCreateEntityManager();
try {
@ -243,10 +232,6 @@ public void testLoadIdNotFound_PropertyBasedAccess() {
assertFalse( Hibernate.isInitialized( proxy ) );
proxy.getId();
fail( "Should have failed because there is no Employee Entity with id == 999" );
}
catch (EntityNotFoundException ex) {
// expected
}
finally {
em.getTransaction().rollback();
@ -256,7 +241,6 @@ public void testLoadIdNotFound_PropertyBasedAccess() {
@Test
@TestForIssue( jiraKey = "HHH-12034")
@FailureExpected( jiraKey = "HHH-12034" )
public void testReferenceIdNotFound_PropertyBasedAccess() {
EntityManager em = getOrCreateEntityManager();
try {
@ -268,10 +252,6 @@ public void testReferenceIdNotFound_PropertyBasedAccess() {
assertFalse( Hibernate.isInitialized( proxy ) );
proxy.getId();
fail( "Should have failed because there is no Employee Entity with id == 999" );
}
catch (EntityNotFoundException ex) {
// expected
}
finally {
em.getTransaction().rollback();