Search code examples
hibernatehibernate-criteria

hibernate criteria query - property of sub-property


I am using spring MVC 3.0 with the jqgrid plugin. I am building the search feature for the jqgrid which sends a json object accross to the server side. I created a dummy java class to parse the json for the jqgrid whenever the search is triggered. All is well up to this point.

I am dynamically creating my criteria query as the user is free to choose the search criteria (equal, no equal,... etc). Here is an example of the json string sent by the jqgrid.

{
    "groupOp": "AND",
    "rules": [{
        "field": "company",
        "op": "cn",
        "data": "School"},
    {
        "field": "numberOfStudents",
        "op": "eq",
        "data": "2"}]
}​

This is the Java class used as a template to parse this JSON:

public class JsonJqgridSearchModel {

    public String groupOp;

    public ArrayList<JqgridSearchCriteria> rules;
}

Notice the type called JqgridSearchCriteria. This is a class which simply returns one restriction any time I call its getRestriction() method. Here is the JqgridSearchCriteria class:

public class JqgridSearchCriteria {

    public String field;

    public String op;

    public String data;

    public SimpleExpression getRestriction(){
        if(op.equals("cn")){
            return Restrictions.like(field, data, MatchMode.ANYWHERE);
        }else if(op.equals("eq")){
            return Restrictions.eq(field, data);
        }else if(op.equals("ne")){
            return Restrictions.ne(field, data);
        }else if(op.equals("lt")){
            return Restrictions.lt(field, data);
        }else if(op.equals("le")){
            return Restrictions.le(field, data);
        }else if(op.equals("gt")){
            return Restrictions.gt(field, data);
        }else if(op.equals("ge")){
            return Restrictions.ge(field, data);
        }else{
            return null;
        }       
    }   
}

If you observe the JSON string, you will see why the field and data are used to return the SimpleExpression via the getRestriction().

Here is the thing: I have Object-A which has Object-B as a reference. What I am getting from the grid is Object-B.getName() thus there is a JqgridSearchCriteria where the field=Object-B.getName() and the data is the name supplied by the user. When this runs I get an exception as follows:

Internal Error
Sorry, we encountered an internal error.
Details
could not get a field value by reflection getter of tt.edu.sbcs.model.Organization.id
org.hibernate.property.DirectPropertyAccessor$DirectGetter.get(DirectPropertyAccessor.java:62)
org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:230)
org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:3852)
org.hibernate.persister.entity.AbstractEntityPersister.isTransient(AbstractEntityPersister.java:3560)
org.hibernate.engine.ForeignKeys.isTransient(ForeignKeys.java:204)
org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:243)
org.hibernate.type.EntityType.getIdentifier(EntityType.java:449)
org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:142)
org.hibernate.loader.Loader.bindPositionalParameters(Loader.java:1789)
org.hibernate.loader.Loader.bindParameterValues(Loader.java:1760)
                               .
                               .
                               .**

Here is a snippet of where it all comes together.

Criteria criteria = session.createCriteria(CorporateRegistration.class); 
Iterator<JqgridSearchCriteria> iterator = jsonJqgridSearchModel.rules.iterator();
String operation = jsonJqgridSearchModel.groupOp;
if(operation.equals("AND")){
    Conjunction conjunction = Restrictions.conjunction();
    while(iterator.hasNext()){  
        conjunction.add(iterator.next().getRestriction());
    }           
    criteria.add(conjunction);
}//conjunctions are for AND operation
else{
    Disjunction disjunction = Restrictions.disjunction();
    while(iterator.hasNext()){  
        disjunction.add(iterator.next().getRestriction());
    }           
    criteria.add(disjunction);          
}//disjunctions are for OR operations       
    for(Object o: criteria.list()){
        corpRegList.add((CorporateRegistration)o);
    }

I also tried searching for numbers, but the value coming across is a string. Do I use the createAlias of the criteria? Can I specify the datatype of the property called data when I return the SimpleExpression?


