Wednesday, October 20, 2010

Grails GORM trick: Use read() rather than get() on updates

Hi

So i discovered that it might be a good idea use read() on your update method.

So use

def person = Person.read(params.id)

instead of

def person = Person.get(params.id)

The consequence of this is that hibernate dirty checking is turned off. So to save your domain object you MUST call save(). So if you cancel the update for any reason without calling save() you do not have to to worry about grails persisting things automatically due to dirty checking :)

Hope this helps

Sunday, October 17, 2010

GORM gotcha: Many to Many mapping causing new Domain to be saved

Hi All

I came across a new Grails GORM subtle gotcha this week.

I was creating a new Domain object and it was still getting persisted to the database even though i was not calling save().

This is not normal behavior for NEW objects. As you may or may not be aware this is the behavior when updating objects though. So i was quite surprised by this and did some trials in trying to fix the problem.

First calling discard() on my NEW object was the first try. But that was a guess and i knew really it would not work. Because that object not yet associated with the session. And it didn't. So i finally found the problem.

This was all to do the the many to many relationship that was setup on the domain object.

So for example i have somethings like this:

class Location {

static hasMany = [posters:Poster]

String name

}

class Poster {

static belongsTo = [Location]

static hasMany = [locations:Location]

String name

}




So in my controller i allow a user to select multiple locations. These locations all get added to the new Poster in this line of code:

Poster poster = new Poster(params)




So if i then have some business logic that decides that i do not want to create this new Poster i just need to not call save right. Turns out that is wrong.

Because of the many to many relationship between Location and Poster. Poster has been assoiated with the locations. So the locations are now dirty and will get persisted at the end of the transaction. This will cascade to my new Poster and save it.

So to fix this you need to discard the Location objects so they do not save your Poster object.

Poster poster = new Poster(params)

if(businessLogicValidate() == false){

poster.locations.each{Location l ->
l.discard();
}

}else{

poster.save()

}





Hope this helps someone.

Thursday, October 14, 2010

Stored procedures on Amazon RDS

Been a long time since i blogged but need to document this pain in the arse.

So for a new project i need to create a mysql function (same for stored proc) on amazon RDS.

When i tried to install it i got this error:

ERROR 1419 (HY000) at line 3: You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)

So to get them to install you need to set that database parameter to ON. However to do that is not so simple.

Lucky for you people i have found out how to do it and here are the steps:

1) Install the RDS CLI tools. I installed these on my EC2 instance.

Download from here:
http://developer.amazonwebservices.com/connect/entry.jspa?categoryID=294&externalID=2928

They are java based and so you need have Java running. Plus they need your amazon key and secret key. Once you have them installed following the readme do the following:

Important!! make sure you set the AWS region you are working in.
Eg export EC2_REGION=ap-southeast-1

2) create a new parameter group

rds-create-db-parameter-group peters-params -f mysql5.1 -d "peters params"

3) modify the log_bin_trust_function_creators to be set to ON

rds-modify-db-parameter-group peters-params --parameters="name=log_bin_trust_function_creators, value=on, method=immediate"

4) change your running db instance to use the new param group

rds-modify-db-instance petersdbinstance --db-parameter-group-name=peters-params

5) restart the instance

rds-reboot-db-instance petersdbinstance


That will allow you to create a function or stored procedure:

Some point about the function / stored proc:

You need to specify the DETERMINISTIC stuff and you need to add a DEFINER=CURRENT_USER


DELIMITER $$
DROP FUNCTION IF EXISTS `sayhello`$$
CREATE DEFINER=CURRENT_USER FUNCTION `sayhello`(param1 VARCHAR(120))
RETURNS VARCHAR(120)
NOT DETERMINISTIC
READS SQL DATA
BEGIN
RETURN CONCAT('Hello, ',s,'!');

END$$
DELIMITER ;


This works for me hope it helps