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

Using the kwName and in sounds like the best way to go.

Your issue in your code was the anyOf represents a new object (and join)
every time it is called.  To have only join once you only call it once and
use a variable.

Collection<Keywords> kwList = searchMedia.getKwCollection();
if (kwList != null && kwList.size() > 0) {
    Expression kwCollection = eb.anyOf("kwCollection"); // ** store variable
    for (Iterator it = kwList.iterator(); it.hasNext();) {
       KwList kw = (KwList) it.next();
       Expression expKw = kwCollection.equal(kw);  // ** use same variable
       if (exp == null) {
          exp = expKw;
       } else {
          exp = exp.or(expKw); // ** use OR
       }
    }
}


sonavor wrote:
> 
> 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
>>>> 
>>>> 
>>>> 
>>>> 
>>> 
>>> 
>> 
>> 
> 
> 


-----
http://wiki.eclipse.org/User:James.sutherland.oracle.com James Sutherland 
http://www.eclipse.org/eclipselink/
 EclipseLink ,  http://www.oracle.com/technology/products/ias/toplink/
TopLink 
Wiki:  http://wiki.eclipse.org/EclipseLink EclipseLink , 
http://wiki.oracle.com/page/TopLink TopLink 
Forums:  http://forums.oracle.com/forums/forum.jspa?forumID=48 TopLink , 
http://www.nabble.com/EclipseLink-f26430.html EclipseLink 
Book:  http://en.wikibooks.org/wiki/Java_Persistence Java Persistence 
-- 
View this message in context: http://www.nabble.com/EclipseLink-Many-to-Many-query-problems-with-MySql-tp25140830p25275996.html
Sent from the EclipseLink - Users mailing list archive at Nabble.com.



Back to the top