Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -375,23 +375,26 @@ public interface SharedSessionContract extends QueryProducer, AutoCloseable, Ser
* @throws IllegalArgumentException if the graph with the given
* name does not have the given entity type as its root
*
* @see jakarta.persistence.EntityManager#createEntityGraph(String)
*
* @since 6.3
*/
<T> RootGraph<T> createEntityGraph(Class<T> rootType, String graphName);

/**
* Obtain an immutable reference to a predefined
* {@linkplain jakarta.persistence.NamedEntityGraph named entity graph}
* or return {@code null} if there is no predefined graph with the given
* name.
* {@linkplain jakarta.persistence.NamedEntityGraph named entity graph}.
*
* @param graphName the name of the predefined named entity graph
* @throws IllegalArgumentException if there is no predefined graph
* with the given name
*
* @apiNote This method returns {@code RootGraph<?>}, requiring an
* unchecked typecast before use. It's cleaner to obtain a graph using
* the static metamodel for the class which defines the graph, or by
* calling {@link SessionFactory#getNamedEntityGraphs(Class)} instead.
*
* @see jakarta.persistence.EntityManager#getEntityGraph(String)
* @see SessionFactory#getNamedEntityGraphs(Class)
* @see jakarta.persistence.EntityManagerFactory#addNamedEntityGraph(String, EntityGraph)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.hibernate.Session;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.resource.jdbc.spi.JdbcSessionOwner;
Expand Down Expand Up @@ -66,15 +65,6 @@ default SessionImplementor getSession() {
@Override
SessionFactoryImplementor getSessionFactory();

@Override
<T> RootGraphImplementor<T> createEntityGraph(Class<T> rootType);

@Override
RootGraphImplementor<?> createEntityGraph(String graphName);

@Override
RootGraphImplementor<?> getEntityGraph(String graphName);

@Override
<T> QueryImplementor<T> createQuery(CriteriaSelect<T> selectQuery);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.hibernate.bytecode.enhance.spi.interceptor.SessionAssociationMarkers;
import org.hibernate.dialect.Dialect;
import org.hibernate.event.spi.EventSource;
import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.query.Query;
import org.hibernate.SharedSessionContract;
import org.hibernate.Transaction;
Expand Down Expand Up @@ -584,4 +585,13 @@ default boolean isStatelessSession() {
*/
@Incubating
SessionAssociationMarkers getSessionAssociationMarkers();

@Override
<T> RootGraphImplementor<T> createEntityGraph(Class<T> rootType);

@Override
RootGraphImplementor<?> createEntityGraph(String graphName);

