Как следует использовать Where-Queries и DetachedCriteria в Grails?
РЕДАКТИРОВАТЬ1: Я также принимаю ответы, которые помогают мне в повторном использовании DetachedCriteria
без запросов. Там, где запросы являются моими предпочтениями, но если обычные DetachedCriteria менее хлопотны и более зрелые, я готов использовать их вместо этого.
Я уже некоторое время пытаюсь обернуть голову вопросами "где" в Grails. Сначала я был поражен их мощью: я могу создавать запросы, не покидая своего уровня бизнес-логики. Я подумал, что это решение некоторых проблем с производительностью, с которыми я столкнулся, выполняя сложные действия, требующие большого количества транзакционной логики.
Тем не менее, я чувствую, что документация опускает все проблемы, где запросы. Я понятия не имею, как использовать их в моем приложении, хотя я читал о них и много играл с ними. Вот как я хочу их использовать:
В моем приложении есть много подзапросов, которые являются избыточными при использовании HQL или собственного SQL, но я не могу использовать их повторно, потому что не хочу, чтобы они выполнялись все подряд. Я хочу, чтобы они выполнялись в одном запросе к базе данных.
Насколько я понимаю, DetachedCriteria
или где-запросы для меня. Я решил использовать где запросы, так как мне нравится подход и синтаксис гораздо лучше, чем обычные критерии.
Вот проблема: во многих вариациях мои запросы игнорируются! В зависимости от ситуации, я должен запомнить определенный порядок или структуру.
Повторное использование объектов DetachedCriteria
Я думаю, что самым логичным, что разработчик попытается сделать, является инкапсуляция часто используемых запросов и доступ к ним при желании. Итак, я помещаю некоторые запросы where в сервисные методы следующим образом:
DetachedCriteria<Customer> allCustomersForProjektCriteria(DetachedCriteria<Customer> criteria, Project project){
criteria.where{projekt == project}
}
Мне нравится, когда запросы возвращают объекты DetachedCriteria.
Теоретически. Может быть, это просто синтаксический сахар, я не знаю. Я пытался сделать что-то подобное, и поведение кажется странным, если не сказать больше.
someService.allCustomersForProjectCriteria(Customer.where{name != null}, Project.get(1))
.where{representatives {type != 'Manager'}}
.list()
Первый, где и запрос, и метод в моем сервисе, работают нормально, а последний - нет. Именно так я узнал, что вам разрешено связывать только те запросы, которые выполняются в рамках одного и того же метода или замыкания. Но это ожидаемое поведение? Аналогичный подход также можно найти в документации Grails в разделе "Составление запроса".
В каком случае я помещаю все свои запросы where в один метод? И как это может быть DetachedCriteria
предлагает метод DetachedCriteria where(Closure)
если это игнорируется в некоторых случаях молча? Почему не исключение?
Повторное использование там, где закрытие запросов
Мой следующий подход также упоминается в той же части документации Grails, что упоминался выше. Вместо того, чтобы связывать воедино запросы, которые инкапсулированы различными методами, я попытался инкапсулировать только замыкания, чтобы повторно использовать их по своему желанию. Я попробовал следующее:
Closure allCustomersForProjectCriteria(Project proj){
({project == proj} as DetachedCriteria<Customer>)
}
Тогда используйте это так:
Customer.where(allCustomersForProjectCriteria(Projekt.get(1))
.where{name != null && representatives {type != 'Manager'}}
.list()
Теперь это не работает со следующей Stacktrace:
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Error casting closure to grails.gorm.DetachedCriteria, Reason: null
at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1276)
at ConsoleScript148$_run_closure2.doCall(ConsoleScript148:9)
at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1276)
at ConsoleScript148.run(ConsoleScript148:12)
at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1276)
Я немного поиграл и обнаружил, что не могу объявить замыкание, привести его и вернуть в одной строке кода. Мне пришлось переписать мой метод следующим образом:
Closure allCustomersForProjectCriteria(Project proj){
def criteria = {project == proj} as DetachedCriteria<Customer>
criteria
}
Это небольшое раздражение, но я мог бы легко с этим жить. Я думал, что понял все это сейчас, потому что не было никаких исключений. Но, опять же, только где запрос из моего метода был выполнен, другой был проигнорирован. Это был момент, когда мое замешательство достигло своего апогея. Что происходит? Это не чувствует себя последовательным. Когда я могу доверять моим критериям, если они игнорируются иногда без исключения?
Как ни странно, если я поставлю свой второй, где запрос в закрытии, он снова работает!!
def additionalCriteria = {name != null && representatives {type != 'Manager'}} as DetachedCriteria<Customer>
Customer.where(allCustomersForProjectCriteria(Projekt.get(1))
.where(additionalCriteria)
.list()
Теперь, чтобы добраться до сути...
Вот как я пытаюсь разобраться в этом: я могу объединить несколько запросов, с недавно определенным замыканием друг за другом, если и только если они находятся в одном и том же методе или замыкании.
def results = Customer.where{...}
.where{...}
.list()
Если они в разных методах, все, кроме первого, где запрос игнорируется:
def getQuery(){
Customer.where{...}
}
query.where{ ... } //This one is ignored
Первый был возвращен из метода или замыкания, поэтому он больше не является "нормальным", где запрос, а "возвращен, где запрос". Определение замыкания при вызове метода теперь не работает, и я должен использовать его следующим образом:
def getQuery(){Customer.where{...}}
def myWhereClosure = { ... }
query.where(myWhereClosure) //This one is NOT ignored anymore!
Если я инкапсулирую замыкания для своих запросов, это тоже самое; как только я выполняю запрос "где запрос" с инкапсулированным "где закрытие" в качестве параметра, он становится "возвращенным запросом" и больше не может использоваться регулярно:
def getCustomerCrit(){
def clos = {...} as DetachedCriteria<Customer>
clos
}
def results = Customer.where(customerCrit)
.where{...} //This one is ignored
.list()
Итак, чтобы заставить его работать, я использую предопределенное закрытие для второго запроса где, так что это также "возвращаемый запрос" и не игнорируется:
...
def additionalCrit = {...}
def results = Customer.where(customerCrit)
.where(additionalCrit) //This one is NOT ignored anymore
.list()
Так, как предполагается использовать эти запросы, как они работают под капотом и где я могу найти больше ресурсов по этой теме, чем в документации?