Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] EclipseLink Many-to-Many query problems with MySql

I tried out the query expression -

Expression expKw =
eb.anyOf("kwCollection").get("kwName").in(keywordStringList);

where the keywordStringList is a Collection<String> of the submitted
keywords to search on.
"kwCollection" is the many-to-many relationship of MediaListing keywords ( a
Collection<Keywords> ).
"kwName" is the String keyword value.

Using that expression works and results in returning a list of MediaListing
JPA entities that contain at least on keyword that is in the submitted
keywordStringList.

In addition, that generated SQL only joins the KEYWORDS table and the KW_MAP
table once.  This partially solves my problem.

I did realize while testing this implementation that I had a mistake in my
posting of the original query expression.

Previously I stated that this expression -
Expression expKw = eb.anyOf("kwCollection").in(kwList);

was what I was using and that it worked.  It actually does not work and
wasn't the current code I had been trying.  When I execute that type of
expression I get the following error -

Exception [EclipseLink-6075] (Eclipse Persistence Services -
1.1.2.v20090612-r4475): org.eclipse.persistence.exceptions.QueryException
Exception Description: Object comparisons can only use the equal() or
notEqual() operators.  Other comparisons must be done through query keys or
direct attribute level comparisons. 
Expression: [
Relation operator  IN 
   Query Key kwCollection
      Base com.cim.jpa.MediaListing
   Constant [com.cim.jpa.Keywords[kwId=405, kwName=coconut],
com.cim.jpa.Keywords[kwId=406, kwName=coffee]]]
Query: ReportQuery(referenceClass=MediaListing )

The "in" operator for the "anOf" expression must compare objects using an
equal or notEqual operator.

So the solution of using 
Expression expKw =
eb.anyOf("kwCollection").get("kwName").in(keywordStringList); 
is the way to go. 

Or I could use the Id values -
Expression expKw = eb.anyOf("kwCollection").get("kwId").in(keywordIdList);

where the keywordIdList is a Collection<Integer>

The query expression that I had working where I encountered the MySql table
join limit was where I was attempting to "AND" the keywords in the join.  I
would like to create a query where the MEDIA_LISTING records that contained
all of the keywords submitted in a list are returned.

My attempt to do that with the ExpressionBuilder was like this -

ExpressionBuilder eb = new ExpressionBuilder(MediaListing.class);
Expression exp = null;
Collection<Keywords> kwList = searchMedia.getKwCollection();
if (kwList != null && kwList.size() > 0) {
    for (Iterator it = kwList.iterator(); it.hasNext();) {
       KwList kw = (KwList) it.next();
       Expression expKw = eb.anyOf("kwCollection").equal(kw);
       if (exp == null) {
          exp = expKw;
       } else {
          exp = exp.and(expKw);
       }
    }
}

The result of a query constructed that way is for each iteration of the
submitted keyword list a new join of the KEYWORDS and KW_MAP table is added. 
But that will eventually hit the MySql 61 table join limit.

Now that I think about it, that makes sense due to the repeated adding of a
new keyword expression during the iteration loop.

That leaves me with a question on how to perform the keyword "AND" search
without exceeding the maximum MySql table joins?

-sonavor








