Defining OR Condition with grails criteria api - grails

I have the following domain objects:
class User {
String name
Transaction transaction
static constraints = {
transaction nullable: true
}
}
class Transaction {
boolean successful
User user
static belongsTo = User
}
I want to select all users that have no successful transaction. This means I want the users without any transaction (transaction == null) and the users that have a transaction with the successful value false (transaction.successful == false). I want to do this with the Criteria API (because this can be combined with other conditions based on user input).
I tried this:
def c = User.createCriteria()
def results = c {
or {
isNull 'transaction'
transaction {
eq 'successful', false
}
}
}
However this gives me only the users that have a transaction (with the successful value false). But I do not get the users where transaction is null
The following code shows how I created some sample data:
def createUserAndTransaction(String name, Boolean successful = null) {
User u = new User(name: name)
if (successful != null) {
Transaction t = new Transaction(user: u, successful: successful)
u.setTransaction(t)
}
u.save()
}
def init = { servletContext ->
createUserAndTransaction 'john', true
createUserAndTransaction 'mike', false
createUserAndTransaction 'pablo'
}
My criteria query only returns mike in this case. But I want mike and pablo. What am I missing?

So the problem is that it defaults to an inner join. You have to create an alias for the table and define its join type:
createAlias("transaction", "t", CriteriaSpecification.LEFT_JOIN)
or {
isNull("t")
eq("t.successful", false)
}

Related

Grails 2.4.4 removing all items from a hasmany String relationship

I have a User class that hasMany organizations, and organizations are String UIDs.
class User implements Serializable {
...
List organizations
static hasMany = [organizations: String]
...
}
To update an User instance, I need to remove all the Strings from "organizations" before adding what the updated uids, but it doesn't work. The same organizations are resaved into the collection after I delete them and save the User instance.
I tried many ways:
// 1
user.organizations.clear()
// 2
def orgsToDelete = [] // this is to avoid concurrent modification exception
orgsToDelete += user.organizations
orgsToDelete.each { orguid ->
user.removeFromOrganizations(orguid)
}
After any of 1 or 2, the user.organizations is empty, but when I do:
user.save(flush:true)
I get the same organizations that where before the clear/removeFromOrganizations
I can't do user.organizations.each { it.delete() } because the items are not domain classes but Strings.
Another weird thing is I have a custom validator to check if the collection has any items, and it seems it doesn't gets any errors, event the organizations is empty, this is my validator:
organizations validator: { val, obj ->
if (obj.organizations.size() == 0) return false
return true
}
When I do this on the controller update action, it says hasErrors() == false
if (userInstance.hasErrors())
{
println "has errors"
respond userInstance.errors, view:'edit'
return
}
Any ideas?
Here is the controller: https://github.com/ppazos/cabolabs-ehrserver/blob/master/grails-app/controllers/com/cabolabs/security/UserController.groovy#L199-L237
Use your own domain class instead of String as collection's elements, like:
class Organization {
String name
static belongsTo = [ User ]
}
then you should be able to properly remove the children by
user.organisations.clear()
user.save()
The reason for the behaviour you a facing is, that the String instances are mapped to a table which has no back-refs to the user. That leads to the situation, that the records in that string table are not actually removed and are loaded upon the next database call.
I have read your project code and you have the DataSource bad set. You are using a H2 memory BBDD, but using the MySQL driver instead (in the development environment). It doesnt look good. Set it properly, and try again.
I have test your model, and used:
user.organizations.clear()
without any problems, even using your custom validator.
In other hand you should use a Service instead for all the business logic, not a controller. Because a service is transactional by default.
Here are the changes I did, and works:
// List organizations = []
static hasMany = [organizations: String]
static mapping = {
password column: '`password`'
// organizations lazy: false
}
Removing List organizations. Using the H2 datasource with this config at DataSource development environment:
dbCreate = "create-drop"
url = "jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
username = "sa"
password = ""
And this code works like a charm:
def clearOrganizationFromUser(){
//creating user with timestamp
User user = new User(username: 'username' + new Date(), password: 'password', email: 'email#email.es', accountExpired: false, accountLocked: false,
passwordExpired: false)
List<String> organizations = []
10.times{
organizations << "organization $it"
}
organizations.each{ String organization ->
user.addToOrganizations(organization)
}
user.save()
println "Organizations after saving: ${user.organizations}"
//Its not neccesary but I did it for showing that organization are persisted in DDBB
User newUserFromDDBB = User.get(user.id)
//deleting organization
newUserFromDDBB.organizations.clear()
newUserFromDDBB.save()
println "---> Organizations after deleting: ${newUserFromDDBB.organizations}"
}
Hope It works. If do, please mark as solved.

Grails findBy belongsTo gives JdbcSQLException

