Top Banner
© 2010 SpringSource, A division of VMware. All rights reserved CONFIDENTIAL CONFIDENTIAL GORM Optimization Burt Beckwith SpringSource
28
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: GR8Conf 2011: GORM Optimization

© 2010 SpringSource, A division of VMware. All rights reserved

CONFIDENTIALCONFIDENTIAL

GORM Optimization

Burt Beckwith

SpringSource

Page 2: GR8Conf 2011: GORM Optimization

2CONFIDENTIAL 2CONFIDENTIAL 2

Mapping Database Views

Writing a Custom Configuration Subclass for Fun and Profit

Read-only Domain Classes

Monitoring

Agenda

Page 3: GR8Conf 2011: GORM Optimization

3CONFIDENTIAL 3CONFIDENTIAL

Also See

class UserInfo { String name String orgName

static mapping = { table 'v_user_info' }}

Advanced GORM - Performance, Customization and

Monitoring

•Spring One / 2GX 2010

•http://www.infoq.com/presentations/GORM-Performance

•Primarily focused on performance implications around

using Many-to-One and Many-to-Many in GORM

Page 4: GR8Conf 2011: GORM Optimization

4CONFIDENTIAL 4CONFIDENTIAL

Mapping Database Views

Page 5: GR8Conf 2011: GORM Optimization

5CONFIDENTIAL 5CONFIDENTIAL

Database View –> Entity

class AuthUser { String name String password Organization organization}

class UserInfo { String name String orgName}

class Organization { String name}

Page 6: GR8Conf 2011: GORM Optimization

6CONFIDENTIAL 6CONFIDENTIAL

Database View –> Entity

CREATE OR REPLACE VIEW v_user_info ASSELECT u.name, u.id, u.version, o.name org_nameFROM auth_user u, organization oWHERE u.organization_id = o.id

class UserInfo { String name String orgName

static mapping = { table 'v_user_info' }}

Page 7: GR8Conf 2011: GORM Optimization

7CONFIDENTIAL 7CONFIDENTIAL

Database View –> Entity

ERROR hbm2ddl.SchemaExport  ­ Unsuccessful: create table v_user_info (id bigint not null auto_increment, version bigint not null, name varchar(255) not null, org_name varchar(255) not null, primary key (id)) type=InnoDB

ERROR hbm2ddl.SchemaExport  ­ Table 'v_user_info' already exists

Page 8: GR8Conf 2011: GORM Optimization

8CONFIDENTIAL 8CONFIDENTIAL

Database View –> Entity

Register a Custom Configuration in DataSource.groovy

dataSource { pooled = true driverClassName = ... username = ... password = ... dialect = ... configClass = gr8conf.DdlFilterConfiguration}

Page 9: GR8Conf 2011: GORM Optimization

9CONFIDENTIAL 9CONFIDENTIAL

Database View –> Entity

public class DdlFilterConfiguration extends GrailsAnnotationConfiguration {

private static final String[] IGNORED_NAMES = { "v_user_info" }; ... private boolean isIgnored(String command) { command = command.toLowerCase();

for (String table : IGNORED_NAMES) { if (command.startsWith("create table " + table + " ") || command.startsWith("alter table " + table + " ") || command.startsWith("drop table " + table) || command.startsWith("drop table if exists " + table)) { return true; } }

return false; }}

Page 10: GR8Conf 2011: GORM Optimization

10CONFIDENTIAL 10CONFIDENTIAL

Database View –> Entity

<hibernate­configuration>

   <session­factory>

      <mapping resource='misc.mysql.innodb.hbm.xml'/>

      <mapping resource='misc.h2.hbm.xml'/>

   </session­factory>

</hibernate­configuration>

grails-app/conf/hibernate/hibernate.cfg.xml

Page 11: GR8Conf 2011: GORM Optimization

11CONFIDENTIAL 11CONFIDENTIAL

Database View –> Entity

<hibernate­mapping>   <database­object>

      <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>

      <dialect­scope          name='org.hibernate.dialect.MySQLInnoDBDialect' />

   </database­object></hibernate­mapping>

grails-app/conf/hibernate/misc.mysql.innodb.hbm.xml

Page 12: GR8Conf 2011: GORM Optimization

12CONFIDENTIAL 12CONFIDENTIAL

Subdomain Entity

class Person { String name Organization organization

static mapping = { table 'auth_user' }}

“Subdomain” with a subset of AuthUser data:

Page 13: GR8Conf 2011: GORM Optimization

13CONFIDENTIAL 13CONFIDENTIAL

