Showing posts with label createCriteria. Show all posts
Showing posts with label createCriteria. Show all posts

Thursday, October 12, 2017

Grails Create Criteria | Grails CreateCriteria | Custom Criteria | Custom Criteria Condition

Grails Create Criteria | Grails CreateCriteria | Custom Criteria | Custom Criteria Condition

You have to create a groovy class as below:


import org.hibernate.Criteria
import org.hibernate.HibernateException
import org.hibernate.criterion.CriteriaQuery
import org.hibernate.criterion.Criterion
import org.hibernate.engine.spi.TypedValue
import org.hibernate.internal.util.StringHelper

/**
 * Created by pritom on 25/09/2017.
 */
class CustomCriteria implements Criterion {
    private static final TypedValue[] NO_VALUES = new TypedValue[0]
    private final String propertyName

    CustomCriteria(String propertyName) {
        this.propertyName = propertyName
    }

    public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
        String[] columns = criteriaQuery.findColumns(this.propertyName, criteria)
        String result = StringHelper.join(" or ", StringHelper.suffix(columns, " is not null"))
        if(columns.length > 1) {
            result = '(' + result + ')'
        }

        return result
    }

    public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
        return NO_VALUES
    }

    public String toString() {
        return this.propertyName + " is not null"
    }
}

Then you to attach those functions as below:


Object.metaClass.with {
    customCriteria = {
        String propertyName = delegate.delegate.calculatePropertyName(it)
        delegate.delegate.addToCriteria(new CustomCriteria(propertyName))
        return delegate.delegate
    }
}

Then you can use your custom criteria function as below:


return (List<Home>) Home.createCriteria().list {
    setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
    createAlias("childs", "childs")
    isNotNull("childs.id")
    customCriteria("childs.id")
    not { isNull("childs.id") }
}

Wednesday, October 9, 2013

The Grails Hibernate Dynamica/On Fly CriteriaBuilder

The HibernateCriteriaBuilder is a great Grails feature, that you should know when working with the Grails framework. It is an alternative way to create dynamic or static queries with a groovy syntax based on the Criteria API of Hibernate.


Although there is some documentation about it in the Grails Pages, this does not demonstrate some advanced features like joins or pagination. There are several blog posts about the HibernateCriteriaBuilder, but most of them only show a simple example. I want to share my experience with a more complex example that demonstrates some features like:
  • query attributes of associated entities using joins
  • pagination parameters and their consequences
  • reuse of the criteria definition for similar domain classes

Query attributes of associated entities using joins

The example code uses a parcel instance as an example to carry parameter values to construct the criteria.

def example = new Parcel(parcelNumber: '1234%');

def crit = Parcel.createCriteria()
def parcels = crit.list {
  ilike("parcelNumber", example.parcelNumber)
  gt("parcelPrice", example.parcelPrice)
  recipient {
    ilike("lastName", example.recipient.lastName)
  }
  depot {
     address {
       ilike("city", example.depot.address.city)
       ilike("postalCode", example.depot.address.postalCode)
     }
  }
}

Dynamic query parameters

We extend the example to include the filtering only when the value of the example object is not empty.
def example = new Parcel(parcelNumber: '1234%');


def crit = Parcel.createCriteria()

def parcels = crit.list {
    if (example.parcelNumber) ilike("parcelNumber", example.parcelNumber)
    if (example.parcelPrice) gt("parcelPrice", example.parcelPrice)
    if (example.recipient) {
        recipient {
            if (example.recipient?.lastName) {
                ilike("lastName", example.recipient.lastName)
            }
        }
    }
    if (example.depot?.address) {
        depot {
            address {
                if (example.depot.address.city) {
                    ilike("city", example.depot.address.city)
                }
                if (example.depot.address.postalCode) {
                    ilike("postalCode", example.depot.address.postalCode)
                }
            }
        }
    }
}

Reuse of the criteria definition

