I'm using Grails 3.3 and GORM CriteriaBuilder for most of my querying these days but I am stuck on how to call functions on properties - for example, calling the hour() function on a Date property. If I were using where DetachedCriteria I could have something like this:
def whereQuery = Student.where {
hour(registration) = 15
}
to find all students who registered between 15:00 and 15:59.
However, if I use a CriteriaBuilder instead, I cannot do this:
def c = Student.createCriteria()
def results = c.list {
eq 'hour(registration)', 15
}
Is there some way to accomplish this in the builder DSL? I know I can go back to the domain class and define a mapping that extracts the hour from the date field but that seems kind of clumsy.
Give the sqlRestriction a try:
def results = Student.withCriteria {
sqlRestriction 'hour(registration) = 15'
}
See 7.5.6. Using SQL Restrictions in http://gorm.grails.org/latest/hibernate/manual/index.html#criteria
Related
In Grails Service, I have to delete records from the Db, but I get below error :
spi.SqlExceptionHelper, Connection is read-only. Queries leading to data modification are not allowed.
Although, there is #Transactional(readOnly = false) in my service, here is the delete part in my service code:
def adsDurationIs7 = null
adsDurationIs7 = Ads.findAllByDuration("7 days", [sort: "dateCreated", order: "asc"])
adsDurationIs7.each {
Ads.get(it.id).delete(flush: true)
}
I'm not sure why that's not working, but even if it did you're doing the work about as expensively as you possibly can. You load all of the instances into memory (including all non-lazy properties and collections), then for each one you get its id and use that to load the instance again with a get call (although if you're lucky and/or you have caching configured correctly this might be a no-op), and then use that to delete each database record one at a time. And you're ordering in the database, which adds processing time but is completely unnecessary since you're deleting everything that the query returns.
What you really want is GORM code that ends up running SQL similar to
delete from ads where duration=?
where the PreparedStatement sets the ? parameter value to "7 days".
This "where" query will do exactly that:
Ads.where { duration == '7 days' }.deleteAll()
as will this HQL update:
Ads.executeUpdate 'delete Ads a where a.duration = :duration',
[duration: '7 days']
So your service should look like
import grails.transaction.Transactional
#Transactional
class MyService {
void deleteAdsDurationIs7() {
Ads.where { duration == '7 days' }.deleteAll()
}
}
or
import grails.transaction.Transactional
#Transactional
class MyService {
void deleteAdsDurationIs7() {
Ads.executeUpdate 'delete Ads a where a.duration = :duration',
[duration: '7 days']
}
}
You are executing this service function from controller's function, which is not transactional. Add #Transactional to controller's function.
Here is an example:
https://stackoverflow.com/a/21998182/2166188
I am getting this error: Cannot get property 'id' on null object and i can't understand the problem.
Here is my code in provionController.groovy
CreateCriteria returns one element, I verified in the database, size = 1 but when i tried to display the Id, I get this error.
def prov_model = null
def model = Provision_model.CreateCriteria{
gilt_air{
eq("air",air)
}
gilt_coo{
eq("coo",coo)
}
le("date_from", per.begin)
ge("date_to", per.end)
eq("active", 1)
}
println(model.size())
prov_model = model[0]
println(prov_model.id)
but when I am getting it directly by method get(), it hasn't no problem
prov_model = Provision_model.get(57)
println(prov_model.id)
1st: the method is called createCriteria(), not CreateCriteria()
2nd: the method itself DOES NOT invoke any db operation. You have to call list() or get() etc. on it to get the query results
If order to execute the query and store the results in model, replace this
def model = Provision_model.CreateCriteria
with
def model = Provision_model.withCriteria
#injecteer and #Donal both have very valid input. First, you need to address the syntax issue, here is an example of one way to format your criteria:
def prov_model = null
def model = Provision_model.createCriteria().get() {
gilt_air{
eq("air",air)
}
gilt_coo{
eq("coo",coo)
}
le("date_from", per.begin)
ge("date_to", per.end)
eq("active", 1)
}
Keep in mind that by using .get() you are limiting the return from the criteria to one record. Second, if you try writing the criteria both ways, using withCriteria and using the format above and it still doesn't work, your problem may be in the domain model or the configuration of the database.
The simplified domain model:
'Txn' (as in Transaction) hasMany 'TxnStatus'. TxnStatus has a dateTime
This is a legacy mapping so I cant change the DB, the mapping on Txn:
static mapping = {
txnStatus column: 'MessageID', ignoreNotFound: true, fetch: 'join'
}
I need to get Txns based on a number of dynamically built criteria, currently using GORM's 'where' query, it works well; BUT I need to also get only the latest txnStatus.
Tried:
def query = Txn.where {
txnStatus { dateTime == max(dateTime) }
}
gives: java.lang.ClassCastException: org.hibernate.criterion.DetachedCriteria cannot be cast to java.util.Date
also tried:
def query = Txn.where {
txnStatus.dateTime == max(txnStatus.dateTime)
}
which gives:
Compilation Error: ...
Cannot use aggregate function max on expressions "txnStatus.dateTime"
At this stage I am thinking of changing to HQL...any help appreciated!
There was a question a couple of days ago very similar to this. It appears that using where queries with a 'max' subquery doesn't work well with ==
The OP was able to get it to work with < and worked around it that way. Looking at the docs on where queries has not helped me figure this one out.
Here is a really wild guess -
Txn.where {
txnStatus {
dateTime == property(dateTime).of { max(dateTime) }
}
}
I am using an older version of grails (1.1.1) and I am working on a legacy application for a government client.
Here is my question (in psuedo form):
I have a domain that is a Book. It has a sub domain of type Author associated with it (1:many relationship). The Author domain has a firstName and lastName field.
def c = Book.createCriteria()
def booklist = c.listDistinct {
author {
order('lastName', 'asc')
order('firstName', 'asc')
}
}
Let's say I have a list of fields I want to use for an excel export later. This list has both the author domain call and the title of the column I want to use.
Map fields = ['author.lastName' : 'Last Name', 'author.firstName', 'First Name']
How can I dynamically call the following code--
booklist.eachWithIndex(){
key, value ->
println key.fields
}
The intent is that I can create my Map of fields and use a loop to display all data quickly without having to type all of the fields by hand.
Note - The period in the string 'author.lastName' throws an error when trying to output key['author.lastName'] too.
I don't recall the version of Groovy that came with Grails 1.1, but there are a number of language constructs to do things like this. If it's an old version, some things may not be available - so your mileage may vary.
Map keys can be referenced with quotes strings, e.g.
def map = [:]
map."person.name" = "Bob"
The above will have a key of person.name in the map.
Maps can contain anything, including mixed types in Groovy - so you really just need to work around string escapes or other special cases if you are using more complex keys.
You can also use a GString in the above
def map = [:]
def prop = "person.name"
map."${prop}" = "Bob"
You can also get a map of property/value off of a class dynamically by the properties field on it. E.g.:
class Person { String name;String location; }
def bob = new Person(name:'Bob', location:'The City')
def properties = bob.properties
properties.each { println it }
I am using the findAll() SQL-like method:
MyDomainClass.findAll("firstname='George' and lastname='kuo' and username='kjlop'"),
but I have got problem:
when value starts with number(for example,when age='2poj') it throws an exception
I use grails 1.3.2 and gorm-hbase 0.2.4 plugin and in my domain classes fields have String type.
Here is the Stack Trace:
expecting token in range: '0'..'9', found 'p'
at org.grails.hbase.ghql.LexerRules.nextToken(LexerRules.java:125)
at org.grails.hbase.finders.QueryStringTokenizer.tokenize(QueryStringTokenizer.groovy:59)
at org.grails.hbase.finders.TokenizerStrategy$tokenize.call(Unknown Source)
//---------
I wonder is there any way in groovy change findAll() method work ?
If anybody know solution please help.
Thanks in advance.
You should be able to run a dynamic finder method on the domain object to achieve what you need.
Example:
MyDomainClass.findAllByFirstnameAndAge('Dan', 25)
This works for all data types and enums.
You can try like the Grails example:
MyDomainClass.findAll("from DomainTable as b where b.firstname=:firstname and b.age=:age", [firstname:'Dan Brown', age: 25]
Notice: I don't know if you mistype it, but '25' is a string, so that it can't be age='25'
EDIT:
I don't know how this doesn't work, but in case you want to find with multiple properties, you should use createCriteria().
def c = MyDomainClass.createCriteria()
def results = c.list {
like("firstName", "George%")
like("age", "25");
}
EDIT2: Sorry, createCriteria is not supported by hbase plugin. Based on your condition, I think it's suitable to try DynamicFinderFilter (with approriate import).
// all books written by Dan Brown or J K Rowling
DynamicFinderFilter filterList = new FinderFilterList(Operator.OR)
DynamicFinderFilter filter1 = new Filter('author', 'Dan Brown')
filterList.addFilter(filter1)
DynamicFinderFilter filter2 = new Filter('author', 'J K Rowling')
filterList.addFilter(filter12)
results = Book.findAll(filterList)
The complete example can be find in the plugin page.