Subdomain Entity

class Person { String name Organization organization

static mapping = { table 'auth_user' dynamicUpdate true }}

Updatable Subdomain:

Page 14: GR8Conf 2011: GORM Optimization

14CONFIDENTIAL 14CONFIDENTIAL

Writing a Custom Configuration Subclass for Fun and Profit

Page 15: GR8Conf 2011: GORM Optimization

15CONFIDENTIAL 15CONFIDENTIAL

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()

Page 16: GR8Conf 2011: GORM Optimization

16CONFIDENTIAL 16CONFIDENTIAL

Writing a Custom Configuration Subclass for Fun and Profit

secondPassCompile()

• Access each PersistentClass/RootClass and call any of

• addFilter()

• setBatchSize()

• setCustomSQLDelete()

• setCustomSQLInsert()

• setCustomSQLUpdate()

• setDynamicInsert()

• setDynamicUpdate()

• setExplicitPolymorphism()

• setOptimisticLockMode()

• setSelectBeforeUpdate()

• setWhere()

Page 17: GR8Conf 2011: GORM Optimization

17CONFIDENTIAL 17CONFIDENTIAL

Read-only Domain Classes

Page 18: GR8Conf 2011: GORM Optimization

18CONFIDENTIAL 18CONFIDENTIAL

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

Page 19: GR8Conf 2011: GORM Optimization

19CONFIDENTIAL 19CONFIDENTIAL

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

Page 20: GR8Conf 2011: GORM Optimization

20CONFIDENTIAL 20CONFIDENTIAL

Read-only Domain Classes

import gr8conf.ReadOnlyEventListener

import o.c.g.g.orm.hibernate.HibernateEventListeners

beans = {

   readOnlyEventListener(ReadOnlyEventListener)

   hibernateEventListeners(HibernateEventListeners) {      listenerMap = ['pre­delete': readOnlyEventListener,                     'pre­insert': readOnlyEventListener,                     'pre­update': readOnlyEventListener]   }}

grails-app/conf/spring/resources.groovy

Page 21: GR8Conf 2011: GORM Optimization

21CONFIDENTIAL 21CONFIDENTIAL

Read-only Domain Classes

class ReadOnlyEventListener implements PreDeleteEventListener,         PreInsertEventListener, PreUpdateEventListener {

 private static final List<String> READ_ONLY = ['gr8conf.LegacyData']

 boolean onPreDelete(PreDeleteEvent event) {    isReadOnly event.persister.entityName }

 boolean onPreInsert(PreInsertEvent event) {    isReadOnly event.persister.entityName }

 boolean onPreUpdate(PreUpdateEvent event) {    isReadOnly event.persister.entityName }

 private boolean isReadOnly(String name) { READ_ONLY.contains name }}

src/groovy/gr8conf/ReadOnlyEventListener.groovy

Page 22: GR8Conf 2011: GORM Optimization

22CONFIDENTIAL 22CONFIDENTIAL

Read-only Domain Classes

class LegacyData {

   String name

   static mapping = {      id column: 'legacy_data_id'      version false   }}

grails-app/domain/gr8conf/LegacyData.groovy

Page 23: GR8Conf 2011: GORM Optimization

23CONFIDENTIAL 23CONFIDENTIAL

Read-only Domain Classes

ReadOnlyEventListener

• delete()

• silently fails

• Update with save()

• silently fails

• New instance save()

• Throws exception ?

Page 24: GR8Conf 2011: GORM Optimization

24CONFIDENTIAL 24CONFIDENTIAL

Read-only Domain Classes

class User {

   String username   String password}

You can also map a second writable domain class to the same table, e.g. for admin:

class WritableUser {

   String username   String password

   static mapping = {      table 'user'   }}

Page 25: GR8Conf 2011: GORM Optimization

25CONFIDENTIAL 25CONFIDENTIAL

Monitoring

Page 26: GR8Conf 2011: GORM Optimization

26CONFIDENTIAL 26CONFIDENTIAL

Monitoring

SQL logging

• logSql=true in DataSource.groovy

• org.hibernate.SQL → debug, org.hibernate.type → trace

• 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

appenders {   file name: 'sql', file: 'sql.log'}

debug additivity: false, sql: 'org.hibernate.SQL'trace additivity: false, sql: 'org.hibernate.type'

Page 27: GR8Conf 2011: GORM Optimization

27CONFIDENTIAL 27CONFIDENTIAL

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

Page 28: GR8Conf 2011: GORM Optimization

28CONFIDENTIAL 28CONFIDENTIAL

Questions?