4. Create
Person p = new Person()
p.name = “Vijay”
p.age = 25
p.lastVisit = new Date()
p.save()
Person p = new Person(name: "Fred", age: 40, lastVisit: new Date())
p.save()
5. Read/Retrieve
get(), read(), load()
get():- Retrieves an instance of the domain class for the specified id.
read():- Retrieves an instance of the domain class for the specified id in a
read-only state.
load():- Returns a proxy instance of the domain class for the given
identifier.
Ex:- def p = Person.get(1)
assert 1 = p.id
Note*:- In case of get() and read() null is returned if the row with the
6. get()vsread()vsload()
get() vs read()
The read method is similar to the get method except that automatic dirty
detection(check all the properties) is disabled.
The instance isn't truly read-only - you can modify it - but if it isn't
explicitly saved but has been modified, it won't be updated in the database
during a flush.
get() vs load()
get() returns null if data is not found in session as well as database, but
load() returns ObjectNotFoundException().
get() always return completely initialized object while load() may be not.
7. get() loads the data as soon as it’s called whereas load() returns a proxy
object and loads data only when it’s actually required, so load() is better
because it support lazy loading.
Since load() throws exception when data is not found, we should use it only
when we know data exists.
We should use get() when we want to make sure data exists in the database.
8. Update
To update an instance, change some properties and then call save again:
def p = Person.get(1)
p.name = "Shukla"
p.save()
9. Delete
To delete an instance use the delete method:
def p = Person.get(1)
p.delete()
12. When we save or delete the Face instance, GORM will save or delete the Nose. In other words, saves
and deletes will cascade from Face to the associated Nose:-
new Face(nose:new Nose()).save()
Now if we delete the Face instance, the Nose will go too:
def f = Face.get(1)
f.delete() // both Face and Nose deleted
To make the relationship a true one-to-one, use the hasOne property on the owning side, e.g. Face:
Note that using this property puts the foreign key on the inverse table to the example A, so in this case the
foreign key column is stored in the nose table inside a column called face_id. Also, hasOne only works
with bidirectional relationships.
class Face {
static hasOne = [nose:Nose]
}
class Nose {
Face face
}
13. One-To-many
A one-to-many relationship is when one class, has many instances of another class. With Grails you
define such a relationship with the hasMany setting:
In this case we have a unidirectional one-to-many. Grails will, by default, map this kind of relationship with a join table.
Grails will automatically inject a property of type java.util.Set into the domain class based on the hasMany setting. This can
be used to iterate over the collection:-
class Author {
static hasMany = [books: Book]
String name
}
class Book {
String title
}
def a = Author.get(1)
for (book in a.books) {
println book.title
}
14. The default cascading behaviour is to cascade saves and updates, but not deletes unless a belongsTo is
also specified:
class Author {
static hasMany = [books: Book]
String name
}
class Book {
static belongsTo = [author: Author]
String title
}
15. Many-to-many
Grails supports many-to-many relationships by defining a hasMany on both sides of the relationship and
having a belongsTo on the owned side of the relationship:
Grails maps a many-to-many using a join table at the database level. The owning side of the relationship,
in this case Author, takes responsibility for persisting the relationship and is the only side that can cascade
saves across.
class Book {
static belongsTo = Author
static hasMany = [authors:Author]
String title
}
class Author {
static hasMany = [books:Book]
String name
}
16. Compositioningorm
Grails supports the notion of composition. In this case instead of mapping classes onto separate tables a
class can be "embedded" within the current table.
class Person {
Address homeAddress
Address workAddress
static embedded = ['homeAddress', 'workAddress']
}
class Address {
String number
String code
}
17. Inheritanceingorm
GORM supports inheritance both from abstract base classes and concrete persistent GORM entities.
class Content {
String author
}
class BlogEntry extends Content {
URL url
}
class Book extends Content {
String ISBN
}
def content = Content.list() // list all blog entries, books and podcasts
content = Content.findAllByAuthor('Joe Bloggs') // find all by author
def podCasts = PodCast.list() // list only podcasts
19. PersistenceBasics
A key thing to remember about Grails is that under the surface Grails is using Hibernate for persistence.
Grails automatically binds a Hibernate session to the currently executing request. This lets you use the
save and delete methods as well as other GORM methods transparently.
20. SavingandUpdating
def p = Person.get(1)
p.save()
This save will be not be pushed to the database immediately - it will be pushed when the next flush
occurs.
But there are occasions when you want to control when those statements are executed or, in Hibernate
terminology, when the session is "flushed". To do so you can use the flush argument to the save method:
def p = Person.get(1)
p.save(flush: true)
21. Another thing to bear in mind is that Grails validates a domain instance every time you save it. If that
validation fails the domain instance will not be persisted to the database. By default, save() will simply
return null in this case, but if you would prefer it to throw an exception you can use the failOnError
argument:
def p = Person.get(1)
try {
p.save(failOnError: true)
}
catch (ValidationException e) {
// deal with exception
}
22. Eagerandlazyfetching
class Airport {
String name
static hasMany = [flights: Flight]
}
class Flight {
String number
Location destination
static belongsTo = [airport: Airport]
}
class Location {
String city
String country
}
def airport = Airport.findByName("Gatwick")
for (flight in airport.flights) {
println flight.destination.city
}
GORM will execute a single SQL query to fetch the Airport
instance, another to get its flights, and then 1 extra query
for each iteration over the flights association to get the
current flight's destination. In other words you get N+1
queries (if you exclude the original one to get the airport).
24. PessimisticandoptimisticLock
By default GORM classes are configured for optimistic locking. Optimistic locking is a feature of Hibernate
which involves storing a version value in a special version column in the database that is incremented
after each update.
When you perform updates Hibernate will automatically check the version property against the version
column in the database and if they differ will throw a StaleObjectException. This will roll back the
transaction if one is active.
This is useful as it allows a certain level of atomicity.
Pessimistic locking is equivalent to doing a SQL "SELECT * FOR UPDATE" statement and locking a row
in the database. This has the implication that other read operations will be blocking until the lock is
released.
25. Modificationchecking
isDirty:- You can use the isDirty method to check if any field has been modified:
getDirtyPropertyNames:- You can use the getDirtyPropertyNames method to retrieve the names of
modified fields; this may be empty but will not be null.
getPersistentValue:- You can use the getPersistentValue method to retrieve the value of a modified
field
def airport = Airport.get(10)
assert !airport.isDirty()
airport.properties = params
def modifiedFieldNames = airport.getDirtyPropertyNames()
for (fieldName in modifiedFieldNames) {
def currentValue = airport."$fieldName"
def originalValue = airport.getPersistentValue(fieldName)
if (currentValue != originalValue) {
// do something based on changed value
}
}
27. Advancedgormfeatures
GORM supports the registration of events as methods that get fired when certain events occurs such as
deletes, inserts and updates. The following is a list of supported events:
beforeInsert - Executed before an object is initially persisted to the database. If you return false, the insert will be
cancelled.
beforeUpdate - Executed before an object is updated. If you return false, the update will be cancelled.
beforeDelete - Executed before an object is deleted. If you return false, the delete will be cancelled.
beforeValidate - Executed before an object is validated
afterInsert - Executed after an object is persisted to the database
afterUpdate - Executed after an object has been updated
28. CustomORMmapping
Custom mappings are defined using a static mapping block defined within your domain class:
class Person {
…
static mapping = {
version false
autoTimestamp false
}
}
You can also configure global mappings in application.groovy (or an external config file) using this setting:
grails.gorm.default.mapping = {
version false
autoTimestamp false
}
29. TableandColumnsname
class Person {
…
static mapping = {
table 'people'
}
}
class Person {
String firstName
static mapping = {
table 'people'
firstName column: 'First_Name'
}
}
class Address {
String number
String postCode
static mapping = {
postCode type: PostCodeType
}
The ORM DSL allows mapping unidirectional relationships using a foreign key association instead
The default fetch strategy used by Grails is "lazy", which means that the collection will be lazily initialized on first access. This can lead to the n+1 problem if you are not careful.
If you define the Address class in a separate Groovy file in the grails-app/domain directory you will also get an address table. If you don't want this to happen use Groovy's ability to define multiple classes per file and include the Address class below the Person class in the grails-app/domain/Person.groovy file
The version will only be updated after flushing the session.