-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: Reusable Criteria instance - is it possible?
PostPosted: Wed Feb 16, 2011 8:39 am 
Newbie

Joined: Tue Dec 21, 2010 9:43 am
Posts: 8
I have a method that creates a criteria from a session
Code:
Session session = this.sessionFactory.openSession();
        Criteria criteria = session.createCriteria(DsEvents.class);

adds some restrictions

and returns a POJO which has a Criteria property in it.

Then I want to use this criteria in 3 places:

First, I want to change fetch mode and add sort order and get a list from the criteria.

After that, I want to add a Projection.rowCount to get the total number of rows (on the original criteria instance, without the fetch mode and sort order).

Finally I want to get the list again with a different sor order (and extra Restrictions)

These should be 3 different methods.

When I try to pass my POJO in these methods something really strange happens: the Criteria instance inside the POJO is updated automatically.

For example, in my first method I have:

Code:
Criteria criteria = gridHelper.getCriteria();
criteria.add(Restrictions.gt("updTime", gridHelper.getLatestUpdateTime()));
criteria.addOrder(Order.asc(gridHelper.getSidx()));
return (ArrayList<T>) criteria.list();


gridHelper is the POJO instance.

in my second method, using the common POJO instance,

this line:

Code:
Criteria criteria = gridHelper.getCriteria();


does not get me the original Criteria instance,
but the one with the Restricion and the sort order form the first method!

How does this work? I do not understand how this is happening. I am not updating the criteria instance inside the POJO, why is it updated automatically?

I am probaby doing something completely wrong. Can someone help me on this? How is the correct way of doing this?


Top
 Profile  
 
 Post subject: Re: Reusable Criteria instance - is it possible?
PostPosted: Wed Feb 16, 2011 2:30 pm 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
You have not posted the code for the gridHelper object, but since you say it is a POJO I assume that the getCriteria() is simply returning a reference to the instance that is kept inside it. So there is only one copy and when you modify it it will not help to call getCriteria() again since it is returning a reference to the same object. I think you'll need separate Criteria objects for each of your three queries.


Top
 Profile  
 
 Post subject: Re: Reusable Criteria instance - is it possible?
PostPosted: Wed Feb 16, 2011 3:36 pm 
Newbie

Joined: Tue Dec 21, 2010 9:43 am
Posts: 8
nordborg, thank you for your answer...

Never mind my apparent complete lack of Java knowledge... So does this mean that I will have to query the database three times for the same query? Isn't any way to have a shared Criteria?

If not, I don't understand, how do you construct dynamic WHERE clauses through the Criteria API?

When you write sql code you can have a shared WHERE string, or create the WHERE clause dynamically, depending on the situation (nested ifs for example). You don't need to query the db more than once. But as the cases become more numerous the WHERE String becomes more and more messy. That is why I was trying to replace it with the more elegant Criteria API. So there must be a way to do this. Please let me know.

Thanks again.


Top
 Profile  
 
 Post subject: Re: Reusable Criteria instance - is it possible?
PostPosted: Wed Feb 16, 2011 4:22 pm 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
I must say that I am not completely sure what you mean...

Quote:
So does this mean that I will have to query the database three times for the same query?


Your description in the original post talks about three different queries.

Quote:
how do you construct dynamic WHERE clauses through the Criteria API?


What do you mean with a dynamic WHERE clause?

Quote:
When you write sql code you can have a shared WHERE string


What is a shared WHERE string?


Top
 Profile  
 
 Post subject: Re: Reusable Criteria instance - is it possible?
PostPosted: Thu Feb 17, 2011 3:43 am 
Newbie

Joined: Tue Dec 21, 2010 9:43 am
Posts: 8
Yo are right I am being very vague so let em try to be very specific.

I have a method I call createWhereClause:

