When I have below entity.
@Entity
class Article {
@OneToMany
Set<Comment> comments;
}
@Entity
class Comment {
@ManyToOne
User author;
}
And create some view specification class using EntityGraph and static metamodel like below.
class ArticleViewSpec {
/**
* fetch comments and each of comment's author.
*/
public static final Function<EntityManager, EntityGraph<Article>> DETAIL = em -> {
EntityGraph<Article> graph = em.createEntityGraph(Article.class);
Subgraph<Comment> sgComment = graph.addSubgraph(Article_.comments);
sgComment.addAttributeNodes(Comment_.author);
return graph;
};
}
But above class can't be compiled, because expected type is not
Subgraph<Comment> sgComment = graph.addSubgraph(Article_.comments);
but
Subgraph<Set<Comment>> sgComment = graph.addSubgraph(Article_.comments);
This problem occurs when we have attribute that extends javax.persistence.metamodel.PluralAttribute
.
(e.g. SetAttribute, ListAttribute)
This behaviour is obviously from api spec.
javax.persistence.EntityGraph#addSubgraph(javax.persistence.metamodel.Attribute<T,X>)
But how can I create EntityGraph programmably and type-safely using JPA static MetaModel in these case ?
/**
* fetch comments and each of comment's author.
*/
public static final Function<EntityManager, EntityGraph<Article>> DETAIL = em -> {
EntityGraph<Article> graph = em.createEntityGraph(Article.class);
Subgraph<Comment> sgComment =
graph.addSubgraph(Article_.comments.getName(), Comment.class);
sgComment.addAttributeNodes(Comment_.author);
return graph;
};
I ran into the same issue, and after doing some research I'm pretty certain this is a flaw in the JPA API.
The implementation in EclipseLink seems to do the correct thing:
public <T> Subgraph<T> addSubgraph(Attribute<X, T> attribute) {
Class type = attribute.getJavaType();
if (attribute.isCollection()) {
type = ((PluralAttribute) attribute).getBindableJavaType();
}
return addSubgraph(attribute.getName(), type);
}
Notice how the implemetation violates the declaration from the interface when a PluralAttribute
is given: With T
of the Attribute
being a Collection<something>
, the method does not actually return a Subgraph<Collection<something>>
as declared but an instance of Subgraph<something>
instead.
To me it looks as though the API is actually broken in that respect, and noone seems to care because probably not many people make use of EntityGraph
s and the static metamodel, though this would be a good thing to do.
Someone should create an issue somewhere to get that part of the API fixed.
My current 'fix' for the problem is a kind of custom EntityGraphBuilder
which accepts SingularAttribute<E,X>
and PluralAttribute<E,?,X>
to allow type safe-ish creation of EntityGraph
s. Basically, I just have my EntityGraphElement
s representing an attribute, or node in the tree, and plug them together via type-safe generic methods. This also has the benefit of merging the distinct for-whatever-reason EntityGraph
and SubGraph
interfaces under a common API, so that EntityGraph
representations can be created and used and re-used as subgraphs in other entity graphs.
My solution could probably be applied to the JPA API too, it basically just splits up the
<T> Subgraph<T> addSubgraph(Attribute<X, T> attribute)
into two methods:
<F> EntityGraphElement<F> fetch(SingularAttribute<? super T, F> attr);
and
<F> EntityGraphElement<F> fetch(PluralAttribute<? super T, ?, F> attr);
Update:
Using some generics Voodoo, one method is enough:
<F, A extends Attribute<? super T, ?> & Bindable<F>> EntityGraphElement<F> fetch(A attribute);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With