Skip to main content

Session Notes - A Practical Take on GORM

Presenter: Robert Fischer
Author: Grails Persistence with GORM and GSQL
SmokeJumperIT -- Grails consulting and training


Agenda



  • Overview of GORM

  • Intro to HQL

  • tour of GORM labs

  • tour of GORM plugins

  • extending GORM


GORM 101



  • Grails Object Relational Mapping

  • extensible and dynamic data access API

    • dynamic -- as you describe to grails what's in your application, you get more functionality automatically



  • hibernate wrapper (sometimes)

    • can use GORM outside of grails

    • transaction handling

    • data cache

    • schema generation/update system



  • even with the no SQL movement you still need an object to something mapping


Basic GORM Conventions



  • ./grails-app/domain

  • domain classes define tables

  • instance properties define columns

  • static properties define configuration

    • e.g. static hasMany = [bazzes:Baz], static mapping = { ... }

    • any time you see this, some sort of Grails magic is going to be happening behind the scenes



  • automatic id and version

    • GORM assumes you want an artificial ID and you want to work with optimistic locking

    • natural keys are enticing, but always become a problem

    • can opt out of these, but not encouraged



  • opt-in timestamping using dateCreated and lastUpdated

  • everything beyond these basics is probably boundary conditions or a need to extend GORM in some way


GORM Classes (Domain Classes)



  • even with a simple class Baz {} declaration you get tons of functionality

  • better to err on the side of having a domain class for everything in your app to get counts, lists, findByVersion, etc.

  • constraints used for data integrity

    • note that if you're using existing data you can pull "bad" data back but when you try to save, the constraints will kick in and fail so you'll have to deal with the bad data at that point



  • mapping -- depending on how you define things may or may not get mapped

    • String foo (declared string) = mapped

    • bar (only defined via its getter and setter but not declared) = mapped

      • this will always be dirty

      • can come in handy for implementing transforms in an overridden setter



    • bar2 (default artificial property) = not mapped

      • can come in handy for tracking schema changes over time without having this reflected in the database



    • foo2 (default natural property) = not mapped

      • the default part is the problem, not artificial vs. natural



    • baz (runtime added property) = not mapped

      • at application startup grails looks at the compiled structure of the classes to generate hibernate mappings






Underappreciated Methods



  • Foo.lock(id)

    • pulls an object back and locks it from being updated from other processes

    • can solve transactional issues

    • does for update in oracle, currently does nothing in mysql (but supposedly this is getting implemented)



  • Foo.read(id)

    • pulls a record from the database and says "I'm not going to modify this"

    • if changes are made on the object after read, they're ignored

    • saves cache since it won't be cached



  • Foo.findAllWhere(bar:'one',baz:true)

    • can be implemented as Foo.findAllByBarAndBaz('one', true) but is more concise



  • Foo.executeQuery(hql) and Foo.executeUpdate(hql)

    • can use executeUpdate on update, delete, insert

    • allows you to take a bit more ownership of what's going on with the database and the generated sql




Hibernate Query Language (HQL)



  • like SQL but with a lot less pain

    • working directly with objects and relationships as opposed to having to write sql joins manually

    • if you're finding yourself writing sql and manually building up objects, chances are hql can handle what you're doing more simply



  • leverages mapped classes and properties

  • provides abilities to construct objects and maps directly in queries

  • one major limitation: error reporting is miserable

    • if you screw up your hql statement in a way that makes sense to the parser, error messages are good

    • if the hql doesn't make sense to the parser, you just get a null pointer exception from somewhere deep in hibernate

    • hibernate team is aware of this and working on improving




HQL for SQL-heads



  • query on objects, not on database tables

  • objects are normally returned

  • select * can be left off (just "from Foo")

  • no need to join *-to-one relationships: from Foo f where f.bar = ?

  • traverse many-to-many relationships via a joining on object relationships

    • from Foo f join f.bazzes b

    • can say "fetch join" to eagerly pull back bazzes for Foo



  • on existing databases--can use GREG (or GRAG?) -- Grails Application Generator

    • generates domain classes, etc. from an existing database

    • falls down if some company mandates all db access is through storedprocs

      • problem for Hibernate in general

      • fighting against the GORM conventions so hard that it makes it very difficult

      • plugin available to specify different databases for different domain objects--can get around some of this going this route






HQL Tips and Tricks



  • encapsulate the HQL in methods

    • don't put it in a service--put it on the domain class where it belongs

    • HQL is referencing properties, so if the property name changes you have to track all references down if this isn't in the domain class



  • use $Token.name to save having to write out package names

    • standard groovy references--resolves the same way



  • use simple interpolation (${}) to add a bit of dynamism to your query

  • do not ever directly interpolate external values into hql

    • sql injection attack vector

    • always pass these in as parameters




