Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] Number of problems with query hint extensions

Hello Tom,

playing around with JPA 2.0, I faced the first issue.
The following CriteriaQuery is mapped to the SQL statement below.
Model:
Scenario (1:N) Depot (1:N) Vehicle (1:N) Tour (1:N) TourPosition (<|LoadStop, <|OrderStop)

JPQ:
CriteriaQuery<Depot> cq = cfab.createQuery (Depot.class);
cq.distinct (false);
Root<Depot> depot = cq.from (Depot.class);
depot.fetch ("locatedVehicles", JoinType.LEFT).fetch ("tours", JoinType.LEFT).fetch
("positions", JoinType.INNER);
cq.where (crit.equal (depot.get("scenario").get ("id"), 1)); // ! //

SQL:
SELECT t1.depot_id, ...
FROM depot t1
LEFT OUTER JOIN vehicle t0 ON (t0.depot_id = t1.depot_id)
LEFT OUTER JOIN tour t2 ON (t2.vehicle_id = t0.vehicle_id),tour_position t3
LEFT OUTER JOIN load_stop t4 ON (t4.tour_position_id = t3.tour_position_id)
LEFT OUTER JOIN order_stop t5 ON (t5.tour_position_id = t3.tour_position_id),
scenario t6
WHERE ((t6.scenario_id = ?)
  AND ((t6.scenario_id = t1.scenario_id)
  AND (t3.tour_id = t2.tour_id)));

The join of the scenario is redundant, a simple "where t1.scenario_id = ?") is enough.

Specifying the id of the scenario directly
cq.where (crit.equal (depot.get("scenario_id"), 1));
is not possible, as an IllegalArgumentException is thrown:
"The attribute [scenario_id] from the managed type [EntityTypeImpl@12112029:Depot [ javaType:
class net.uniopt.domain.ot.Depot descriptor: RelationalDescriptor(net.uniopt.domain.ot.Depot -->
[DatabaseTable(depot)]), mappings: 20]] is not present."

Should I open an issue on this?

Kind Regards, Michael

Tom Ware schrieb:
> Hi Michael,
> 
>   I noticed you posted a couple of times related to this thread.  I'll
> answer in this one.
> 
>   The specification does not require JPA implementers to implement
> nested fetch joins in the criteria API.  EclipseLink does implement
> them.  That means several things to you:
> 
> 1. You should be able to get the join you are looking for from EclipseLink
> 2. The API you use to do that will be compatible with other JPA providers
> 3. If you choose another JPA provider, you should check what their level
> of support is.
> 
>   Here is a simple criteria-API-based example. (Customer has 1-M to
> Order and Order has 1-1 to SalesPerson
> 
>         EntityManager em = createEntityManager();
>         CriteriaBuilder qb = em.getCriteriaBuilder();
>         CriteriaQuery<Customer> cq = qb.createQuery(Customer.class);
>         Root<Customer> root = cq.from(Customer.class);
>         Fetch<Customer, Order> o = root.fetch("orders", JoinType.LEFT);
>         o.fetch("salesPerson");
>         List result = em.createQuery(cq).getResultList();
> 
>   EclipseLink produces this SQL:
> 
> SELECT t1.CUST_ID, t1.NAME, t1.CITY, t1.CUST_VERSION, t0.ORDER_ID,
> t0.QUANTITY, t0.SHIP_ADDR, t0.ORDER_VERSION, t0.CUSTOMER_CUST_ID,
> t0.SALESPERSON_ID, t0.ITEM_ID, t0.BILLEDCUSTOMER_CUST_ID, t2.ID, t2.NAME
> FROM CMP3_CUSTOMER t1
>   LEFT OUTER JOIN CMP3_ORDER t0 ON (t0.CUSTOMER_CUST_ID = t1.CUST_ID),
>   CMP3_SALESPERSON t2 WHERE (t2.ID = t0.SALESPERSON_ID)
> 
>   As for comments about existing bugs, if you find legitimate bugs,
> please feel free to file them.  A test case definitely makes solving the
> issue easier.
> 
>   I would also like to encourage you to vote for any bugs you find
> important. We are prioritizing bugs with higher numbers of votes.
> 
> -Tom
> 
> 
> Michael Simons wrote:
>> Hello,
>>
>> guess what I fall over while trying to understand the JPA 2 Spec,
>> Chapter 6.5.4:
>> "Multiple levels of fetch joins are not required to be supported by an
>> implementation of this
>> specification. Applications that use multi-level fetch joins will not
>> be portable."
>>
>> I don't understand at all, why this restriction is made in the
>> specification.
>> Does anyone know this?
>> Does EclipseLink implement the "multi-level fetch joins"?
>>
>> We absolutely need "multi-level fetch joins". So if EL does not
>> support them, there's no sense
>> in migrating to JPA 2.0.
>>
>> Kind Regards, Michael
>>
>> Tom Ware schrieb:
>>> Hi Michael,
>>>
>>>   I just realized there is another JPA-based way you can do this.  I
>>> believe the JPA 2.0 criteria API allows nested fetch joins in a way that
>>> could allow this to work as well.  (EclipseLink 2.0 or better)  I don't
>>> have a good documentation link aside from the specification itself, but
>>> here's a chunk of code from our tests that does a fetch.
>>>
>>>         CriteriaBuilder qb = em.getCriteriaBuilder();
>>>         CriteriaQuery<Employee> cq = qb.createQuery(Employee.class);
>>>         Root<Employee> root = cq.from(Employee.class);
>>>         root.fetch("phoneNumbers", JoinType.LEFT);
>>>         List result = em.createQuery(cq).getResultList();
>>>
>>>   I think that playing around with the fetch API should allow you to get
>>> the query you want.
>>>
>>>   For native code, here is a sample using a slightly different Object
>>> model. Customer has a 1-M to Order and Order has a 1-1 to salesPerson.
>>>
>>>   Start with this:
>>>
>>>         EntityManager em = createEntityManager();
>>>         Query query = em.createQuery("select c from Customer c");
>>>
>>>   The following replicates your issue:
>>>
>>>         query.setHint(QueryHints.FETCH, "c.orders");
>>>         query.setHint(QueryHints.FETCH, "o.orders.salesPerson");
>>>         List results = query.getResultList();
>>>
>>>   The follow native API avoids it:
>>>
>>>     // get the underlying Eclipse query
>>>         ReadAllQuery raq =
>>> (ReadAllQuery)((JpaQuery)query).getDatabaseQuery();
>>>
>>>     // get the EclipseLink ExpressionBuilder for the query
>>>         ExpressionBuilder customerBuilder = raq.getExpressionBuilder();
>>>
>>>     // build an expression to get the orders
>>>     // orders is 1-m so use anyOf() for join or anyOfAllowingNull() for
>>> outer join
>>>         Expression ordersExp = customerBuilder.anyOf("orders");
>>>
>>>     // build an expression for salesPerson using the already-created
>>>     // ordersExp to indicate to use the same join
>>>     // use get() because this is a 1-1 or getAllowingNull() for outer
>>> join
>>>         Expression salesPersonExp = ordersExp.get("salesPerson");
>>>
>>>     // fetch join these two attributes
>>>         raq.addJoinedAttribute(ordersExp);
>>>         raq.addJoinedAttribute(salesPersonExp);
>>>
>>>     // back to standard JPA
>>>         List results = query.getResultList();
>>>
>>> There is some documentation here:
>>>
>>> http://wiki.eclipse.org/Using_EclipseLink_JPA_Extensions_%28ELUG%29#How_to_Use_EclipseLink_Query_API_in_JPA_Queries
>>>
>>>
>>>
>>> -Tom



Back to the top