Code:
/**
     * Dynamic creation of WHERE clause for filtering by service and/or by job
     */
    private String createWhereClause(JqGridFilters filters, boolean includeWhere, boolean fetch, String jobType) {

        String whereClause = "";
        String filterClause = "";
        ArrayList<JqGridFilterRule> ruleList = new ArrayList<JqGridFilterRule>();

        if (filters != null) {
            ruleList = filters.getRules();
        }

        if (ruleList.size() > 0) {

            for (JqGridFilterRule rule : ruleList) {

                String fieldName = rule.getField();
                String fieldData = rule.getData();
                if (fieldName.equals("service") && fieldData.equals("all")) {
                    filterClause = "(";
                    for (String service : serviceList) {
                        fieldData = service;
                        filterClause += "grid." + fieldName + " = '" + fieldData + "' OR ";
                    }
                    filterClause = filterClause.substring(0, filterClause.length() - 4);
                    filterClause += ") AND ";
                } else if (fieldName.equals("acquired") || fieldName.equals("ended")) {
                    fieldData = GridServiceCommons.convertDateToTimestamp(fieldData);
                    if (fieldName.equals("acquired")) {
                        fieldData = GridServiceCommons.fillTimestamp(fieldData, true);
                        filterClause += "grid.ended >= " + fieldData + "AND ";
                    }
                    if (fieldName.equals("ended")) {
                        fieldData = GridServiceCommons.fillTimestamp(fieldData, false);
                        filterClause += " grid.acquired <= " + fieldData + "AND ";
                    }

                } else {
                    filterClause += "grid." + fieldName + " LIKE '" + fieldData + "%' AND ";
                }
            }

            if (includeWhere) {
                whereClause += " WHERE ";
            }
            if (!filterClause.equals("")) {
                whereClause += filterClause;
                whereClause = whereClause.substring(0, whereClause.length() - 4);
            }

            if (jobType.equals("running")) {
                    whereClause += "AND grid.status < 2 ";
                } else if (jobType.equals("attention")) {
                    whereClause += "AND grid.rc != 0 ";
                }

        } //if there are no filters
        else {
            if (includeWhere) {
                whereClause += " WHERE ";
                if (jobType.equals("running")) {
                    whereClause += " grid.status < 2 AND";
                } else if (jobType.equals("attention")) {
                    whereClause += " grid.rc != 0 AND";
                }
                whereClause += "(";
                for (String service : serviceList) {
                    String data = service;
                    whereClause += "grid.service = '" + data + "' OR ";
                }
                whereClause = whereClause.substring(0, whereClause.length() - 4);
                whereClause += ")";
            }
        }
        return whereClause;
    }


It is a compelte mess, I know, and it definitely needs refactoring. But this way I can have queries like:

Code:
Query query = sessionFactory.getCurrentSession().createQuery("SELECT COUNT(*) FROM " + modelName + " grid" + whereClause);


or

Code:
return (ArrayList<T>) this.sessionFactory.getCurrentSession().createQuery("from " + modelName + " grid " + fetchLiteral + " " + whereClause
                + " order by grid." + sidx + " " + sord).setFirstResult(start).setMaxResults(rows).list();


The 'whereClause' string is returned by the createWhereClause method. So these two queries can be used by many services, for many tables by having a dynamic WHERE clause created accordingly. (The createWhereClause method is different for every table of course).

What I wanted was, instead of refactoring, to replace this with the Criteria API and have one general crietria object and pass it around and augment it with Restrictions for the specifics of each query.

So now I have a createWhereClauseCriteria method that uses the criteria API to create the generic criteria. And when I tried to pass it around, of course, the criteria is altered permantently.

You say I should create the criteria every time from the start. So for every query I would have to call the generic createWhereClauseCriteria method, which has an overhead. I was wondering how is the best way to do this in Hibernate with the Criteria API, if there is such a way.

Thanks again.


Top
 Profile  
 
 Post subject: Re: Reusable Criteria instance - is it possible?
PostPosted: Thu Feb 17, 2011 4:05 am 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
Ok, now I understand what you are trying to do. But as you say, changes made to a criteria can't be undone later so you need to start with a new criteria for the different queries. I am not sure that the criteria api is the best for this kind of thing. It is maybe better to try HQL queries instead which are much more similar to SQL.

By the way... I don't know how you get the data you are using to build the where clause as in:

Code:
filterClause += "grid." + fieldName + " = '" + fieldData + "' OR ";


If this is user input you really need to make sure that nothing bad happens in case the user enters "bad" values. The keyword here is "SQL injection".


Top
 Profile  
 
 Post subject: Re: Reusable Criteria instance - is it possible?
PostPosted: Thu Feb 17, 2011 4:52 am 
Newbie

Joined: Tue Dec 21, 2010 9:43 am
Posts: 8
Thank you nordborg.

That is what I meant, I am using HQL. How will you avoid this mess with HQL? Even if it is less messy, you are going to have hard coded field names for example, so still Criteria API will be more elegant.

THe SQL Injection issue is not really important. The authentication mechanism uses PreparedStatment. After that the user is not considered malicious(intranet application, few known users). Butnyway, how would you avoid the string concantenation in this situation?


Top
 Profile  
 
 Post subject: Re: Reusable Criteria instance - is it possible?
PostPosted: Fri Feb 18, 2011 3:21 am 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
Quote:
That is what I meant, I am using HQL. How will you avoid this mess with HQL?


You wrote SQL... so I assumed that it was what you used before trying the criteria api. It doesn't have to be messy. We are doing something which I believe is very similar in our application and are using HQL with parameters. I haven't tried out the criteria api so I don't know if it is easier or not. In any case, you'll have to create a new criteria for each query.


Top
 Profile  
 
 Post subject: Re: Reusable Criteria instance - is it possible?
PostPosted: Tue Apr 19, 2011 2:00 pm 
Newbie

Joined: Tue Apr 19, 2011 1:56 pm
Posts: 1
hi otinanism

I stumbled upon you post while preparing myself to do something very similar, I would love to know if you succeeded getting the rules-to-SQL working, and whether you had any other issues and pointers from your experience with it


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.