@Override
RootGraphImplementor<?> getEntityGraph(String graphName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.event.monitor.spi.EventMonitor;
import org.hibernate.graph.RootGraph;
import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.persister.entity.EntityPersister;
Expand Down Expand Up @@ -632,12 +633,12 @@ public TimeZone getJdbcTimeZone() {
}

@Override
public <T> RootGraph<T> createEntityGraph(Class<T> rootType) {
public <T> RootGraphImplementor<T> createEntityGraph(Class<T> rootType) {
return delegate.createEntityGraph( rootType );
}

@Override
public RootGraph<?> createEntityGraph(String graphName) {
public RootGraphImplementor<?> createEntityGraph(String graphName) {
return delegate.createEntityGraph( graphName );
}

Expand All @@ -647,7 +648,7 @@ public <T> RootGraph<T> createEntityGraph(Class<T> rootType, String graphName) {
}

@Override
public RootGraph<?> getEntityGraph(String graphName) {
public RootGraphImplementor<?> getEntityGraph(String graphName) {
return delegate.getEntityGraph( graphName );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,13 +354,13 @@ protected final boolean applySelectionHint(String hintName, Object value) {
DEPRECATION_LOGGER.deprecatedSetting( HINT_JAVAEE_FETCH_GRAPH, HINT_SPEC_FETCH_GRAPH );
//fall through to:
case HINT_SPEC_FETCH_GRAPH:
applyEntityGraphHint( hintName, value );
applyEntityGraphHint( GraphSemantic.FETCH, value, hintName );
return true;
case HINT_JAVAEE_LOAD_GRAPH:
DEPRECATION_LOGGER.deprecatedSetting( HINT_JAVAEE_LOAD_GRAPH, HINT_SPEC_LOAD_GRAPH );
//fall through to:
case HINT_SPEC_LOAD_GRAPH:
applyEntityGraphHint( hintName, value );
applyEntityGraphHint( GraphSemantic.LOAD, value, hintName );
return true;
case HINT_FETCH_PROFILE:
queryOptions.enableFetchProfile( (String) value );
Expand All @@ -371,17 +371,39 @@ protected final boolean applySelectionHint(String hintName, Object value) {
}
}

protected void applyEntityGraphHint(String hintName, Object value) {
final GraphSemantic graphSemantic = GraphSemantic.fromHintName( hintName );
protected void applyEntityGraphHint(GraphSemantic graphSemantic, Object value, String hintName) {
if ( value instanceof RootGraphImplementor<?> rootGraphImplementor ) {
applyGraph( rootGraphImplementor, graphSemantic );
}
else if ( value instanceof String string ) {
applyGraph( string, graphSemantic );
// try and interpret it as the name of a @NamedEntityGraph
final var entityGraph = getEntityGraph( string );
if ( entityGraph == null ) {
try {
// try and parse it in the entity graph language
applyGraph( string, graphSemantic );
}
catch ( IllegalArgumentException e ) {
throw new IllegalArgumentException( "The string value of the hint '" + hintName
+ "' must be the name of a named EntityGraph, or a representation understood by GraphParser" );
}
}
else {
applyGraph( entityGraph, graphSemantic );
}
}
else {
throw new IllegalArgumentException( "The value of the hint '" + hintName
+ "' must be an instance of EntityGraph or the string name of a named EntityGraph" );
+ "' must be an instance of EntityGraph, the string name of a named EntityGraph, or a string representation understood by GraphParser" );
}
}

private RootGraphImplementor<?> getEntityGraph(String string) {
try {
return getSession().getEntityGraph( string );
}
catch ( IllegalArgumentException e ) {
return null;
}
}

Expand All @@ -392,7 +414,7 @@ protected void applyGraph(String graphString, GraphSemantic graphSemantic) {
throw new IllegalArgumentException(
String.format(
ROOT,
"Invalid entity-graph definition `%s`; expected form `${EntityName}( ${property1} ... )",
"Invalid entity-graph definition '%s'; expected form '${EntityName}( ${property1} ... )'",
graphString
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -673,8 +673,8 @@ public Query<R> applyGraph(@SuppressWarnings("rawtypes") RootGraph graph, GraphS
}

@Override
protected void applyEntityGraphHint(String hintName, Object value) {
super.applyEntityGraphHint( hintName, value );
protected void applyEntityGraphHint(GraphSemantic graphSemantic, Object value, String hintName) {
super.applyEntityGraphHint( graphSemantic, value, hintName );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@
import java.util.HashSet;
import java.util.Set;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.NamedAttributeNode;
import jakarta.persistence.NamedEntityGraph;
import jakarta.persistence.Table;
import jakarta.persistence.TypedQuery;

import org.hibernate.jpa.SpecHints;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import static org.hibernate.Hibernate.isInitialized;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Based on the test developed by Hans Desmet to reproduce the bug reported in HHH-9230
*
Expand All @@ -33,6 +37,23 @@
})
public class QueryWithInParamListAndNamedEntityGraphTest {

@BeforeAll
void prepareTest(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
Person boss = new Person();
boss.id = 2L;
boss.name = "B";
entityManager.persist( boss );
Person person = new Person();
person.id = 1L;
person.name = "X";
person.boss = boss;
entityManager.persist( person );
}
);
}

@Test
public void testInClause(EntityManagerFactoryScope scope) {
// this test works
Expand All @@ -41,9 +62,9 @@ public void testInClause(EntityManagerFactoryScope scope) {
Set<Long> ids = new HashSet<>();
ids.add( 1L );
ids.add( 2L );
TypedQuery<Person> query = entityManager.createQuery( "select p from Person p where p.id in :ids", Person.class );
query.setParameter( "ids", ids );
query.getResultList();
entityManager.createQuery( "select p from Person p where p.id in :ids", Person.class )
.setParameter( "ids", ids )
.getResultList();
}
);
}
Expand All @@ -53,9 +74,10 @@ public void testEntityGraph(EntityManagerFactoryScope scope) {
// this test works
scope.inTransaction(
entityManager -> {
TypedQuery<Person> query = entityManager.createQuery( "select p from Person p", Person.class );
query.setHint( "javax.persistence.loadgraph", entityManager.createEntityGraph( "withBoss" ) );
query.getResultList();
entityManager.createQuery( "select p from Person p", Person.class )
.setHint( SpecHints.HINT_SPEC_LOAD_GRAPH,
entityManager.createEntityGraph( QueryWithInParamListAndNamedEntityGraphTest_.Person_.GRAPH_WITH_BOSS ) )
.getResultList();
}
);
}
Expand All @@ -68,23 +90,38 @@ public void testEntityGraphAndInClause(EntityManagerFactoryScope scope) {
Set<Long> ids = new HashSet<>();
ids.add( 1L );
ids.add( 2L );
TypedQuery<Person> query = entityManager.createQuery( "select p from Person p where p.id in :ids", Person.class );
query.setHint( "javax.persistence.loadgraph", entityManager.createEntityGraph( "withBoss" ) );
query.setParameter( "ids", ids );
query.getResultList();
entityManager.createQuery( "select p from Person p where p.id in :ids", Person.class )
.setHint( SpecHints.HINT_SPEC_LOAD_GRAPH,
entityManager.createEntityGraph( QueryWithInParamListAndNamedEntityGraphTest_.Person_.GRAPH_WITH_BOSS ) )
.setParameter( "ids", ids )
.getResultList();
}
);
}

@Test
public void testNamedEntityGraph(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
Person person =
entityManager.createQuery( "select p from Person p where p.boss is not null", Person.class )
.setHint( SpecHints.HINT_SPEC_LOAD_GRAPH,
QueryWithInParamListAndNamedEntityGraphTest_.Person_.GRAPH_WITH_BOSS )
.getSingleResult();
assertTrue( isInitialized( person.boss ) );
}
);
}

@Entity(name = "Person")
@Table(name = "Person")
@NamedEntityGraph(name = "withBoss", attributeNodes = @NamedAttributeNode("boss"))
@NamedEntityGraph(name = "withBoss",
attributeNodes = @NamedAttributeNode("boss"))
public static class Person {
@Id
@GeneratedValue
private long id;
private String name;
@ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn
private Person boss;
}
Expand Down