| 
 | 1 | +/*  | 
 | 2 | + * Hibernate, Relational Persistence for Idiomatic Java  | 
 | 3 | + *  | 
 | 4 | + * License: GNU Lesser General Public License (LGPL), version 2.1 or later  | 
 | 5 | + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html  | 
 | 6 | + */  | 
 | 7 | +package org.hibernate.orm.test.entitygraph;  | 
 | 8 | + | 
 | 9 | +import java.util.List;  | 
 | 10 | + | 
 | 11 | +import org.hibernate.testing.jdbc.SQLStatementInspector;  | 
 | 12 | +import org.hibernate.testing.orm.junit.DomainModel;  | 
 | 13 | +import org.hibernate.testing.orm.junit.Jira;  | 
 | 14 | +import org.hibernate.testing.orm.junit.SessionFactory;  | 
 | 15 | +import org.hibernate.testing.orm.junit.SessionFactoryScope;  | 
 | 16 | +import org.junit.jupiter.api.BeforeAll;  | 
 | 17 | +import org.junit.jupiter.api.Test;  | 
 | 18 | + | 
 | 19 | +import jakarta.persistence.Entity;  | 
 | 20 | +import jakarta.persistence.EntityGraph;  | 
 | 21 | +import jakarta.persistence.FetchType;  | 
 | 22 | +import jakarta.persistence.GeneratedValue;  | 
 | 23 | +import jakarta.persistence.Id;  | 
 | 24 | +import jakarta.persistence.JoinColumn;  | 
 | 25 | +import jakarta.persistence.ManyToOne;  | 
 | 26 | +import jakarta.persistence.NamedAttributeNode;  | 
 | 27 | +import jakarta.persistence.NamedEntityGraph;  | 
 | 28 | +import jakarta.persistence.TypedQuery;  | 
 | 29 | +import jakarta.persistence.criteria.CriteriaBuilder;  | 
 | 30 | +import jakarta.persistence.criteria.CriteriaQuery;  | 
 | 31 | +import jakarta.persistence.criteria.Join;  | 
 | 32 | +import jakarta.persistence.criteria.JoinType;  | 
 | 33 | +import jakarta.persistence.criteria.Root;  | 
 | 34 | + | 
 | 35 | +import static org.assertj.core.api.Assertions.assertThat;  | 
 | 36 | +import static org.hibernate.jpa.SpecHints.HINT_SPEC_FETCH_GRAPH;  | 
 | 37 | + | 
 | 38 | +/**  | 
 | 39 | + * @author Marco Belladelli  | 
 | 40 | + */  | 
 | 41 | +@DomainModel( annotatedClasses = {  | 
 | 42 | +		EntityGraphAndJoinTest.Person.class,  | 
 | 43 | +		EntityGraphAndJoinTest.Address.class,  | 
 | 44 | +} )  | 
 | 45 | +@SessionFactory( useCollectingStatementInspector = true )  | 
 | 46 | +@Jira( "https://hibernate.atlassian.net/browse/HHH-17629" )  | 
 | 47 | +public class EntityGraphAndJoinTest {  | 
 | 48 | +	@BeforeAll  | 
 | 49 | +	public void setUp(SessionFactoryScope scope) {  | 
 | 50 | +		scope.inTransaction( session -> {  | 
 | 51 | +			final Address a1 = new Address( 1L, "test" );  | 
 | 52 | +			final Address a2 = new Address( 2L, "test" );  | 
 | 53 | +			session.persist( a1 );  | 
 | 54 | +			session.persist( a2 );  | 
 | 55 | +			session.persist( new Person( "Marco", a1 ) );  | 
 | 56 | +			session.persist( new Person( "Andrea", a2 ) );  | 
 | 57 | +		} );  | 
 | 58 | +	}  | 
 | 59 | + | 
 | 60 | +	@Test  | 
 | 61 | +	public void testHqlJoin(SessionFactoryScope scope) {  | 
 | 62 | +		executeQuery( scope, false, false );  | 
 | 63 | +	}  | 
 | 64 | + | 
 | 65 | +	@Test  | 
 | 66 | +	public void testHqlLeftJoin(SessionFactoryScope scope) {  | 
 | 67 | +		executeQuery( scope, false, true );  | 
 | 68 | +	}  | 
 | 69 | +	  | 
 | 70 | +	@Test  | 
 | 71 | +	public void testCriteriaJoin(SessionFactoryScope scope) {  | 
 | 72 | +		executeQuery( scope, true, false );  | 
 | 73 | +	}  | 
 | 74 | + | 
 | 75 | +	@Test  | 
 | 76 | +	public void testCriteriaLeftJoin(SessionFactoryScope scope) {  | 
 | 77 | +		executeQuery( scope, true, true );  | 
 | 78 | +	}  | 
 | 79 | + | 
 | 80 | +	private void executeQuery(SessionFactoryScope scope, boolean criteria, boolean leftJoin) {  | 
 | 81 | +		final SQLStatementInspector inspector = scope.getCollectingStatementInspector();  | 
 | 82 | +		inspector.clear();  | 
 | 83 | + | 
 | 84 | +		scope.inTransaction( session -> {  | 
 | 85 | +			final TypedQuery<Person> query;  | 
 | 86 | +			if ( criteria ) {  | 
 | 87 | +				final CriteriaBuilder cb = session.getCriteriaBuilder();  | 
 | 88 | +				final CriteriaQuery<Person> cq = cb.createQuery( Person.class );  | 
 | 89 | +				final Root<Person> root = cq.from( Person.class );  | 
 | 90 | +				final Join<Person, Address> join = root.join( "address", leftJoin ? JoinType.LEFT : JoinType.INNER );  | 
 | 91 | +				cq.distinct( true ).where( cb.equal( join.get( "description" ), "test" ) ).orderBy( cb.asc( join.get( "id" ) ) );  | 
 | 92 | +				query = session.createQuery( cq.distinct( true ) );  | 
 | 93 | +			}  | 
 | 94 | +			else {  | 
 | 95 | +				query = session.createQuery( String.format(  | 
 | 96 | +						"select distinct p from Person p %s p.address a where a.description = 'test' order by a.id",  | 
 | 97 | +						leftJoin ? "left join" : "join"  | 
 | 98 | +				), Person.class );  | 
 | 99 | +			}  | 
 | 100 | +			final EntityGraph<?> entityGraph = session.getEntityGraph( "test-graph" );  | 
 | 101 | +			final List<Person> resultList = query.setHint( HINT_SPEC_FETCH_GRAPH, entityGraph ).getResultList();  | 
 | 102 | +			assertThat( resultList ).hasSize( 2 );  | 
 | 103 | +			assertThat( resultList.stream().map( p -> p.getAddress().getId() ) ).containsExactly( 1L, 2L );  | 
 | 104 | +			inspector.assertExecutedCount( 1 );  | 
 | 105 | +			inspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 );  | 
 | 106 | +		} );  | 
 | 107 | +	}  | 
 | 108 | + | 
 | 109 | +	@Entity( name = "Address" )  | 
 | 110 | +	public static class Address {  | 
 | 111 | +		@Id  | 
 | 112 | +		private Long id;  | 
 | 113 | + | 
 | 114 | +		private String description;  | 
 | 115 | + | 
 | 116 | +		public Address() {  | 
 | 117 | +		}  | 
 | 118 | + | 
 | 119 | +		public Address(Long id, String description) {  | 
 | 120 | +			this.id = id;  | 
 | 121 | +			this.description = description;  | 
 | 122 | +		}  | 
 | 123 | + | 
 | 124 | +		public Long getId() {  | 
 | 125 | +			return id;  | 
 | 126 | +		}  | 
 | 127 | + | 
 | 128 | +		public String getDescription() {  | 
 | 129 | +			return description;  | 
 | 130 | +		}  | 
 | 131 | +	}  | 
 | 132 | + | 
 | 133 | +	@Entity( name = "Person" )  | 
 | 134 | +	@NamedEntityGraph( name = "test-graph", attributeNodes = {  | 
 | 135 | +			@NamedAttributeNode( "address" ),  | 
 | 136 | +	} )  | 
 | 137 | +	public static class Person {  | 
 | 138 | +		@Id  | 
 | 139 | +		@GeneratedValue  | 
 | 140 | +		private Integer id;  | 
 | 141 | + | 
 | 142 | +		private String name;  | 
 | 143 | + | 
 | 144 | +		@ManyToOne( fetch = FetchType.LAZY )  | 
 | 145 | +		@JoinColumn( name = "address_id" )  | 
 | 146 | +		private Address address;  | 
 | 147 | + | 
 | 148 | +		public Person() {  | 
 | 149 | +		}  | 
 | 150 | + | 
 | 151 | +		public Person(String name, Address address) {  | 
 | 152 | +			this.name = name;  | 
 | 153 | +			this.address = address;  | 
 | 154 | +		}  | 
 | 155 | + | 
 | 156 | +		public Address getAddress() {  | 
 | 157 | +			return address;  | 
 | 158 | +		}  | 
 | 159 | +	}  | 
 | 160 | +}  | 
0 commit comments