HHH-13658 : make NO_PROXY unnecessary

- Better handle `FetchModeType#LAZY` for to-one associations based on whether bytecode-enhancement-as-proxy is enabled.  Minimize the cases a user is likely to need to use `@LazyToOne`
- See also EAP7-1402
This commit is contained in:
Steve Ebersole 2021-01-13 12:03:00 -06:00
parent 949ba3b083
commit 0c974991f7
5 changed files with 408 additions and 0 deletions

View File

@ -0,0 +1,42 @@
/*
* 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.mapping.lazytoone;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* @author Steve Ebersole
*/
@Entity( name = "Airport" )
@Table( name = "airport" )
public class Airport {
@Id
private Integer id;
private String code;
public Airport() {
}
public Airport(Integer id, String code) {
this.id = id;
this.code = code;
}
public Integer getId() {
return id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.mapping.lazytoone;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.LazyToOne;
import static javax.persistence.FetchType.LAZY;
import static org.hibernate.annotations.LazyToOneOption.NO_PROXY;
/**
* @author Steve Ebersole
*/
@Entity( name = "Flight" )
@Table( name = "flight" )
public class Flight {
@Id
private Integer id;
private String number;
@ManyToOne( fetch = LAZY )
private Airport origination;
@ManyToOne( fetch = LAZY )
@LazyToOne( NO_PROXY )
private Airport destination;
public Flight() {
}
public Flight(Integer id, String number) {
this.id = id;
this.number = number;
}
public Flight(Integer id, String number, Airport origination, Airport destination) {
this.id = id;
this.number = number;
this.origination = origination;
this.destination = destination;
}
public Integer getId() {
return id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Airport getOrigination() {
return origination;
}
public void setOrigination(Airport origination) {
this.origination = origination;
}
public Airport getDestination() {
return destination;
}
public void setDestination(Airport destination) {
this.destination = destination;
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.mapping.lazytoone;
import org.hibernate.Hibernate;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Same as {@link LazyToOneTest} except here we have bytecode-enhanced entities
* via {@link BytecodeEnhancerRunner}
*/
@RunWith( BytecodeEnhancerRunner.class )
public class InstrumentedLazyToOneTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Airport.class, Flight.class };
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
}
@Override
protected void prepareTest() throws Exception {
inTransaction(
(session) -> {
final Airport austin = new Airport( 1, "AUS" );
final Airport baltimore = new Airport( 2, "BWI" );
final Flight flight1 = new Flight( 1, "ABC-123", austin, baltimore );
final Flight flight2 = new Flight( 2, "ABC-987", baltimore, austin );
session.persist( austin );
session.persist( baltimore );
session.persist( flight1 );
session.persist( flight2 );
}
);
}
@Override
protected void cleanupTestData() throws Exception {
inTransaction(
(session) -> {
session.createQuery( "delete Flight" ).executeUpdate();
session.createQuery( "delete Airport" ).executeUpdate();
}
);
}
@Test
@FailureExpected( jiraKey = "HHH-13658", message = "Flight#origination is not treated as lazy. Not sure why exactly" )
public void testEnhancedButProxyNotAllowed() {
final StatisticsImplementor statistics = sessionFactory().getStatistics();
statistics.clear();
inTransaction(
(session) -> {
final Flight flight1 = session.byId( Flight.class ).load( 1 );
// unlike the other 2 tests we should get 2 db queries here
assertThat( statistics.getPrepareStatementCount(), is( 2L ) );
assertThat( Hibernate.isInitialized( flight1 ), is( true ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "origination" ), is( true ) );
// this should be a non-enhanced proxy
assertThat( Hibernate.isInitialized( flight1.getOrigination() ), is( false ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "destination" ), is( false ) );
// the NO_PROXY here should trigger an EAGER load
assertThat( Hibernate.isInitialized( flight1.getDestination() ), is( false ) );
}
);
}
}

View File

@ -0,0 +1,104 @@
/*
* 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.mapping.lazytoone;
import org.hibernate.Hibernate;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Same as {@link InstrumentedLazyToOneTest} except here we enable bytecode-enhanced proxies
*/
@RunWith( BytecodeEnhancerRunner.class )
public class InstrumentedProxyLazyToOneTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Airport.class, Flight.class };
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
}
@Override
protected void prepareTest() throws Exception {
inTransaction(
(session) -> {
final Airport austin = new Airport( 1, "AUS" );
final Airport baltimore = new Airport( 2, "BWI" );
final Flight flight1 = new Flight( 1, "ABC-123", austin, baltimore );
final Flight flight2 = new Flight( 2, "ABC-987", baltimore, austin );
session.persist( austin );
session.persist( baltimore );
session.persist( flight1 );
session.persist( flight2 );
}
);
}
@Override
protected void cleanupTestData() throws Exception {
inTransaction(
(session) -> {
session.createQuery( "delete Flight" ).executeUpdate();
session.createQuery( "delete Airport" ).executeUpdate();
}
);
}
@Test
public void testEnhancedWithProxy() {
final StatisticsImplementor statistics = sessionFactory().getStatistics();
statistics.clear();
inTransaction(
(session) -> {
final Flight flight1 = session.byId( Flight.class ).load( 1 );
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
assertThat( Hibernate.isInitialized( flight1 ), is( true ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "origination" ), is( true ) );
assertThat( Hibernate.isInitialized( flight1.getOrigination() ), is( false ) );
// let's make sure these `Hibernate` calls pass for the right reasons...
assertThat( flight1.getOrigination(), instanceOf( PersistentAttributeInterceptable.class ) );
final PersistentAttributeInterceptable originationProxy = (PersistentAttributeInterceptable) flight1.getOrigination();
assertThat( originationProxy.$$_hibernate_getInterceptor(), notNullValue() );
assertThat( originationProxy.$$_hibernate_getInterceptor(), instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "destination" ), is( true ) );
assertThat( Hibernate.isInitialized( flight1.getDestination() ), is( false ) );
// let's make sure these `Hibernate` calls pass for the right reasons...
assertThat( flight1.getDestination(), instanceOf( PersistentAttributeInterceptable.class ) );
final PersistentAttributeInterceptable destinationProxy = (PersistentAttributeInterceptable) flight1.getDestination();
assertThat( destinationProxy.$$_hibernate_getInterceptor(), notNullValue() );
assertThat( destinationProxy.$$_hibernate_getInterceptor(), instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
}
);
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.mapping.lazytoone;
import org.hibernate.Hibernate;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
public class LazyToOneTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Airport.class, Flight.class };
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
}
@Override
protected void prepareTest() throws Exception {
inTransaction(
(session) -> {
final Airport austin = new Airport( 1, "AUS" );
final Airport baltimore = new Airport( 2, "BWI" );
final Flight flight1 = new Flight( 1, "ABC-123", austin, baltimore );
final Flight flight2 = new Flight( 2, "ABC-987", baltimore, austin );
session.persist( austin );
session.persist( baltimore );
session.persist( flight1 );
session.persist( flight2 );
}
);
}
@Override
protected void cleanupTestData() throws Exception {
inTransaction(
(session) -> {
session.createQuery( "delete Flight" ).executeUpdate();
session.createQuery( "delete Airport" ).executeUpdate();
}
);
}
@Test
public void testNonEnhanced() {
final StatisticsImplementor statistics = sessionFactory().getStatistics();
statistics.clear();
inTransaction(
(session) -> {
final Flight flight1 = session.byId( Flight.class ).load( 1 );
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
assertThat( Hibernate.isInitialized( flight1 ), is( true ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "origination" ), is( true ) );
assertThat( Hibernate.isInitialized( flight1.getOrigination() ), is( false ) );
assertThat( flight1.getOrigination(), instanceOf( HibernateProxy.class ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "destination" ), is( true ) );
assertThat( Hibernate.isInitialized( flight1.getDestination() ), is( false ) );
assertThat( flight1.getDestination(), instanceOf( HibernateProxy.class ) );
}
);
}
}