| When programatically creating EntityGraph subgraphs we need to add subgraphs for keys and values independently using addKeySubgraph(...) and addSubgraph(...) methods. However, each of these method will replace the attribute node from the previous step with a new one. Thus, if we first "add" a key subgraph, adding the value subgraph later will effectively remove the key subgraph and vice versa - if we add the value subgraph first, then the key, the value one will be removed. In other words, the last one wins and the previous state is lost. For example, given MockEntity.java:
import java.util.Map;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
@Entity
public class MockEntity {
private String id;
private Map< MockEntity, MockEntity > map;
@Id
public String getId() { return id; }
public void setId(String id) { this.id = id; }
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@ElementCollection( targetClass = MockEntity.class)
public Map<MockEntity, MockEntity> getMap() { return map; }
public void setMap(Map<MockEntity, MockEntity> map) { this.map = map; }
}
... the following two methods illustrate the the two cases (only the order of adding subgraphs is different):
void testHHHMapSubgraphsKeyFirst(EntityManager entityManager) {
EntityGraph<MockEntity> graph = entityManager.createEntityGraph(MockEntity.class);
Subgraph<MockEntity> keySubgraph = graph.addKeySubgraph("map");
Subgraph<MockEntity> valueSubgraph = graph.addSubgraph("map");
int count = 0;
for (AttributeNode<?>node: graph.getAttributeNodes()) {
if ("map".equals(node.getAttributeName())) {
count++;
Assert.assertTrue("Missing the value subgraph", !node.getSubgraphs().isEmpty()); Assert.assertTrue("Missing the key subgraph", !node.getKeySubgraphs().isEmpty()); }
}
Assert.assertEquals( 1, count );
}
and:
void testHHHMapSubgraphsValueFirst(EntityManager entityManager) {
EntityGraph<MockEntity> graph = entityManager.createEntityGraph(MockEntity.class);
Subgraph<MockEntity> valueSubgraph = graph.addSubgraph("map");
Subgraph<MockEntity> keySubgraph = graph.addKeySubgraph("map");
int count = 0;
for (AttributeNode<?>node: graph.getAttributeNodes()) {
if ("map".equals(node.getAttributeName())) {
count++;
Assert.assertTrue("Missing the key subgraph", !node.getKeySubgraphs().isEmpty()); Assert.assertTrue("Missing the value subgraph", !node.getSubgraphs().isEmpty()); }
}
Assert.assertEquals( 1, count );
}
There is even a debug-level logged note in the org.hibernate.jpa.graph.internal.AbstractGraphNode addAttributeNode() method that seems related to this case (does not expect it) - specifically "Encountered request to add entity graph node [%s] using a name [%s] under which another node is already registered [%s]":
protected AttributeNodeImpl addAttributeNode(AttributeNodeImpl attributeNode) {
if ( ! mutable ) {
throw new IllegalStateException( "Entity/sub graph is not mutable" );
}
if ( attributeNodeMap == null ) {
attributeNodeMap = new HashMap<String, AttributeNodeImplementor<?>>();
}
else {
final AttributeNode old = attributeNodeMap.get( attributeNode.getRegistrationName() );
if ( old != null ) {
log.debugf(
"Encountered request to add entity graph node [%s] using a name [%s] under which another " +
"node is already registered [%s]",
old.getClass().getName(),
attributeNode.getRegistrationName(),
attributeNode.getClass().getName()
);
}
}
attributeNodeMap.put( attributeNode.getRegistrationName(), attributeNode );
return attributeNode;
}
|