Solution

  • For those who may be looking for the answer, I used a sub criteria. That is if you have a criteria called A, for these types of situations you can say A.createCriteria(field).add(conjunction); where field refers to the actual property in the owning entity. Here is something I created to allow for a more generic search logic that is project specific. So when you call A.list() you are good to go. Its a bit long but quite simple to understand.

    public class JqgridSearchCriteria {
    
        public String field;
    
        public String op;
    
        public String data;
    
        public String dataType;
    
        public String dataProperty;
    
        public SimpleExpression getSimpleExpression(String myField, Object o){
            if(op.equals("cn")){
                return Restrictions.like(myField, o.toString(), MatchMode.ANYWHERE);
            }else if(op.equals("eq")){
                return Restrictions.eq(myField, o);
            }else if(op.equals("ne")){
                return Restrictions.ne(myField, o);
            }else if(op.equals("lt")){
                return Restrictions.lt(myField, o);
            }else if(op.equals("le")){
                return Restrictions.le(myField, o);
            }else if(op.equals("gt")){
                return Restrictions.gt(myField, o);
            }else if(op.equals("ge")){
                return Restrictions.ge(myField, o);
            }else{
                return null;
            }       
        }   
    
        public void addMyRestriction(String groupOperation, Criteria criteria){
            Conjunction conjunction = Restrictions.conjunction();
            Disjunction disjunction = Restrictions.disjunction();
            if(groupOperation.equals("AND")){
                if(dataType.isEmpty()){
                    conjunction.add(this.getSimpleExpression(field, data));
                    criteria.add(conjunction);
                }else{
                    if(dataType.equals("Calendar")){
                        try{
                            DateFormat formatter = new SimpleDateFormat("MMMM dd, yyyy");
                            Date date = (Date)formatter.parse(data);
                            Calendar cal = Calendar.getInstance();
                            cal.setTime(date);
                            conjunction.add(this.getSimpleExpression(field, cal));
                            criteria.add(conjunction);
                        }catch (ParseException e){
                            System.out.println("Exception :"+e);
                        }                   //used for calendar data types 
                    }else if(dataType.equals("Long")){
                        Long myLong = Long.parseLong(data);
                        conjunction.add(this.getSimpleExpression(field, myLong));
                        criteria.add(conjunction);
                                            //used for Long data types
                    }else if(dataType.equals("Integer")){
                        Integer myInt = Integer.parseInt(data);
                        conjunction.add(this.getSimpleExpression(field, myInt));
                        criteria.add(conjunction);
                                            //used for Integer data types
                    }else{
                        conjunction.add(this.getSimpleExpression(dataProperty, data));
                        criteria.createCriteria(field).add(conjunction);
                    }                       //used for custom or project specific data types
                }// AND operation used conjunctions
            }else{
                if(dataType.isEmpty()){
                    disjunction.add(this.getSimpleExpression(field, data));
                    criteria.add(disjunction);
                }else{
    
                    if(dataType.equals("Calendar")){
                        try{
                            DateFormat formatter = new SimpleDateFormat("MMMM dd, yyyy");
                            Date date = (Date)formatter.parse(data);
                            Calendar cal = Calendar.getInstance();
                            cal.setTime(date);
                            disjunction.add(this.getSimpleExpression(field, cal));
                            criteria.add(disjunction);
                        }catch (ParseException e){
                            System.out.println("Exception :"+e);
                        } 
                    }else if(dataType.equals("Long")){
                        Long myLong = Long.parseLong(data);
                        disjunction.add(this.getSimpleExpression(field, myLong));
                        criteria.add(disjunction);
                    }else if(dataType.equals("Integer")){
                        Integer myInt = Integer.parseInt(data);
                        disjunction.add(this.getSimpleExpression(field, myInt));
                        criteria.add(disjunction);
                    }else{
                        disjunction.add(this.getSimpleExpression(dataProperty, data));
                        criteria.createCriteria(field).add(disjunction);
                    }
                }           
            }// OR operation used disjunctions
        }
    }