The document discusses several techniques for optimizing GORM performance:
1. Mapping database views to domain classes to avoid unnecessary joins.
2. Writing a custom configuration subclass to filter DDL statements or override Hibernate behavior.
3. Implementing read-only domain classes by preventing saves, deletes, and updates through event listeners.
4. Monitoring SQL queries through logging, plugins, and timing analysis to identify optimization opportunities.
2. Agenda
Mapping Database Views
Writing a Custom Configuration Subclass for Fun and Profit
Read-only Domain Classes
Monitoring
CONFIDENTIAL 2
3. Also See
Advanced GORM - Performance, Customization and
Monitoring
• Spring One / class UserInfo {
2GX 2010
String name
String orgName
• http://www.infoq.com/presentations/GORM-Performance
static mapping = {
• Primarily focused on 'v_user_info'
table performance implications around
}
}
using Many-to-One and Many-to-Many in GORM
CONFIDENTIAL 3
11. Database View –> Entity
grails-app/conf/hibernate/misc.mysql.innodb.hbm.xml
<hibernatemapping>
<databaseobject>
<create>
CREATE OR REPLACE VIEW v_user_info AS
SELECT u.name, u.id, u.version, o.name org_name
FROM auth_user u, organization o
WHERE u.organization_id = o.id
</create>
<drop>DROP VIEW IF EXISTS v_user_info</drop>
<dialectscope
name='org.hibernate.dialect.MySQLInnoDBDialect' />
</databaseobject>
</hibernatemapping>
CONFIDENTIAL 11
12. Subdomain Entity
“Subdomain” with a subset of AuthUser data:
class Person {
String name
Organization organization
static mapping = {
table 'auth_user'
}
}
CONFIDENTIAL 12
13. Subdomain Entity
Updatable Subdomain:
class Person {
String name
Organization organization
static mapping = {
table 'auth_user'
dynamicUpdate true
}
}
CONFIDENTIAL 13
14. Writing a Custom Configuration Subclass for
Fun and Profit
CONFIDENTIAL 14
15. Writing a Custom Configuration Subclass for Fun and Profit
Custom Configurations: http://burtbeckwith.com/blog/?p=465
Examples
• Previous SQL generation example
• Overrides generateSchemaCreationScript(),
generateDropSchemaScript(), and generateSchemaUpdateScript()
• Specifying the 'connection.provider_class' property
• Overrides buildSettings()
• Renaming columns of a composite foreign key
• Overrides secondPassCompile()
• Overriding the EntityPersister class
• Overrides secondPassCompile()
• Using field access
• Overrides secondPassCompile()
CONFIDENTIAL 15
16. Writing a Custom Configuration Subclass for Fun and Profit
secondPassCompile()
• Access each PersistentClass/RootClass and call any of
• addFilter() • setDynamicUpdate()
• setBatchSize() • setExplicitPolymorphism()
• setCustomSQLDelete() • setOptimisticLockMode()
• setCustomSQLInsert() • setSelectBeforeUpdate()
• setCustomSQLUpdate() • setWhere()
• setDynamicInsert()
CONFIDENTIAL 16
18. Read-only Domain Classes
Not 100% possible, but close enough
Hibernate
• Seems possible via setMutable(false) in a custom Configuration, but:
• Won't block saves or deletes, only disables dirty-check updates
• Does nothing with collections since they're a PersistentSet or
PersistentList and managed separately (but you wouldn't want to map
collections anyway, right?)
• See http://docs.jboss.org/hibernate/core/3.5/reference/en/html/readonly.html
CONFIDENTIAL 18
19. Read-only Domain Classes
Grails
• Only beforeUpdate supports 'vetoing' by returning false
• A hackish solution would be to throw an exception in beforeDelete and
beforeInsert
• Better solution: hibernateEventListeners bean in grails
app/conf/spring/resources.groovy
• Still a good idea to use a custom Configuration and call setMutable(false)
to reduce memory usage
CONFIDENTIAL 19
24. Read-only Domain Classes
You can also map a second writable domain class
to the same table, e.g. for admin:
class User { class WritableUser {
String username String username
String password String password
}
static mapping = {
table 'user'
}
}
CONFIDENTIAL 24
26. Monitoring
SQL logging
• logSql=true in DataSource.groovy
• org.hibernate.SQL → debug, org.hibernate.type → trace
appenders {
file name: 'sql', file: 'sql.log'
}
debug additivity: false, sql: 'org.hibernate.SQL'
trace additivity: false, sql: 'org.hibernate.type'
• P6spy plugin
• Use SQL Profiler Swing app to view realtime logs; see Mike Hugo's blog post
Grails, p6spy and Sql Profiler
• Run explain (Oracle, MySQL, others) on real queries to look for missing indexes
CONFIDENTIAL 26
27. Monitoring
Spring Insight lets you drill down to SQL and view timing
• http://www.grails.org/screencast/show/13
Profiler plugin can give you timing data
JavaMelody Plugin
App Info plugin
CONFIDENTIAL 27