Skip to content

Commit

Permalink
HHH-14730 Test lazy loading of bytecode-enhancement proxy triggered b…
Browse files Browse the repository at this point in the history
…y loading of a collection with eager references to that proxy
  • Loading branch information
yrodiere committed Jul 16, 2021
1 parent 8b02aaf commit 4a2d19a
Show file tree
Hide file tree
Showing 6 changed files with 386 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.hibernate.orm.test.mapping.lazytoone.collectioninitializer;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Company {
@Id
private Long id;

@Override
public String toString() {
return "Company{" +
"id=" + id +
'}';
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.hibernate.orm.test.mapping.lazytoone.collectioninitializer;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

@Entity
public class CostCenter {
@Id
private Long id;

@ManyToOne(optional = false)
private Company company;

@Override
public String toString() {
return "CostCenter{" +
"id=" + id +
", company=" + company +
'}';
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public Company getCompany() {
return company;
}

public void setCompany(Company company) {
this.company = company;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* 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.collectioninitializer;

import org.hibernate.Hibernate;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;

import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.loader.BatchFetchStyle.PADDED;

/**
* Test lazy-to-one initialization within a collection initialization,
* with the PADDED batch-fetch-style.
* <p>
* In particular, with Offer having a lazy to-one association to CostCenter,
* and User having a lazy to-many association to UserAuthorization1 and UserAuthorization2,
* and UserAuthorization1 and UserAuthorization2 having an EAGER association to CostCenter,
* test:
* <ul>
* <li>Get a reference to Offer (which will create an uninitialized proxy for CostCenter)</li>
* <li>Get a reference to User</li>
* <li>Initialize User's collection containing UserAuthorization1 and UserAuthorization2,
* which will initialize CostCenter DURING the loading,
* which used to fail because we tried to initialize CostCenter twice
* (once for UserAuthorization1, and once for UserAuthorization2)</li>
* </ul>
*/
@RunWith(BytecodeEnhancerRunner.class)
@TestForIssue(jiraKey = "HHH-14730")
public class InitLazyToOneWithinPaddedCollectionInitializationAllowProxyTest
extends BaseNonConfigCoreFunctionalTestCase {

@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( User.class );
sources.addAnnotatedClass( UserAuthorization.class );
sources.addAnnotatedClass( Company.class );
sources.addAnnotatedClass( CostCenter.class );
sources.addAnnotatedClass( Offer.class );
}

@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
ssrb.applySetting( AvailableSettings.BATCH_FETCH_STYLE, PADDED );
ssrb.applySetting( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, 10 );
}

@Override
protected void afterSessionFactoryBuilt(SessionFactoryImplementor sessionFactory) {
inTransaction( session -> {
User user0 = new User();
user0.setId( 0L );
session.persist( user0 );

User user1 = new User();
user1.setId( 1L );
session.persist( user1 );

User user2 = new User();
user2.setId( 2L );
session.persist( user2 );

Company company = new Company();
company.setId( 2L );
session.persist( company );

CostCenter costCenter = new CostCenter();
costCenter.setId( 3L );
costCenter.setCompany( company );
session.persist( costCenter );

UserAuthorization user0Authorization1 = new UserAuthorization();
user0Authorization1.setId( 1L );
user0Authorization1.setUser( user0 );
user0Authorization1.setCostCenter( costCenter );
session.persist( user0Authorization1 );

UserAuthorization user1Authorization1 = new UserAuthorization();
user1Authorization1.setId( 11L );
user1Authorization1.setUser( user1 );
user1Authorization1.setCostCenter( costCenter );
session.persist( user1Authorization1 );

UserAuthorization user1Authorization2 = new UserAuthorization();
user1Authorization2.setId( 12L );
user1Authorization2.setUser( user1 );
user1Authorization2.setCostCenter( costCenter );
session.persist( user1Authorization2 );

UserAuthorization user2Authorization1 = new UserAuthorization();
user2Authorization1.setId( 21L );
user2Authorization1.setUser( user2 );
user2Authorization1.setCostCenter( costCenter );
session.persist( user2Authorization1 );

UserAuthorization user2Authorization2 = new UserAuthorization();
user2Authorization2.setId( 22L );
user2Authorization2.setUser( user2 );
user2Authorization2.setCostCenter( costCenter );
session.persist( user2Authorization2 );

UserAuthorization user2Authorization3 = new UserAuthorization();
user2Authorization3.setId( 23L );
user2Authorization3.setUser( user2 );
user2Authorization3.setCostCenter( costCenter );
session.persist( user2Authorization3 );

Offer offer = new Offer();
offer.setId( 6L );
offer.setCostCenter( costCenter );
session.persist( offer );
} );
}

@Test
public void testOneReference() {
inTransaction( (session) -> {
// Add a lazy proxy of the cost center to the persistence context
// through the lazy to-one association from the offer.
Offer offer = session.find( Offer.class, 6L );

User user = session.find( User.class, 0L );

assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isFalse();

// Trigger lazy-loading of the cost center
// through the loading of the authorization,
// which contains an eager reference to the cost center.
assertThat( user.getAuthorizations().size() ).isEqualTo( 1 );

assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isTrue();
} );
}

@Test
public void testTwoReferences() {
inTransaction( (session) -> {
// Add a lazy proxy of the cost center to the persistence context
// through the lazy to-one association from the offer.
Offer offer = session.find( Offer.class, 6L );

User user = session.find( User.class, 1L );

assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isFalse();

// Trigger lazy-loading of the cost center
// through the loading of the 2 authorizations,
// which both contain an eager reference to the cost center.
assertThat( user.getAuthorizations().size() ).isEqualTo( 2 );

assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isTrue();
} );
}

@Test
public void testThreeReferences() {
inTransaction( (session) -> {
// Add a lazy proxy of the cost center to the persistence context
// through the lazy to-one association from the offer.
Offer offer = session.find( Offer.class, 6L );

User user = session.find( User.class, 2L );

assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isFalse();

// Trigger lazy-loading of the cost center
// through the loading of the 3 authorizations,
// which all contain an eager reference to the cost center.
assertThat( user.getAuthorizations().size() ).isEqualTo( 3 );

assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isTrue();
} );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.hibernate.orm.test.mapping.lazytoone.collectioninitializer;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

import static javax.persistence.FetchType.LAZY;

@Entity
public class Offer {
@Id
private Long id;

@ManyToOne(fetch = LAZY, optional = false)
private CostCenter costCenter;

@Override
public String toString() {
return "Offer{" +
"id=" + getId() +
'}';
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public CostCenter getCostCenter() {
return costCenter;
}

public void setCostCenter(CostCenter costCenter) {
this.costCenter = costCenter;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.hibernate.orm.test.mapping.lazytoone.collectioninitializer;

import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import static javax.persistence.CascadeType.ALL;

@Entity
@Table(name = "users")
public class User {
@Id
private Long id;

@OneToMany(mappedBy = "user", cascade = ALL, orphanRemoval = true)
private List<UserAuthorization> authorizations = new ArrayList<>();

@Override
public String toString() {
return "User{" +
"id='" + getId() + '\'' +
'}';
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public List<UserAuthorization> getAuthorizations() {
return authorizations;
}

public void setAuthorizations(List<UserAuthorization> authorizations) {
this.authorizations = authorizations;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.hibernate.orm.test.mapping.lazytoone.collectioninitializer;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

@Entity
public class UserAuthorization {
@Id
private Long id;

@ManyToOne(optional = false)
private User user;

@ManyToOne(optional = false)
private CostCenter costCenter;

@Override
public String toString() {
return "UserAuthorization{" +
"id='" + getId() + '\'' +
'}';
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

public CostCenter getCostCenter() {
return costCenter;
}

public void setCostCenter(CostCenter costCenter) {
this.costCenter = costCenter;
}

}

0 comments on commit 4a2d19a

Please sign in to comment.