Help

Linda has written up the new typesafe query API. I previously blogged the reasoning behind this stuff here and here.

An open issue that Linda doesn't mention is query execution. I'm trying to convince the rest of the group that we should carry the typesafety all the way through to the query result set. Here's what I wrote to the group a few weeks ago:

Folks, I figured out a refactoring that gives us a way to do typesafe result sets, avoiding the use of Result. In this new approach, CriteriaQuery and Query would both have a type parameter. You could write code like this, if you have a single selection:
  CriteriaQuery<Order> q = qb.create(Order.class);
  Root<Order> order = q.from(Order.class);
  q.select(order);

  Query<Order> eq = em.createQuery(q);
  List<Order> res= eq.getTypedResultList();
like this, if you have multiple selections and an object to wrap them in:
  CriteriaQuery<OrderProduct> q = qb.create(OrderProduct.class);
  Root<Order> order = q.from(Order.class);
  Join<Item, Product> product = order.join(Order_.items)
                                     .join(Item_.product);
  q.select( qb.construct(OrderProduct.class, order, product) );

  Query<OrderProduct> eq = em.createQuery(q);
  List<OrderProduct> res= eq.getTypedResultList();
Or, if you don't have a nice wrapper class like OrderProduct, you can fall back to use Result:
  CriteriaQuery<Result> q = qb.create();
  Root<Order> order = q.from(Order.class);
  Join<Item, Product> product = order.join(Order_.items)
                                     .join(Item_.product);
  q.select( qb.result(order, product) );

  Query<Result> eq = em.createQuery(q);
  List<Result> res= eq.getTypedResultList();
This change let's people directly get typesafe lists of entities or wrappers, which is something that many people have asked for!

The big point about this API is that I can't write a query which selects Foo and then try to put it in a List<Bar>. It's truly typesafe, end-to-end.

The sticking point with this is that javax.persistence.Query does not currently have the needed type parameter, and there are millions of queries written to the JPA 1.0 APIs which would suddenly spit compiler warnings if we added a type parameter. So we might have to introduce a new interface like TypesafeQuery or something.

17 comments:
 
15. May 2009, 12:25 CET | Link
Anonymous

Join<Item, Product> product = order.join("items").join("product") isn't type-safe.

ReplyQuote
 
15. May 2009, 13:15 CET | Link

Excuse me, I should have read the examples properly before copy/pasting, fixed now. :-)

 
15. May 2009, 22:13 CET | Link
Mik Lernout

I don't understand why CriteriaQuery returns a Query, instead of directly the results, which would eliminate the backwards compatibility problems.

Query is used for EJBQL-queries, CriteriaQuery is something very different and queries the datastore using criteria. It sounds a lot like dragging unnecessary implementation concerns in the API.

 
16. May 2009, 01:19 CET | Link
I don't understand why CriteriaQuery returns a Query, instead of directly the results

Well, there's other things you need to do via the Query interface, such as parameter binding...

 
16. May 2009, 18:54 CET | Link
Sanne Grinovero
...there are millions of queries written to the JPA 1.0 APIs which would suddenly spit compiler warnings if we added a type parameter. So we might have to introduce a new interface like TypesafeQuery or something.

All projects I've seen so far using the JPA 1.0 API do an immediate cast to the generified version of List, so either they are living with compiler warnings or they are suppressing them on the same line.

This means IMHO that having typesafe return types vould be great, and not at all a problem. Query looks so much more natural than TypeSafeQuery

I don't think people are expecting to be able to replace their 1.0 EntityManager with a brand new 2.0 without having to make some application changes... personally I'd love to make this kind of changes, and the eventually warnings are good to help me find where the code needs improvements.

 
17. May 2009, 00:39 CET | Link

But Query also needs a type parameter - it's going to be Query<X> if it will return List<X> typesafely.

24. May 2009, 06:49 CET | Link
Joseph

Hi dear in the original string based system i can include property path such as product.category.name How this can be achieved with type safe api thanks

25. May 2009, 04:47 CET | Link

product.get(Product_.category).get(Category_.name)

25. May 2009, 05:07 CET | Link
Vladimir Kovalyuk | kovalyuk(AT)gmail.com

It is no doubt better than string. However I can create query or its parts using just strings but I can't create type-safe Criteria without entity manager. Please shade some light on why EM is required.

25. May 2009, 05:15 CET | Link
Vladimir Kovalyuk

Gays, it is really difficult to remember three inputs in the form? Why I need to type my name and address every time I post here?

Please introduce s:multilineOutputText. It is really strange to see how CRs disappear from my posts.

25. May 2009, 05:20 CET | Link
Vladimir Kovalyuk