Say, for a system, I had Student and User objects, like so:
User {
}
Student {
User user //not nullable
static belongsTo = [User]
}
Teacher {
static belongsTo = [user:User]
}
I have a lot of code written against these models, and plenty of dynamic finders in it
def u1 = new User().save( flush: true)
new Student( user: u1 ).save( flush: true)
...
def s = Student.findByUser( springSecurityService.currentUser)
//s can be null
def t = Teacher.findByUser( springSecurityService.currentUser)
//t can be null
if( s != null)
//do something with 's'
if( t != null)
//do something with 't'
...
And this all worked fine. But I wanted to update my domain model to add cascading access from User to Student. So I did:
User {
Student student //nullable
}
And I would save a user like this:
new User( student: new Student() ).save( flush: true)
But the problem is, when I try to access 's' like I did above.
If the Student.findByUser can find one such student, then it works.
But if it should return null, it gives an error instead.
def s = Student.findByUser( springSecurityService.currentUser)
will result in:
org.h2.jdbc.JdbcSQLException
Parameter #2 is not set; SQL statement:
select this_.id as id18_0_, this_.version as version18_0_, from student this_ where this_.id=? limit ? [90012-173]
I know I can access the student object through the user object like user.student, but I would like to be able to keep the existing code as it is.
I've seen this thread, and it had a similar problem, but no resolution grails email list
Is this a bug? What can I do to get around it? Am I not setting up my domain models properly?
Thanks
As an alternative, you could try something like this using hasOne instead of belongsTo.
class User {
static hasOne = [student:Student, teacher:Teacher]
static constraints = {
student unique:true, nullable: true
teacher unique:true, nullable: true
}
}
class Student {
User user
}
class Teacher {
User user
}
It still cascades and appears to handle null searches fine.

Grails Web flow with Multiple domain

I am new to Grails development. I started doing the web flow for the user registration. It has two forms that in first form i get the basic details and get the bank details in second one. And both of them going to save in different domains.
class Customer {
String Name
Integer Age
Date DateOfBirth
String FatherOrHusbandName
String IdProof
String IdProofNumber
Boolean IsDeleted
static hasOne = [bankdetail:BankDetails]
static hasMany = [property:Property]
static constraints = {
}}
Bank details
class BankDetails {
String bank_name
String account_number
String account_holder_name
String branch_name
String IFSC_code
String account_type
Customer customer
static constraints = {
customer unique:true
}
static mapping = {
customer insertable: false
customer updateable: false
}}
These two domain classes are used for the customer. I got the nice tutorial for web flow implementation from http://ridingthetiger.wikia.com/wiki/Creating_the_Example_Grails_WebFlow_Project. But in this tutorial only one domain is used. I want the grails webflow example with two or more domain classes. Please suggest if a have any or give one simple example....
Thanks in advance
Basically, what I do is create two command objects and pass them around through next and previous. One for each step. But in this case the domain objects are simple you can just pass them around in the flow object. e.g
def createCustomerFlow = {
basicInfo{
on('next'){Customer cust ->
flow.customer = cust
if(!cust.validate()){
flow.errorbean = cust
return error()
}else{
return success()
}
}.to 'bankInfo'
on("cancel").to 'cancel'
}
bankInfo{
on('finish'){BankDetails bank ->
flow.bank = bank
if(!bank.validate()){
flow.errorbean = bank
return error()
}else{
try{
//create customer. Implement this method yourself
def cust = createCustomer(flow.customer, flow.bank)
flow.customer = cust
}catch(ValidationException e){
log.error("Validation exception", e)
flow.errorbean = e
return error()
}
return success()
}
}.to 'summary'
on("previous"){BankDetails bank ->
flow.bank = bank
}.to 'basicInfo'
on("cancel").to 'cancel'
}
}

How to convert my query to Criteria in grails

class FaciltyUserRole{
User user
Role permission
Facility facility
}
class Facility{
String id
String groupName
}
These are my domains. I would like to make a query and fetch the facilities with a given user, permission, and the groupName. How can I do that? I have already started my criteria with this, I just have no idea how to filter the facilities I get based on the groupName.
def query = {
and{
eq("user", user)
eq("role", permission)
}
projections {
property("facility")
}
}
def facList = FacilityUserRole.createCriteria().list(query)
if my understanding is correct this is what you are looking for
def query = {
and{
eq("user", user)
eq("role", permission)
facility{
eg("groupName", groupName)
}
}
}

Grails querying the database

I am trying to query a database within grails using:
def per = User.get(springSecurityService.principal.id)
def query = Post.whereAny {
author { username == per.username }
}.order 'dateCreated', 'desc'
messages = query.list(max: 10)
My User is in a User domain and in the Post domain I have:
String message
User author
Date dateCreated
when I run this query its empty every time my method in the controller for populating the database is:
def updateStatus() {
def status = new Post(message: params.message)
System.out.println("test " + params.message)
status.author = User.get(springSecurityService.principal.id)
status.save()
}
I am very new at quering databases in grails so if anyone could recommend some reading this would be good too.
Thanks
New version of spring-security-core plugin add methods to you controller, thus you can replace:
def per = User.get(springSecurityService.principal.id)
def query = Post.whereAny {
author { username == per.username }
}.order 'dateCreated', 'desc'
messages = query.list(max: 10)
with
messages = Post.findAllByAuthor(principal, [sort: 'dateCreated', order 'desc', max: 10])
Without declaring springSecurityService
You can replace the updateStatus action with:
def updateStatus(){
def status = new Post(message: params.message, author: principal)
status.save()
}
Empty query may be resolved with:
def updateStatus() {
def status = new Post(message: params.message)
System.out.println("test " + params.message)
status.author = User.get(springSecurityService.principal.id)
status.save(flush: true)
}
This may help you find the data later as the save() just gives your hibernate context the instruction to persist the data, but does not force hibernate to do it at that moment. passing flush:true will force data to be persisted immediately. Also status.save(failOnError:true) will reveal if your domain object properties have validation issues preventing the domain instance from being saved at all to the database (by throwing an Exception). You can use both if desired:
def updateStatus() {
def status = new Post(message: params.message)
System.out.println("test " + params.message)
status.author = User.get(springSecurityService.principal.id)
status.save(flush: true, failOnError: true)
}
hope that helps.
You can set flush and failOnError: true globally as well in grails-app/conf/Config.groovy:
grails.gorm.failOnError=true
grails.gorm.autoFlush=true

Resources