We have 2 domain classes with the same properties: Parcel and ParcelArchive. We want to reuse the criteria definition, but we must be careful, because extracting it with your IDE ("extract method" refactoring) could break your code. You can use the groovy with.{closure} to extract the criteria:


// search Parcel

def example = new Parcel(parcelNumber: '1234%');

def crit = Parcel.createCriteria()
def parcels = crit.list {
    buildSearchCriteria(example, crit)
}

// search ParcelArchive

def example = new ParcelArchive(parcelNumber: '1234%');

def crit = ParcelArchive.createCriteria()
def parcels = crit.list {
    buildSearchCriteria(example, crit)
}

// criteria method for both

private static void buildSearchCriteria(def example, HibernateCriteriaBuilder crit) {
    crit.with {  
    // make the criteriaBuilder the receiver, because the definitions have been extracted a method
        if (example.parcelNumber) {
            ilike("parcelNumber", example.parcelNumber)
            if (example.parcelPrice) {
                gt("parcelPrice", example.parcelPrice)
            }
            if (example.recipient) {
                recipient {
                    if (example.recipient?.lastName) {
                        ilike("lastName", example.recipient.lastName)
                    }
                }
            }
            if (example.depot?.address) {
                depot {
                    address {
                        if (example.depot.address.city) i {
                            like("city", example.depot.address.city)
                        }
                        if (example.depot.address.postalCode) {
                            ilike("postalCode", example.depot.address.postalCode)
                        }
                    }
                }
            }
        }
    }
} 

Check and create criteria dynamically



package com.pritom
 
import grails.orm.HibernateCriteriaBuilder
import org.codehaus.groovy.grails.commons.GrailsApplication

class DataService { 
    /**
     * Now if you want to search schoolName against a Student you 
     * have to something like below:
     */
    def searchStudentBySchoolName() {
        def key = "discpline.school.name";
        def value = "Some Value";
        def criteria = Student.createCriteria();
        criteria.list {
            buildSearchCriteria(criteria, key.trim("."), value);
        }
    }

    /**
     * isDomainClass() ? Somehow check that the name is a domain class
     * Otherwise it is a property in the domain.
     * 
     * Implement getDomainClassByName() to get the domain class dynamically
     */

    def buildSearchCriteria(HibernateCriteriaBuilder criteria, String[] fields, String value) {
        criteria.with {
            for (Integer index2 = 0; index2 < fields.size() - 1; index2++) {
                if(fields[index2] != null && fields[index2 + 1] != null && isDomainClass(fields[index2])
                        && !isDomainClass(fields[index2 + 1])) {
                    def domainClass = getDomainClassByName(fields[index2].getMetaClass().getTheClass());
                    def domainCriteria = domainClass.createCriteria();
                    'in' (fields[index2], domainCriteria.list {
                        like(fields[index2 + 1], "%" + value + "%")
                    });
                } else if(fields[index2] != null && fields[index2 + 1] != null && isDomainClass(fields[index2])
                        && isDomainClass(fields[index2 + 1])) {
                    def domainClass = getDomainClassByName(fields[index2]).getMetaClass().getTheClass();
                    String current2 = fields[index2];
                    fields[index2] = null;
                    def domainCriteria = domainClass.createCriteria();
                    'in' (current2, domainCriteria.list{
                        buildSearchCriteria(domainCriteria, fields, value)
                    });
                }
            }
        }
    }
} 
 
/* The domain classes are below: */
class Student {
    Integer id;
    String studentId;
    Integer age;
     det belongsTo = [
        discipline: Discipline
    ]
} 
 
class Discipline {
    Integer id;
    String disciplineName; 
 
     def belongsTo = [
        school: School
    ]
}

class School {
    Integer id;
    String schoolName;
} 

http://www.viaboxxsystems.de/the-grails-hibernatecriteriabuilder

Monday, April 22, 2013

Grails create criteria on model

