jakartaee / persistence

https://jakartaee.github.io/persistence/
Other
187 stars 55 forks source link

factory-level access to named queries and named entity graphs #505

Closed gavinking closed 8 months ago

gavinking commented 10 months ago

It has always seemed to me that these methods:

public  EntityGraph<?> getEntityGraph(String graphName);
public <T> List<EntityGraph<? super T>> getEntityGraphs(Class<T> entityClass);

really logically belong to EntityManagerFactory rather than to EntityManager, though I definitely see why it makes sense to have getEntityGraph() on EntityManager as a convenience for the most common usage.

Relatedly, it seems like an oversight that there's no way to obtain a list of defined named queries from the factory. (And this has come up on #111.)

But I'm not sure precisely what the API should look like here.

First possibility

Naively, perhaps something like:

interface EntityManagerFactory {
    ...

    List<String> getQueryNames();
    Map<String,EntityGraph<?>> getNamedEntityGraphs();
}

On the other hand, there's a pretty big asymmetry here if one of these methods returns a list of names, and the other returns a map to the named things.

Second possibility

Another possibility would be to return instances of the defining annotation types:

interface EntityManagerFactory {
    ...

    Map<String,NamedQuery> getNamedQueries();
    Map<String,NamedNativeQuery> getNamedNativeQueries();
    Map<String,NamedEntityGraph> getNamedEntityGraphs();
    Map<String,SqlResultSetMapping> getSqlResultSetMappings();  // less useful
}

though perhaps in this approach I would move the methods off of EntityManagerFactory to, say, Metamodel, or perhaps to a new Registry object or something.

Third possibility

A final (third) possibility would be to introduce some sort of typesafe reference object for named queries, something like NamedQueryReference<R>.

And thenEntityManagerFactory would have:

interface EntityManagerFactory {
    ...

    <R> Map<String, NamedQueryReference<R>> getNamedQueries(Class<R> resultType);
    <E> Map<String, EntityGraph<E> getNamedEntityGraphs(Class<E> entityType);
}

And then we would also add the following operation to EntityManager:

interface EntityManager {
    ...

    <R> TypedQuery<R> createNamedQuery(NamedQueryReference<R> reference);
}

This last option might look a bit convoluted, perhaps, but it comes with some rather nice possibilities for more typesafe usage of this stuff.

At startup time, you could save away references:

@PersistenceUnit EntityManagerFactory emf;

...

final NamedQueryReference<Author> queryRef = emf.getNamedQueries(Author.class).get("someQueryName");
final EntityGraph<Book> graphRef = emf.getNamedEntityGraphs(Book.class).get("theEntityGraphName");
assert queryRef != null;
assert graphRef != null;

and then at runtime you would use them like this:

@PersistenceUnit EntityManager em;

...

List<Author> result = em.createNamedQuery(queryRef).getResultList();
Book book = em.find(graphRef, id);

Reactions?

gavinking commented 10 months ago

Aaaaactually, forgive me for being slow to see the obvious, but moving in this direction opens up the possibility for even better typesafety. In #459 we added String-typed references to named queries and named entity graphs to the canonical static metamodel.

But with the proposed introduction of NamedQueryReference, we could go even further and have typed references to named queries and entity graphs in the static metamodel, so that you would be able to write stuff like:

Book book = em.find(Book_.withAuthors_, bookId);
List<Book> books = em.createNamedQuery(Book_.byTitle_).setParameter(1, title).getResultList();

where Book_.withAuthors_ is a generated reference to a named EntityGraph and Book_.byTitle_ is a generated reference to a named query.

Now, the caveat here is that our annotations are currently not quite perfect for nailing down the result type of a named JPQL query, but with the addition of an optional member to @NamedQuery I think we would be good. (We can default to Object or Object[], of course, so no big deal.)

This looks great!