Grails findBy belongsTo gives JdbcSQLException - grails

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.

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 Criteria - list contents

These are my domain objects
User{
hasMany = {roles: UserRole}
}
UserRole{
User user
Role role
}
Role{
String authority
}
I need to find users based on their Role. For that I am trying to use the following criteria:
def role_admin = Role.findByAuthority('ROLE_ADMIN')
def criteria = new DetachedCriteria(User).build {
roles{
role{
idEq(role_admin.id)
}
}
}
result.users = criteria.list(params)
result.total = criteria.count()
The above will always return one result, even though I have verified by looking at the database directly that there should be more results. The params passed to list are correct, but I tried removing them just to be sure. I can't see what is wrong with the above, any suggestions ?
I also tried this
roles{
role{
eq("authority","ROLE_ADMIN")
}
}
But it is throwing exception:
Unknown column 'role_alias2_.authority' in 'where clause'
this works for me:
def criteriaUsers = UserRole.createCriteria()
def users = criteriaUsers.listDistinct{
eq("role", role_admin)
}.user
Side note: from the nomenclature of your classes it looks like you are using spring-security-core plugin. In my humble opinion the hasMany = [roles: UserRole] is redundant as the association User - Role is already being modeled in the UserRole class.

Grails save() passes although objects in hasMany relationship has constraint errors

What is the correct way to handle domain save errors on domain classes under the hasMany relationship? It seems that calling save() on the owning side of the relation will return true even if there are validation errors on the owned objects.
How should I test this in running code or integration test?
I have reduced my problem to the following simple case.
class User {
static hasMany = [emails: Email]
static constraints = { }
}
.
class Email {
static belongsTo = [user: User]
String emailAddress
static constraints = {
emailAddress unique: true
}
}
Here are two suggestions on how to do this. Neither is really elegant.
First one calls save() individually to each on the hasMany relationship. What is good here is that we get the exact error out of the test case. This pretty cumbersome.
#Test
void testHasManyConstraintsOwned(){
def john = new User(login: 'johnDoe')
def email = new Email(emailAddress: 'john#gmail.com')
def duplicateEmail = new Email(emailAddress: 'john#gmail.com')
john.save() // otherwise: NULL not allowed for column "USER_ID" is thrown for email.save()
john.addToEmails(email).addToEmails(duplicateEmail)
assert email.save()
assert !duplicateEmail.save()
assert "unique"== duplicateEmail.errors.getFieldError("emailAddress").code
}
Another approach uses try/catch to detect the excepted fail. Problem here is that we have no way of knowing what went wrong and thus cannot actually test that the domain constraints are working as we expect.
#Test
void testHasManyConstraintsOwningExcp(){
def john = new User(login: 'johnDoe')
def email = new Email(emailAddress: 'john#gmail.com')
def duplicateEmail = new Email(emailAddress: 'john#gmail.com')
john.addToEmails(email).addToEmails(duplicateEmail)
try {
john.save(flush: true, failOnError: true)
assert false // should not reach here
}catch(Exception e){
}
}
What is the correct way to test and to react in the application code?

Defining OR Condition with grails criteria api

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)
}

Saving associated domain classes in Grails

I'm struggling to get association right on Grails. Let's say I have two domain classes:
class Engine {
String name
int numberOfCylinders = 4
static constraints = {
name(blank:false, nullable:false)
numberOfCylinders(range:4..8)
}
}
class Car {
int year
String brand
Engine engine = new Engine(name:"Default Engine")
static constraints = {
engine(nullable:false)
brand(blank:false, nullable:false)
year(nullable:false)
}
}
The idea is that users can create cars without creating an engine first, and those cars get a default engine. In the CarController I have:
def save = {
def car = new Car(params)
if(!car.hasErrors() && car.save()){
flash.message = "Car saved"
redirect(action:index)
}else{
render(view:'create', model:[car:car])
}
}
When trying to save, I get a null value exception on the Car.engine field, so obviously the default engine is not created and saved. I tried to manually create the engine:
def save = {
def car = new Car(params)
car.engine = new Engine(name: "Default Engine")
if(!car.hasErrors() && car.save()){
flash.message = "Car saved"
redirect(action:index)
}else{
render(view:'create', model:[car:car])
}
}
Didn't work either. Is Grails not able to save associated classes? How could I implement such feature?
I think you need a belongsTo in your Engine ie
static belongsTo = [car:Car]
Hope this helps.
For what is worth, I finally nailed it.
The exception I got when trying to save a car was
not-null property references a null or
transient value
It was obvious that the engine was null when trying to save, but why? Turns out you have to do:
def car = new Car(params)
car.engine = new Engine(name: "Default Engine")
car.engine.save()
Since engine doesn't belongs to a Car, you don't get cascade save/update/delete which is fine in my case. The solution is to manually save the engine and then save the car.

Resources