Skip to content

Commit 3de89ba

Browse files
committed
HHH-19216 - NamedEntityGraph annotation supporting Hibernate parseable format
1 parent c64e3b9 commit 3de89ba

File tree

5 files changed

+247
-0
lines changed

5 files changed

+247
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.annotations;
6+
7+
import java.lang.annotation.Repeatable;
8+
import java.lang.annotation.Target;
9+
import java.lang.annotation.Retention;
10+
11+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
12+
import static java.lang.annotation.ElementType.PACKAGE;
13+
import static java.lang.annotation.ElementType.TYPE;
14+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
15+
16+
/**
17+
* Same as {@linkplain jakarta.persistence.NamedEntityGraph}, but leveraging
18+
* Hibernate's ability to specify the graph as text.
19+
*
20+
* @see org.hibernate.graph.GraphParser
21+
*
22+
* @since 7.0
23+
* @author Steve Ebersole
24+
*/
25+
@Target({TYPE, PACKAGE, ANNOTATION_TYPE})
26+
@Retention(RUNTIME)
27+
@Repeatable(NamedEntityGraphs.class)
28+
public @interface NamedEntityGraph {
29+
/**
30+
* The name used to identify the entity graph in calls to
31+
* {@linkplain org.hibernate.Session#getEntityGraph(String)}.
32+
* Entity graph names must be unique within the persistence unit.
33+
* <p/>
34+
* When applied to a root entity class, the name is optional and
35+
* defaults to the entity-name of that entity.
36+
*/
37+
String name() default "";
38+
39+
/**
40+
* The entity-name of the root of the entity {@linkplain #graph graph}.
41+
* <p/>
42+
* When applied to a root entity class, this is optional and
43+
* defaults to the entity-name of that entity.
44+
*
45+
* @apiNote Entity-name is used here rather than entity-class to allow
46+
* for dynamic models.
47+
*/
48+
String rootEntityName() default "";
49+
50+
/**
51+
* The textual representation of the graph, relative to the named
52+
* {@linkplain #rootEntityName() root entity}.
53+
* See {@linkplain org.hibernate.graph.GraphParser} for details.
54+
*/
55+
String graph();
56+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.annotations;
6+
7+
import java.lang.annotation.Target;
8+
import java.lang.annotation.Retention;
9+
10+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
11+
import static java.lang.annotation.ElementType.PACKAGE;
12+
import static java.lang.annotation.ElementType.TYPE;
13+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
14+
15+
/**
16+
* A grouping of {@link NamedEntityGraph} definitions.
17+
*
18+
* @since 7.0
19+
* @author Steve Ebersole
20+
*/
21+
@Target({TYPE, PACKAGE, ANNOTATION_TYPE})
22+
@Retention(RUNTIME)
23+
public @interface NamedEntityGraphs {
24+
/**
25+
* The grouping of Hibernate named native SQL queries.
26+
*/
27+
NamedEntityGraph[] value();
28+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Testing of Hibernate's {@linkplain org.hibernate.annotations.NamedEntityGraph}.
3+
* <p/>
4+
* We really only test the discovery and basic interpretation of the
5+
* annotations here, since robust testing of the application of graphs
6+
* is handled elsewhere.
7+
*
8+
* @author Steve Ebersole
9+
*/
10+
package org.hibernate.orm.test.entitygraph.named.parsed;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.entitygraph.named.parsed.simple;
6+
7+
import jakarta.persistence.Entity;
8+
import jakarta.persistence.Id;
9+
import jakarta.persistence.NamedAttributeNode;
10+
import org.hibernate.DuplicateMappingException;
11+
import org.hibernate.annotations.NamedEntityGraph;
12+
import org.hibernate.boot.MetadataSources;
13+
import org.hibernate.testing.orm.junit.NotImplementedYet;
14+
import org.hibernate.testing.orm.junit.ServiceRegistry;
15+
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
16+
import org.junit.jupiter.api.Test;
17+
18+
import static org.junit.jupiter.api.Assertions.fail;
19+
20+
21+
/**
22+
* @author Steve Ebersole
23+
*/
24+
@SuppressWarnings("JUnitMalformedDeclaration")
25+
public class DuplicateNameTests {
26+
@Test
27+
@ServiceRegistry
28+
void testDuplicateNamesBaseline(ServiceRegistryScope registryScope) {
29+
final MetadataSources metadataSources = new MetadataSources( registryScope.getRegistry() )
30+
.addAnnotatedClasses( BaselineEntity.class );
31+
try {
32+
metadataSources.buildMetadata();
33+
fail( "Expecting a failure" );
34+
}
35+
catch (DuplicateMappingException expected) {
36+
}
37+
}
38+
39+
@Test
40+
@ServiceRegistry
41+
@NotImplementedYet( reason = "Support for parsed NamedEntityGraph is not implemented" )
42+
void testDuplicateNames(ServiceRegistryScope registryScope) {
43+
final MetadataSources metadataSources = new MetadataSources( registryScope.getRegistry() )
44+
.addAnnotatedClasses( TestEntity.class );
45+
try {
46+
metadataSources.buildMetadata();
47+
fail( "Expecting a failure" );
48+
}
49+
catch (DuplicateMappingException expected) {
50+
}
51+
}
52+
53+
@NamedEntityGraph( name = "test-id-name", graph = "(id, name)" )
54+
@jakarta.persistence.NamedEntityGraph(
55+
name = "test-id-name",
56+
attributeNodes = {
57+
@NamedAttributeNode( "id" ),
58+
@NamedAttributeNode( "name" )
59+
}
60+
)
61+
@Entity
62+
public static class TestEntity {
63+
@Id
64+
private Integer id;
65+
private String name;
66+
}
67+
68+
@jakarta.persistence.NamedEntityGraph(
69+
name = "test-id-name",
70+
attributeNodes = {
71+
@NamedAttributeNode( "id" ),
72+
@NamedAttributeNode( "name" )
73+
}
74+
)
75+
@jakarta.persistence.NamedEntityGraph(
76+
name = "test-id-name",
77+
attributeNodes = {
78+
@NamedAttributeNode( "id" ),
79+
@NamedAttributeNode( "name" )
80+
}
81+
)
82+
@Entity
83+
public static class BaselineEntity {
84+
@Id
85+
private Integer id;
86+
private String name;
87+
}
88+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.entitygraph.named.parsed.simple;
6+
7+
import jakarta.persistence.Entity;
8+
import jakarta.persistence.Id;
9+
import jakarta.persistence.ManyToOne;
10+
import jakarta.persistence.OneToMany;
11+
import org.hibernate.annotations.NamedEntityGraph;
12+
import org.hibernate.engine.spi.SessionFactoryImplementor;
13+
import org.hibernate.graph.spi.AttributeNodeImplementor;
14+
import org.hibernate.graph.spi.RootGraphImplementor;
15+
import org.hibernate.testing.orm.junit.DomainModel;
16+
import org.hibernate.testing.orm.junit.NotImplementedYet;
17+
import org.hibernate.testing.orm.junit.SessionFactory;
18+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
19+
import org.junit.jupiter.api.Test;
20+
21+
import java.util.List;
22+
import java.util.Set;
23+
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
26+
/**
27+
* @author Steve Ebersole
28+
*/
29+
@SuppressWarnings("JUnitMalformedDeclaration")
30+
public class SimpleParsedNamedGraphTests {
31+
@Test
32+
@DomainModel(annotatedClasses = SimpleParsedNamedGraphTests.TestEntity.class)
33+
@SessionFactory(exportSchema = false)
34+
@NotImplementedYet( reason = "Support for parsed NamedEntityGraph is not implemented" )
35+
void checkGraphs(SessionFactoryScope factoryScope) {
36+
final SessionFactoryImplementor sessionFactory = factoryScope.getSessionFactory();
37+
38+
{
39+
final RootGraphImplementor<?> graph = sessionFactory.findEntityGraphByName( "test-id-name" );
40+
assertThat( graph ).isNotNull();
41+
final List<? extends AttributeNodeImplementor<?,?,?>> attributeNodes = graph.getAttributeNodeList();
42+
assertThat( attributeNodes ).hasSize( 2 );
43+
}
44+
45+
{
46+
final RootGraphImplementor<?> graph = sessionFactory.findEntityGraphByName( "test-id-name-parent" );
47+
assertThat( graph ).isNotNull();
48+
final List<? extends AttributeNodeImplementor<?,?,?>> attributeNodes = graph.getAttributeNodeList();
49+
assertThat( attributeNodes ).hasSize( 3 );
50+
}
51+
}
52+
53+
@NamedEntityGraph( name = "test-id-name", graph = "(id, name)" )
54+
@NamedEntityGraph( name = "test-id-name-parent", graph = "(id, name, parent)" )
55+
@Entity
56+
public static class TestEntity {
57+
@Id
58+
private Integer id;
59+
private String name;
60+
@ManyToOne
61+
public TestEntity parent;
62+
@OneToMany( mappedBy = "parent" )
63+
public Set<TestEntity> children;
64+
}
65+
}

0 commit comments

Comments
 (0)