def c = Account.createCriteria()
List results = c.list {
    like("name", "%Name Like%")
    and {
        between("amount", 500, 1000)
        eq("address", "London")
    }
    setFirstResult(0)
    setMaxResults(10)
    order("name", "desc")
    order("id", "asc")
    
    projections {
        property("id")
        groupProperty("invoice.id")
    }
}


Below is a node reference for each criterion method:

Node

Description

between
Where the property value is between to distinct values
between("balance", 500, 1000)
eq
Where a property equals a particular value
eq("branch", "London")
eqProperty
Where one property must equal another
eqProperty("lastTransaction","firstTransaction")
gt
Where a property is greater than a particular value
gt("balance",1000)
gtProperty
Where a one property must be greater than another
gtProperty("balance","overdraft")
ge
Where a property is greater than or equal to a particular value
ge("balance",1000)
geProperty
Where a one property must be greater than or equal to another
geProperty("balance","overdraft")
idEq
Where an objects id equals the specified value
idEq(1)
ilike
A case-insensitive 'like' expression
ilike("holderFirstName","Steph%")
in
Where a one property is contained within the specified list of values note: 'in' is a groovy reserve word, we must escape it by quotes.
'in'("holderAge",[18..65])
isEmpty
Where a collection property is empty
isEmpty("transactions")
isNotEmpty
Where a collection property is not empty
isNotEmpty("transactions")
isNull
Where a property is null
isNull("holderGender")
isNotNull
Where a property is not null
isNotNull("holderGender")
lt
Where a property is less than a particular value
lt("balance",1000)
ltProperty
Where a one property must be less than another
ltProperty("balance","overdraft")
le
Where a property is less than or equal to a particular value
le("balance",1000)
leProperty
Where a one property must be less than or equal to another
leProperty("balance","overdraft")
like
Equivalent to SQL like expression
like("holderFirstName","Steph%")
ne
Where a property does not equals a particular value
ne("branch", "London")
neProperty
Where one property does not equal another
neProperty("lastTransaction","firstTransaction")
order
Order the results by a particular property
order("holderLastName", "desc")
sizeEq
Where a collection property's size equals a particular value
sizeEq("transactions", 10)
sizeGt
Where a collection property's size is greater than a particular value
sizeGt("transactions", 10)
sizeGe
Where a collection property's size is greater than or equal to a particular value
sizeGe("transactions", 10)
sizeLt
Where a collection property's size is less than a particular value
sizeLt("transactions", 10)
sizeLe
Where a collection property's size is less than or equal to a particular value
sizeLe("transactions", 10)
sizeNe
Where a collection property's size is not equal to a particular value
sizeNe("transactions", 10)

With dynamic finders, you have access to options such as max, sort, etc. These are available to criteria queries as well, but they have different names:

Name Description
order(String, String)
Specifies both the sort column (the first argument) and the sort order (either 'asc' or 'desc').
order "age", "desc"
firstResult(int)
Specifies the offset for the results. A value of 0 will return all records up to the maximum specified.
firstResult 20
maxResults(int)
Specifies the maximum number of records to return.
maxResults 10
cache(boolean)
Indicates if the query should be cached (if the query cache is enabled).
cache true


Criteria also support the notion of projections. A projection is used to change the nature of the results. For example the following query uses a projection to count the number of distinct branch names that exist for each Account:


Name Description
property
Returns the given property in the returned results
property("firstName")
distinct
Returns results using a single or collection of distinct property names
distinct("fn") or distinct(['fn', 'ln'])
avg
Returns the average value of the given property
avg("age")
count
Returns the count of the given property name
count("branch")
countDistinct
Returns the count of the given property name for distinct rows
countDistinct("branch")
groupProperty
Groups the results by the given property
groupProperty("lastName")
max
Returns the maximum value of the given property
max("age")
min
Returns the minimum value of the given property
min("age")
sum
Returns the sum of the given property
sum("balance")
rowCount
Returns count of the number of rows returned
rowCount()

XXXXXXXXXXXXXXXX