Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2019 Yak.Works - Licensed under the Apache License, Version 2.0 (the "License")
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/
package gorm.tools.mango
import javax.persistence.criteria.JoinType
import groovy.transform.CompileDynamic
import groovy.util.logging.Slf4j
import org.grails.datastore.gorm.GormEnhancer
import org.grails.datastore.gorm.GormStaticApi
import org.grails.datastore.gorm.finders.DynamicFinder
import org.grails.datastore.gorm.finders.FinderMethod
import org.grails.datastore.gorm.query.criteria.AbstractDetachedCriteria
import org.grails.datastore.gorm.query.criteria.DetachedAssociationCriteria
import org.grails.datastore.mapping.core.Session
import org.grails.datastore.mapping.model.PersistentProperty
import org.grails.datastore.mapping.model.types.Association
import org.grails.datastore.mapping.query.Query
import org.grails.datastore.mapping.query.api.Criteria
import org.grails.datastore.mapping.query.api.QueryArgumentsAware
import org.grails.datastore.mapping.query.api.QueryableCriteria
import org.grails.orm.hibernate.AbstractHibernateSession
import org.hibernate.QueryException
import gorm.tools.beans.Pager
import gorm.tools.mango.api.QueryArgs
import gorm.tools.mango.hibernate.HibernateMangoQuery
import gorm.tools.mango.jpql.JpqlQueryBuilder
import gorm.tools.mango.jpql.JpqlQueryInfo
import gorm.tools.mango.jpql.PagedQuery
import grails.compiler.GrailsCompileStatic
import grails.gorm.DetachedCriteria
import grails.gorm.PagedResultList
import yakworks.api.problem.ThrowableProblem
import yakworks.api.problem.data.DataProblem
import yakworks.commons.lang.NameUtils
/**
* This is here to make it easier to build criteria with domain bean paths
* allows
* order('invoice.customer.name')
*
* instead of
* invoice {
* customer {
* order(name)
* }
* }
*
* simliar with eq, like and in
*
* ilike('invoice.customer.name', 'foo')
*/
@Slf4j
@SuppressWarnings(['MethodCount', 'ClassSize']) //ok for this
@GrailsCompileStatic
class MangoDetachedCriteria extends DetachedCriteria {
/** reference to QueryArgs used to build this if it exists */
QueryArgs queryArgs
/** the root map to apply. Wont neccesarily be same as whats in the QueryArgs as it will have been run through the tidy operation*/
Map criteriaMap
/** the root criteriaClosure to apply */
Closure criteriaClosure
Map propertyAliases = [:]
/**
* auto system created aliases, The aliases we created to make them unique and usable in filters.
* for example a projection:['amount':'sum'] will get an alias of 'amount_sum',
* we remove that _sum suffix automatically because its set in the systemAliases.
* but if we did projection:['amount as amount_totals':'sum'] then that is user specified. we dont want to remove the _totals suffix.
*/
List systemAliases = [] as List
/**
* Query timeout in seconds. If value is set, the timeout would be set on hibernate criteria instance.
*/
Integer timeout = 0
/**
* Constructs a DetachedCriteria instance target the given class and alias for the name
* The default is to use the short domain name with "_" appended to it.
* @param targetClass The target class
* @param alias The root alias to be used in queries
*/
MangoDetachedCriteria(Class targetClass, String zalias = null) {
super(targetClass, zalias)
if(!zalias) this.@alias = "${NameUtils.getPropertyName(targetClass.simpleName)}_"
}
@Override
protected MangoDetachedCriteria newInstance() {
new MangoDetachedCriteria(targetClass, alias)
}
//make junctions accesible
List getJunctions(){
super.@junctions
}
//make target class accesible
Class getEntityClass(){
super.@targetClass
}
/**
* Method missing handler that deals with the invocation of dynamic finders
*
* See comments on why we override and replace this.
* It was firing an extra count query because of a truthy check
*
* @param methodName The method name
* @param args The arguments
* @return The result of the method call
*/
@CompileDynamic
@Override
def methodMissing(String methodName, Object args) {
initialiseIfNecessary(targetClass)
def method = dynamicFinders.find { FinderMethod f -> f.isMethodMatch(methodName) }
if (method != null) {
applyLazyCriteria()
return method.invoke(targetClass, methodName, this, args)
}
if (!args) {
throw new MissingMethodException(methodName, AbstractDetachedCriteria, args)
}
final prop = persistentEntity.getPropertyByName(methodName)
if (!(prop instanceof Association)) {
throw new MissingMethodException(methodName, AbstractDetachedCriteria, args)
}
def zalias = args[0] instanceof CharSequence ? args[0].toString() : null
def existing = associationCriteriaMap[methodName]
// NOTE: ONLY CHANGE HERE
// "!alias && existing" -> !alias && existing != null
// since DetachedAssociationCriteria inherits from DetachedCriteria and it implements asBoolean
// then the truthy check on "existing" is running the count query
//alias = !alias && existing ? existing.alias : alias
zalias = !zalias && existing != null ? existing.alias : zalias
DetachedAssociationCriteria associationCriteria = zalias ? new MangoDetachedAssociationCriteria(prop.associatedEntity.javaClass, prop, zalias)
: new MangoDetachedAssociationCriteria(prop.associatedEntity.javaClass, prop)
associationCriteriaMap[methodName] = associationCriteria
add associationCriteria
def lastArg = args[-1]
if(lastArg instanceof Closure) {
Closure callable = lastArg
callable.resolveStrategy = Closure.DELEGATE_FIRST
Closure parentCallable = callable
while(parentCallable.delegate instanceof Closure) {
parentCallable = (Closure)parentCallable.delegate
}
def previous = parentCallable.delegate
try {
parentCallable.delegate = associationCriteria
callable.call()
} finally {
parentCallable.delegate = previous
}
}
}
/**
* If the underlying datastore supports aliases, then an alias is created for the given association
*
* @param associationPath The name of the association
* @param alias The alias
* @return This create
*/
@SuppressWarnings('InvertedIfElse') //not our code so dont want to change it
@Override //Overriden copy paste in just do we can do instance of this instead
Criteria createAlias(String associationPath, String zalias) {
initialiseIfNecessary(targetClass)
PersistentProperty prop
if(associationPath.contains('.')) {
def tokens = associationPath.split(/\./)
def entity = this.persistentEntity
for(t in tokens) {
prop = entity.getPropertyByName(t)
if (!(prop instanceof Association)) {
throw new IllegalArgumentException("Argument [$associationPath] is not an association")
}
else {
entity = ((Association)prop).associatedEntity
}
}
}
else {
prop = persistentEntity.getPropertyByName(associationPath)
}
if (!(prop instanceof Association)) {
throw new IllegalArgumentException("Argument [$associationPath] is not an association")
}
Association a = (Association)prop
DetachedAssociationCriteria associationCriteria = associationCriteriaMap[associationPath]
if(associationCriteria == null) {
associationCriteria = new MangoDetachedAssociationCriteria(a.associatedEntity.javaClass, a, associationPath, zalias)
associationCriteriaMap[associationPath] = associationCriteria
add associationCriteria
}
else {
associationCriteria.setAlias(zalias)
}
return associationCriteria
}
/**
* Returns a single result matching the criterion contained within this DetachedCriteria instance
*
* @return A single entity
*/
@Override
T get(Map args = Collections.emptyMap(), @DelegatesTo(DetachedCriteria) Closure additionalCriteria = null) {
(T)withQueryInstance(args, additionalCriteria) { Query query ->
query.singleResult()
}
}
/**
* Lists all records matching the criterion contained within this DetachedCriteria instance
*
* @return A list of matching instances
*/
@Override
List list(Map args = Collections.emptyMap(), @DelegatesTo(DetachedCriteria) Closure additionalCriteria = null) {
try {
(List)withQueryInstance(args, additionalCriteria) { Query query ->
if(log.debugEnabled){
log.debug("Query criteria: ${query.criteria}")
}
if (args?.max) {
return new PagedResultList(query)
}
return query.list()
}
} catch (IllegalArgumentException | QueryException ex) {
//Hibernate throws IllegalArgumentException when Antlr fails to parse query
//and throws QueryException when hibernate fails to execute query
throw toDataProblem(ex)
}
}
/**
* Calls paged list
*/
List pagedList(Pager pager) {
List resList
Map args = [max: pager.max, offset: pager.offset]
if(this.projections){
resList = this.mapList(args)
} else {
//return standard list
resList = this.list(args)
}
return resList as List
}
List pagedList() {
Pager pager = queryArgs?.pager ? queryArgs.pager : Pager.of([:])
return pagedList(pager)
}
/**
* Lists all records matching the criterion contained within this DetachedCriteria instance
* Uses the JpqlQueryBuilder to build jpql with map projections.
* Forces the results to be in a map even if its only 1 column like a count.
*
* @return A list of matching instances
*/
List