GR8Conf 2011: GORM Optimization

  • 2,452 views
Uploaded on

 

More in: Business , Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
2,452
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
61
Comments
0
Likes
2

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. GORM OptimizationBurt BeckwithSpringSource CONFIDENTIAL © 2010 SpringSource, A division of VMware. All rights reserved
  • 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
  • 4. Mapping Database Views CONFIDENTIAL 4
  • 5. Database View –> Entity class Organization { String name } class UserInfo { String name String orgName } class AuthUser { String name String password Organization organization } CONFIDENTIAL 5
  • 6. Database View –> Entity 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 class UserInfo { String name String orgName static mapping = { table v_user_info } } CONFIDENTIAL 6
  • 7. 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 CONFIDENTIAL 7
  • 8. Database View –> EntityRegister a Custom Configuration in DataSource.groovy dataSource { pooled = true driverClassName = ... username = ... password = ... dialect = ... configClass = gr8conf.DdlFilterConfiguration } CONFIDENTIAL 8
  • 9. 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; } } CONFIDENTIAL 9
  • 10. Database View –> Entity grails-app/conf/hibernate/hibernate.cfg.xml <hibernate­configuration>    <session­factory>       <mapping resource=misc.mysql.innodb.hbm.xml/>       <mapping resource=misc.h2.hbm.xml/>    </session­factory> </hibernate­configuration> CONFIDENTIAL 10
  • 11. Database View –> Entity grails-app/conf/hibernate/misc.mysql.innodb.hbm.xml <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> 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 ProfitCustom 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
  • 17. Read-only Domain Classes CONFIDENTIAL 17
  • 18. Read-only Domain Classes Not 100% possible, but close enough Hibernate • Seems possible via setMutable(false) in a custom Configuration, but: • Wont block saves or deletes, only disables dirty-check updates • Does nothing with collections since theyre a PersistentSet or PersistentList and managed separately (but you wouldnt 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
  • 20. Read-only Domain Classes grails-app/conf/spring/resources.groovy 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]    } } CONFIDENTIAL 20
  • 21. Read-only Domain Classes src/groovy/gr8conf/ReadOnlyEventListener.groovy 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 } } CONFIDENTIAL 21
  • 22. Read-only Domain Classes grails-app/domain/gr8conf/LegacyData.groovy class LegacyData {    String name    static mapping = {       id column: legacy_data_id       version false    } } CONFIDENTIAL 22
  • 23. Read-only Domain Classes ReadOnlyEventListener • delete() • silently fails • Update with save() • silently fails • New instance save() • Throws exception ? CONFIDENTIAL 23
  • 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
  • 25. Monitoring CONFIDENTIAL 25
  • 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 Hugos 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
  • 28. Questions? CONFIDENTIAL 28