- GORM
- Many-to-one (composition)
class Face {
Nose nose
}
class Nose {
static belongsTo = [face:Face] // To make bidirecional relationship. Without this line it is unidirecional
}
new Face(nose:new Nose()).save()
def f = Face.get(1); f.delete() // both Face and Nose deleted
- one-to-one
class Face {
static hasOne = [nose:Nose]
static constraints = {
nose unique: true // guarantee that it is one-to-one
}
}
class Nose { // here there is the foreign key.
Face face
}
- One-to-Many - One class has many instance of another class
class Author {
List books // Keeps the books in the orders they was inserted. Can use Map, Set, etc.
static hasMany = [books: Book]
}
class Book { // The default cascading behaviour is to cascade saves and updates, but not deletes unless a belongsTo is also specified:
static belongsTo = [author: Author] // this line makes bidirecional one-to-many otherwiswe is unidirectional
String title
}
- Many-to-many - uses a join table at the database level. The owning side takes the responsibility for persisting and is the only side that can cascade.
- Not supported by Scarfold
class Book {
static belongsTo = Author //on the owned side of the relationship:
static hasMany = [authors:Author]
}
class Author {
static hasMany = [books:Book]
String name
}
- Joint a table
class Person {
static hasMany = [nicknames: String] // Has many strings
static mapping = {
hasMany joinTable: [name: 'bunch_o_nicknames', // Set the table of nicknames
key: 'person_id',
column: 'nickname',
type: "text"]
}
}
- Composition in GORM
class Person {
Address homeAddress
Address workAddress
static embedded = ['homeAddress', 'workAddress'] //Everything in the same table.
}
class Address {
String number
String code
}
- Persistence
- Every class created in domain will be persisted. Classes not persisted are in src/groovy and src/java
- Uses optmistic Locking
def p = Person.get(1)
try {
p.save(flush: true) or p.delete(flush: true)
} catch (org.springframework.dao.DataIntegrityViolationException e) {
// deal with exception
}
try {
p.save(failOnError: true) // ValidationException
} catch (ValidationException e) {
// deal with exception
}
- Eager or Lazy Fetching(default)
- class Person {
Pet pet
static hasMany = [addresses: Address]
static mapping = {
addresses lazy: false //way 1
pet fetch: 'join' //way 2
}
}
class Address { }
class Pet { }
- Recomendation: fetch: 'join' for single-ended associations and lazy: false for one-to-manys.
- Alternative: Batch Fetch:
static mapping = { flights batchSize: 10 }
- Locking
Every table has a id and version. VERSION is used for optmistic locking.
- Optimistic : Default. Uses a version in database which is incremented in each update. Can throw an OptimisticLockingFailureException
- Whenever you try to update a object, Hibernate comprares version (Object x DB), if it is different other person modified and the modification is rolledback.
- Pessimistic: Equivalent to "SELECT * FOR UPDATE" and locking a row.
- Programaticaly
def airport = Airport.get(10) or def airport = Airport.lock(10) or def airport = Airport.findByName("Heathrow", [lock: true])
airport.lock() // lock for update
airport.name = "Heathrow"
airport.save()
- Configuration
version = false. Good for read-only domain objects.
- Dirty: Show if a attribute of a object was modified. Does not work in collection.
- Second-level cache
- Hibernate session acts as a cache that consolidades repeated save calls and prevent fetches on the same objects.
- Second-level cache: Extension of that capability. Caches objs at an additional intermediary level between session cache and DB.
- Result in increase speed for often-used data, but at some risks.
ex: static mapping = {
cache usage: 'read-only', include: 'non-lazy'
topics cache:false
}
read-only(read-only operations) read-write(update frequently), nonrestrict-read-write(amost no chance to two transactions try to modify obj
transational (Fully transational cache)
- Querying with GORM
- Dynamic Finders : Book.findByReleaseDateBetween(firstDate, secondDate), Book.findByTitle("The Stand")
- Where Queries:
- def query = Person.where {
lastName == "Simpson"
}
def bartQuery = query.where {
firstName == "Bart"
}
Person p = bartQuery.find()
- def query = Pet.where {
owner.firstName == "Joe" || owner.firstName == "Fred"
}
- Criteria Queries
- createCriteria or withCriteria
def c = Account.createCriteria()
def results = c {
between("balance", 500, 1000)
eq("branch", "London")
or {
like("holderFirstName", "Fred%")
like("holderFirstName", "Barney%")
}
maxResults(10)
order("holderLastName", "desc")
}
- SQL Projections: to customize the returned results
- useful for returning the average, count, maximum, minimum, distinct, or sum of results
def results = c.list {
projections {
sqlProjection 'sum(width * height) as totalArea', 'totalArea', INTEGER
}
}
- Projections with criteria
def results = Person.withCriteria {
gt "age", {
projections {
avg "age"
}
}
order "firstName"
}
- SQL Restrictions:
def c = Person.createCriteria()
def peopleWithShortFirstNames = c.list { //fisrName attribute, but first_name in table
sqlRestriction "char_length(first_name) < ? AND char_length(first_name) > ?", [maxValue, minValue]
}
- Eacher fetch
def results = Airport.withCriteria {
eq "region", "EMEA"
flights {
like "number", "BA%"
}
}
- Detached Criteria: Queries not associated with a specific Session. It is created to be used later.
- Hibernate Query Language (HQL)
Main methods: find, findAll, executeQuery
Ex:
def results = Book.findAll("from Book as b where b.title like 'Lord of the%'")
def results = Book.findAll("from Book as b, Author as a where b.author = a and a.surname = ? asc", ['Smith'], [max: 10, offset: 20])
def results = Book.findAll("from Book as b where b.title like :search or b.author like :search",[search: "The Shi%"])
- Events
beforeInsert - Executed before an object is initially persisted to the database
beforeUpdate - Executed before an object is updated
beforeDelete - Executed before an object is deleted
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
afterDelete - Executed after an object has been deleted
onLoad - Executed when an object is loaded from the database
- Custom Mapping
- It is possible to map table and column based on a preexistent table.
- Programmatic Transaction
- The withTransaction method deals with the begin/commit/rollback logic for you within the scope of the block.
- "save points" to rollback a transaction to a particular point in time if you don't want to rollback the entire transaction.
def transferFunds() {
Account.withTransaction { status ->
def source = Account.get(params.from)
def dest = Account.get(params.to)
def amount = params.amount.toInteger()
if (source.active) {
if (dest.active) {
source.balance -= amount
dest.amount += amount
}
else {
status.setRollbackOnly()
}
}
}
}
Nenhum comentário:
Postar um comentário