Skip to content

Commit

Permalink
HHH-13658 : make NO_PROXY unnecessary
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
sebersole committed Jan 13, 2021
1 parent 949ba3b commit 0c97499
Show file tree
Hide file tree
Showing 5 changed files with 408 additions and 0 deletions.
@@ -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;
}
}
@@ -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;
}
}
@@ -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 ) );
}
);
}
}
@@ -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 ) );
}
);
}

}

0 comments on commit 0c97499

Please sign in to comment.