sonavor wrote:
> 
> Yes, sorry about the confusion on the expression.  I only showed the
> keyword part of my expression building code.  There is an overall
> Expression "exp" variable that I am constructing and the expKw is added to
> it.
> 
> Getting the keyword name from the keyword object and checking it against a
> keyword String list sounds like it could work.  I just think it would be
> slower than having the query comparing numeric primary keys.  Currently,
> in my search I have the keywords for the search entry selected from a
> multi-select list.  However, later I will probably be implementing a
> user-entered keyword search section where keywords are typed in so the
> String list compare would be better in that case.
> 
> Removing the prepareForExecution() and setFirstResult(0) didn't affect the
> SQL that gets generated. 
> 
> If this is a bug in the way EclipseLink generates the SQL what is the
> procedure for logging a bug (I've never done that before).
> 
> -sonavor
> 
> 
> 
> James Sutherland wrote:
>> 
>> The SQL generate does not make sense, perhaps log a bug for this.
>> 
>> Your collection seems to be keyword objects, note the strings or ids. 
>> You could convert the collection to the ids, or string and do,
>> 
>> Expression expKw =
>> eb.anyOf("kwCollection").get("keyword").in(keywordStringList);
>> 
>> Also you had,
>> readAllQuery.setSelectionCriteria(exp);
>> 
>> I assume you meant expKw?
>> 
>> Also only set firstResult if you are using pagination, don't set it to 0.
>> readAllQuery.setFirstResult(0);
>> 
>> and don't call,
>> readAllQuery.prepareForExecution();
>> 
>> this is an internal method, and may be causing your issue.
>> 
>> 
>> 
>> sonavor wrote:
>>> 
>>> First of all, I had originally posted a question about using "IN" with a
>>> JPA query where I have two tables joined by a mapping table.  For some
>>> reason that posting no longer works this forum so I will re-state that
>>> basic problem. 
>>> 
>>> Using a table for media called MEDIA_LISTING I have a second table
>>> called KEYWORDS. I didn't want to search for MEDIA_LISTING records using
>>> a text search for keywords that are associated so I created a mapping
>>> table called KW_MAP.
>>> 
>>> The table structures are as follows -
>>> 
>>> MEDIA_LISTING
>>> MEDIA_ID   integer  PK
>>> MEDIA_TITLE  varchar(50)
>>> ...other columns...
>>> 
>>> KEYWORDS
>>> KW_ID  integer  PK
>>> KW_NAME varchar(50)
>>> 
>>> KW_MAP
>>> KW_ID       integer  PK
>>> MEDIA_ID  integer   PK   ... PK comprised of both values
>>> 
>>> In my original posting I was having a problem determining the best way
>>> to have a query where I could find MEDIA_LISTING items by keywords.  I
>>> wanted to search for MEDIA_LISTING records where the MediaListing JPA
>>> entity's keywords Collection was "IN" a submitted Collection of keywords
>>> (named kwCollection in my case).
>>> 
>>> The solution I ended up with was to use EclipseLink's ExpressionBuilder
>>> and join the keyword table in the MediaListing query like this -
>>> 
>>> List<MediaListing> mList = new ArrayList<MediaListing>();
>>> ExpressionBuilder eb = new ExpressionBuilder(MediaListing.class);
>>> 
>>> Expression expKw = eb.anyOf("kwCollection").in(kwList);
>>> 
>>> I then construct the query -
>>> ReadAllQuery readAllQuery = new ReadAllQuery(MediaListing.class);
>>> readAllQuery.setSelectionCriteria(exp);
>>> readAllQuery.setFirstResult(0);
>>> readAllQuery.setIsReadOnly(true);
>>> readAllQuery.prepareForExecution();
>>> 
>>> ...and execute it.
>>> 
>>> That all works fine.  However, after testing it for a while and
>>> increasing my test data size I ran into a problem where I found there is
>>> a limit to the number of keywords that can be used in the query.  
>>> 
>>> This is due to the resulting SQL that the ReadAllQuery generates from
>>> the Expression created by the ExpressionBuilder.
>>> 
>>> A small query for the MediaListing entity where I am searching for two
>>> keywords results in this SQL:
>>> 
>>> SELECT DISTINCT t1.MEDIA_ID AS MEDIA_ID1, t1.MEDIA_TITLE AS MEDIA_TITLE6
>>> FROM KW_MAP t4, KW_MAP t3, KEYWORD t2, MEDIA_LISTING t1, KEYWORD t0 
>>> WHERE (((t0.KW_ID = ?) AND (t2.KW_ID = ?)) AND (((t3.MEDIA_ID =
>>> t1.MEDIA_ID) AND (t0.KW_ID = t3.KW_ID)) 
>>> AND ((t4.MEDIA_ID = t1.MEDIA_ID) AND (t2.KW_ID = t4.KW_ID)))) LIMIT ?, ?
>>> bind => [405, 406, 0, 10]
>>> 
>>> Notice that for each keyword included in the query that two tables are
>>> joined - a KW_MAP join and a KEYWORD join.  There is a limit in MySql of
>>> 61 tables in a join.  When you factor in adding other MEDIA_LISTING
>>> columns to the query that eats up a lot of the query joins that
>>> available.
>>> 
>>> If I create a SQL statement myself to do the query I can do this -
>>> 
>>> SELECT DISTINCT t1.MEDIA_ID AS MEDIA_ID1, t1.MEDIA_TITLE AS MEDIA_TITLE6
>>> FROM KW_MAP t4, MEDIA_LISTING t1 WHERE (t1.`MEDIA_ID` = t4.`MEDIA_ID`)
>>> AND
>>> (t4.`KW_ID` IN (405, 406))
>>> 
>>> I understand that this query works because I am not including any t4
>>> (KW_MAP) columns in the SELECT portion of the SQL.
>>> 
>>> My question is -
>>> Is the solution for this query Join table limit to use a custom, native
>>> MySql query instead of the generated query that EclipseLink creates?
>>> 
>>> Thanks,
>>> Sonavor
>>> 
>>> 
>>> 
>>> 
>> 
>> 
> 
> 

-- 
View this message in context: http://www.nabble.com/EclipseLink-Many-to-Many-query-problems-with-MySql-tp25140830p25166534.html
Sent from the EclipseLink - Users mailing list archive at Nabble.com.



Back to the top