HHH-12034 - According to JPA, a Proxy should be loaded even when accessing the identifier
This commit is contained in:
parent
b6c6029edb
commit
795055de51
|
@ -924,6 +924,8 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
|||
* 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.
|
||||
*
|
||||
* @since 5.3
|
||||
*/
|
||||
String BEAN_CONTAINER = "hibernate.resource.beans.container";
|
||||
|
||||
|
@ -1772,6 +1774,7 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
|||
* since it extends the JPA one.
|
||||
*
|
||||
* @see JpaCompliance#isJpaTransactionComplianceEnabled()
|
||||
* @since 5.3
|
||||
*/
|
||||
String JPA_TRANSACTION_COMPLIANCE = "hibernate.jpa.compliance.transaction";
|
||||
|
||||
|
@ -1785,6 +1788,7 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
|||
* Deviations result in an exception if enabled
|
||||
*
|
||||
* @see JpaCompliance#isJpaQueryComplianceEnabled()
|
||||
* @since 5.3
|
||||
*/
|
||||
String JPA_QUERY_COMPLIANCE = "hibernate.jpa.compliance.query";
|
||||
|
||||
|
@ -1797,6 +1801,7 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
|||
* is just missing (and its defaults will apply).
|
||||
*
|
||||
* @see JpaCompliance#isJpaListComplianceEnabled()
|
||||
* @since 5.3
|
||||
*/
|
||||
String JPA_LIST_COMPLIANCE = "hibernate.jpa.compliance.list";
|
||||
|
||||
|
@ -1810,21 +1815,40 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
|||
* exceptions when the spec says it should.
|
||||
*
|
||||
* @see JpaCompliance#isJpaClosedComplianceEnabled()
|
||||
* @since 5.3
|
||||
*/
|
||||
String JPA_CLOSED_COMPLIANCE = "hibernate.jpa.compliance.closed";
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @see JpaCompliance#isJpaProxyComplianceEnabled()
|
||||
* @since 5.2.13
|
||||
*/
|
||||
String JPA_PROXY_COMPLIANCE = "hibernate.jpa.compliance.proxy";
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* The default value is true.
|
||||
*
|
||||
* @since 5.3
|
||||
*/
|
||||
String TABLE_GENERATOR_STORE_LAST_USED = "hibernate.id.generator.stored_last_used";
|
||||
|
||||
/**
|
||||
* 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";
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public interface JpaCompliance {
|
|||
boolean isJpaListComplianceEnabled();
|
||||
|
||||
/**
|
||||
*JPA defines specific exceptions on specific methods when called on
|
||||
* JPA defines specific exceptions on specific methods when called on
|
||||
* {@link javax.persistence.EntityManager} and {@link javax.persistence.EntityManagerFactory}
|
||||
* when those objects have been closed. This setting controls
|
||||
* whether the spec defined behavior or Hibernate's behavior will be used.
|
||||
|
@ -63,4 +63,18 @@ public interface JpaCompliance {
|
|||
* @return {@code true} indicates to behave in the spec-defined way
|
||||
*/
|
||||
boolean isJpaClosedComplianceEnabled();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return {@code true} indicates to behave in the spec-defined way
|
||||
*/
|
||||
boolean isJpaProxyComplianceEnabled();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ public class JpaComplianceImpl implements JpaCompliance {
|
|||
private boolean transactionCompliance;
|
||||
private boolean listCompliance;
|
||||
private boolean closedCompliance;
|
||||
private boolean proxyCompliance;
|
||||
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
|
@ -46,6 +47,11 @@ public class JpaComplianceImpl implements JpaCompliance {
|
|||
configurationSettings,
|
||||
jpaByDefault
|
||||
);
|
||||
proxyCompliance = ConfigurationHelper.getBoolean(
|
||||
AvailableSettings.JPA_PROXY_COMPLIANCE,
|
||||
configurationSettings,
|
||||
jpaByDefault
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -68,6 +74,10 @@ public class JpaComplianceImpl implements JpaCompliance {
|
|||
return closedCompliance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJpaProxyComplianceEnabled() {
|
||||
return proxyCompliance;
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Mutators
|
||||
|
|
|
@ -21,8 +21,6 @@ import org.hibernate.internal.CoreMessageLogger;
|
|||
import org.hibernate.internal.SessionFactoryRegistry;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Convenience base class for lazy initialization handlers. Centralizes the basic plumbing of doing lazy
|
||||
* initialization freeing subclasses to acts as essentially adapters to their intended entity mode and/or
|
||||
|
@ -45,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)
|
||||
*/
|
||||
|
@ -67,6 +67,8 @@ public abstract class AbstractLazyInitializer implements LazyInitializer {
|
|||
}
|
||||
else {
|
||||
setSession( session );
|
||||
initializeProxyWhenAccessingIdentifier = session.getFactory().getSessionFactoryOptions()
|
||||
.getJpaCompliance().isJpaProxyComplianceEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,6 +79,9 @@ public abstract class AbstractLazyInitializer implements LazyInitializer {
|
|||
|
||||
@Override
|
||||
public final Serializable getIdentifier() {
|
||||
if ( isUninitialized() && initializeProxyWhenAccessingIdentifier ) {
|
||||
initialize();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ public class JavassistLazyInitializer extends BasicLazyInitializer implements Me
|
|||
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();
|
||||
|
|
|
@ -42,6 +42,7 @@ public class JpaComplianceTestingImpl implements JpaCompliance {
|
|||
private boolean transactionCompliance;
|
||||
private boolean listCompliance;
|
||||
private boolean closedCompliance;
|
||||
private boolean proxyCompliance;
|
||||
|
||||
@Override
|
||||
public boolean isJpaQueryComplianceEnabled() {
|
||||
|
@ -63,4 +64,8 @@ public class JpaComplianceTestingImpl implements JpaCompliance {
|
|||
return closedCompliance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJpaProxyComplianceEnabled() {
|
||||
return proxyCompliance;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
|
|
@ -179,7 +179,6 @@ public class GetLoadTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-12034")
|
||||
@FailureExpected( jiraKey = "HHH-12034" )
|
||||
public void testLoadIdNotFound_FieldBasedAccess() {
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
try {
|
||||
|
@ -192,10 +191,6 @@ public class GetLoadTest extends BaseEntityManagerFunctionalTestCase {
|
|||
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 class GetLoadTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-12034")
|
||||
@FailureExpected( jiraKey = "HHH-12034" )
|
||||
public void testReferenceIdNotFound_FieldBasedAccess() {
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
try {
|
||||
|
@ -217,10 +211,6 @@ public class GetLoadTest extends BaseEntityManagerFunctionalTestCase {
|
|||
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 class GetLoadTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-12034")
|
||||
@FailureExpected( jiraKey = "HHH-12034" )
|
||||
public void testLoadIdNotFound_PropertyBasedAccess() {
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
try {
|
||||
|
@ -243,10 +232,6 @@ public class GetLoadTest extends BaseEntityManagerFunctionalTestCase {
|
|||
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 class GetLoadTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-12034")
|
||||
@FailureExpected( jiraKey = "HHH-12034" )
|
||||
public void testReferenceIdNotFound_PropertyBasedAccess() {
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
try {
|
||||
|
@ -268,10 +252,6 @@ public class GetLoadTest extends BaseEntityManagerFunctionalTestCase {
|
|||
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();
|
||||
|
|
Loading…
Reference in New Issue