Oh boy! I did it. I has always been afraid of. I mistyped guys. I believe you will excuse me that typo.

BTW The button edit would come in handy.

25. May 2009, 05:26 CET | Link
Vladimir Kovalyuk wrote on May 24, 2009 23:07:
It is no doubt better than string. However I can create query or its parts using just strings but I can't create type-safe Criteria without entity manager.

Yes you can. It's EntityManagerFactory.createQueryBuilder().create().

 
26. May 2009, 00:38 CET | Link
Vladimir

To be more specific I mean the ability to use Criteria API on the client not the server side. My understanding of the Specification is that I need persistence unit to properly initialize metamodel to be used in Criteria API. Can I use EntityManagerFactory.createQueryBuilder() without bootstrapping on the client side?

26. May 2009, 00:49 CET | Link
Vladimir

Imagine we display the list of users on the page. We would like to add a checkbox to filter disabled users. We would like to add a edit box to enter filter for name. I'd like to construct a simple Expression on the client side and then pass it with the request to the server side. I'd like to have the only method supporting the query of users for this particular page and use Creteria API for filters. I'd like Eclipse to refactor my Wicket UI code authomatically with changes in the domain model.

 
26. May 2009, 03:04 CET | Link
My understanding of the Specification is that I need persistence unit to properly initialize metamodel to be used in Criteria API. Can I use EntityManagerFactory.createQueryBuilder() without bootstrapping on the client side?

Well, persistence unit just means a jar containing your entity classes and X_ metamodel classes, and EntityManagerFactory is just the runtime representation of that. You are certainly going to need those classes on the client side. You're also certainly going to need jpa.jar, which contains the query APIs, and your persistence provider jar, which implements the interfaces.

And if bootstrapping means reading the entity annotations and creating the metamodel, I don't see how that's a problem, since you need it for error detection when you're creating the query.

So your question is really does createEntityManagerFactory() blow up if I have not specified a JDBC datasource in persistence.xml? Well, my interpretation is no, but I guess the spec is silent on the issues, so let me check with Linda to see if she thinks we should be more explicit about that.

26. Jul 2009, 06:23 CET | Link
chester Chen | cchen(AT)ascentmedia.com

Sorry to pick up the discussion 2 months ago, but I just saw the discussion today.

In our development, we have always made JPA type safe even with 1.0 version. Here is what we did :

define a new interface EntityManagerEx, which will implements all EntityManager's methods. But also add methods such as this

   <T> QueryEx<T> createNativeQuery(String sqlString, Class<T> resultClass);

   .... skips all other methods ...

The Implementation of EntityManagerEx simply delegate to the instance of EntityManage for all other methods and only implements new methods.

Where QueryEx is defined as the following:

public interface QueryEx<T> extends Query {

    public enum QueryType {NAMED, NATIVE, JPA }

    List<T> getResultList();

    T getSingleResult();

    QueryEx<T> setFirstResult(int i);

    QueryEx<T> setFlushMode(FlushModeType flushModeType);

    QueryEx<T> setHint(String s, Object o);

    QueryEx<T> setMaxResults(int i);

    QueryEx<T> setParameter(int i, Calendar calendar, TemporalType temporalType);

    QueryEx<T> setParameter(int i, Date date, TemporalType temporalType);

    QueryEx<T> setParameter(int i, Object o);

    QueryEx<T> setParameter(String s, Calendar calendar, TemporalType temporalType);

    QueryEx<T> setParameter(String s, Date date, TemporalType temporalType);

    QueryEx<T> setParameter(String s, Object o);

    QueryEx<T> setColumnParameterInfos(Collection<ColumnParameterInfo> columnParameterInfos);

    .... skips other methods ...
  }

The results is

we can use QueryEx in consistent manager for all type of queries for JPA 1.0, including direct JDBC calls (for cases JPA doesn't suport).


  EntityManageEx  mEntityManager = getEntityManger();
  QueryEx<Integer> q = mEntityManager.createNativeQuery(GEt_COUNT_SQL, Integer.class)
                                     .setParameter("configName", configName);
  count = q.getSingleResult();

OR

  QueryEx<Config> q =  mEntityManager.createNativeQuery(GET_CONFIG_SQL, Config.class)
                        .setParameter(1, configName);
        
  //if (mLog.isTraceEnabled()) q.setLogSql(true);

  List<Config> configs =q.getResultList();

Chester

 
21. Jun 2014, 16:17 CET | Link

A company cant ever grow beyond its present position without the assistance of promotion. Think that a guy begins a small company in a tiny town business promotion Which is how we must consider a wider market. This is when we must consider stretching your company into other cities. This should help you to balance saturation. money

Post Comment