quinta-feira, 22 de agosto de 2013

Grails - GORM

- 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