GORM Labs



  • plugin

  • tons of added methods to GORM

    • Foo.query -- shorter reference for HQL queries

    • for any property on a class you get

      • min, max, avg, sum, count, countDistinct on each property (e.g. Foo.minBar, Foo.maxBar, etc.)



    • Foo.connection -- access to current db connection

    • Foo.sql

    • Foo.session

    • Foo.flush()

    • $foo.errors -- nicer representation of errors

    • Foo.withCriteria -- allows ordering by nested properties (only works for one to one properties)

    • Gorm.connection, Gorm.flush, etc.

      • makes calls directly on gorm without having to refer to an arbitrary domain class



    • transactional actions in controllers

      • got him flamed on the list since this is supposed to be in services, but many people do db stuff in controllers



    • HasMany pagination/counts
      foo.bars(max:2, offset:3)
      foo.countBars

    • session object dehydration

      • if an object is thrown into the http session and it has an id, the id gets persisted in the session and it can be used to pull back the domain class if the object is attempted to be retrieved after the hibernate session has died






Favorite GORM Stunt: Transparent API Switch



  • assume there is a slow gorm api call
    Foo.findAllByBarOrBaz(bar, baz)

  • define a static method with the api call method name and hand-optimized sql
    def ids = Foo.sql.rows(query)*.id

  • return the (possibly cached) instances
    return Foo.getAll(ids)


Other GORM Plugins



  • audit logging

    • automatically keeps an audit trail on database activity



  • extended GORM mappings

  • GORM HBase (Hadoop HBase)

  • GORM JPA

    • exists because of Google App Engine and the BigTable JPA functionality



  • explicit insert

    • allows you to do an explicit insert as opposed to save() which might do an insert or an update



  • datasources (per-domain class data source)

  • multitenant (per-tenant data source)

    • tenant attached to an HTTP request

    • great for situations where you have multiple clients each of which has their own datasource



  • system itools

    • plugin for IBM itools



  • DTO

  • joda-time

    • makes Joda-Time compatible with java.util.Date

    • makes all Joda-Time structures persistable to the database




Extending GORM The Easy Way



  • create plugin based on GORM Labs
    dependsOn = [gormLabs: '0 > *']

    • lets you take advantage of high-level stuff that GORM labs fixes



  • do metaClass mangling in doWithDynamicMethods

  • retrieve domain class metaClasses by application.domainClasses*.clazz


Current FOSS Projects - Shameless Cry for Help



  • Liquibase-DSL / Autobase

  • Bitescript (was JVMScript)

  • GORM Labs, Testing Labs, Sublog

  • JConch


Q&A



  • interesting plugins for other frameworks/languages

    • PHP plugin -- Grails can wrap existing PHP apps



  • any cases where you wouldn't use GORM/Hibernate?

    • if storedprocs are required, not a good fit

    • in pretty much every other case hibernate works well

    • spent some time doing rails, perfer gorm method of doing things as opposed to activerecord



  • in production databases, what's the best practice for the datasource setting? (e.g. create, update, create-drop, etc.)

    • schema management is a difficult problem if you want 100% uptime

    • changing from one schema to another can cause some issues with hibernate

    • autobase plugin fixes some of these problems -- allows you to run migrations but have to write migrations by hand at this point



Comments

Popular posts from this blog

Running a Django Application on Windows Server 2012 with IIS

This is a first for me since under normal circumstances we run all our Django applications on Linux with Nginx, but we're in the process of developing an application for another department and due to the requirements around this project, we'll be handing the code off to them to deploy. They don't have any experience with Linux or web servers other than IIS, so I recently took up the challenge of figuring out how to run Django applications on Windows Server 2012 with IIS.

Based on the dated or complete lack of information around this I'm assuming it's not something that's very common in the wild, so I thought I'd share what I came up with in case others need to do this.


This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Assumptions and CaveatsThe operating system is Windows Server 2012 R2, 64-bit. If another variant of the operating system is being used, these instructions may not work properly.All of the soft…

Installing and Configuring NextPVR as a Replacement for Windows Media Center

If you follow me on Google+ you'll know I had a recent rant about Windows Media Center, which after running fine for about a year suddenly decided as of January 29 it was done downloading the program guide and by extension was therefore done recording any TV shows.

I'll spare you more ranting and simply say that none of the suggestions I got (which I appreciate!) worked, and rather than spending more time figuring out why, I decided to try something different.

NextPVR is an awesome free (as in beer, not as in freedom unfortunately ...) PVR application for Windows that with a little bit of tweaking handily replaced Windows Media Center. It can even download guide data, which is apparently something WMC no longer feels like doing.

Background I wound up going down this road in a rather circuitous way. My initial goal for the weekend project was to get Raspbmc running on one of my Raspberry Pis. The latest version of XBMC has PVR functionality so I was anxious to try that out as a …

Fixing DPI Scaling Issues in Skype for Business on Windows 10

My setup for my day job these days is a Surface Pro 4 and either an LG 34UC87M-B or a Dell P2715Q monitor, depending on where I'm working. This is a fantastic setup, but some applications have trouble dealing with the high pixel density and don't scale appropriately.
One case in point is Skype for Business. For some reason it scales correctly as I move between the Surface screen and the external monitor when I use the Dell, but on the LG monitor Skype is either massive on the external monitor, or tiny on the Surface screen.
After a big of digging around I came across a solution that worked for me, which is to change a setting in Skype's manifest file (who knew there was one?). On my machine the file is here: C:\Program Files\Microsoft Office\Office16\LYNC.EXE.MANIFEST
And the setting in question is this:
<dpiAware>True/PM</dpiAware>
Which I changed to this: <dpiAware>False/PM</dpiAware>
Note that you'll probably have to edit the file as administr…