Advanced O/R Mapping with Glorp

35
Advanced O/R Mapping with Glorp Alan Knight ([email protected]) Cincom Systems of Canada

description

Advanced O/R Mapping with Glorp. Alan Knight . ESUG 2007, Lugano

Transcript of Advanced O/R Mapping with Glorp

Page 1: Advanced O/R Mapping with Glorp

Advanced O/R Mapping with Glorp

Alan Knight ([email protected])Cincom Systems of Canada

Page 2: Advanced O/R Mapping with Glorp

Metaphor/Motivating Example

Ruby on Rails/ActiveRecordReading Database Schema

» Some interesting mapping issuesCreating Mappings Dynamically

» Conventions/GrammarAPIs

» Querying» Globals, Singletons, Transactions

Schema EvolutionWeb Integration

Page 3: Advanced O/R Mapping with Glorp

Ruby on Rails

“Opinionated software”Rails

Go really fast, but only in one directionReaction against J2EE/.NET“Greenfield” projectsRuby-Based

Smalltalk-like, scripting languageSome very Smalltalkish “tricks” in rails

ActiveRecord pattern for persistence

Page 4: Advanced O/R Mapping with Glorp

Architecture: Glorp and ActiveRecord

Metadata vs. convention-drivenGlorp: Explicit metadata

TablesClassesDescriptors/Mappings

ActiveRecordStrict naming conventionsAware of language formsHints at the class levelCode Generation (mostly for web)

Page 5: Advanced O/R Mapping with Glorp

BrokersGlorp

Single broker (session)» Responsible for object identity» Manages automatic writes

Multiple clients use multiple sessionsIndependent of other frameworks

ActiveRecordClasses as brokers

» No object identity» Single global session

Explicit writesTightly integrated with web framework

Page 6: Advanced O/R Mapping with Glorp

Domain Model

GlorpNo metadata in domain classesPremium on flexibility, ability to optimizeExpect existing classes, schema

ActiveRecordPremium on simplicityMinimal metadata, but on domain classesMay not even be a domain model

» Use ActiveRecord directly» Instance variables as properties

Page 7: Advanced O/R Mapping with Glorp

Goal

Can we provide some of the benefits,but without losing our advantages“Hyperactive Records”

Automatic persistenceConvention-drivenBut be less restrictiveUse a bit more information (constraints)Allow a graceful transition

Page 8: Advanced O/R Mapping with Glorp

Issue: Reading Schema

Before we can automate, we need toread the database schema.A nicely recursive problem

DatabaseTable•name•constraints•fields DatabaseField

•name•isPrimaryKey•type•nullable

ForeignKeyConstraint•name•sourceFields•targetFields

Page 9: Advanced O/R Mapping with Glorp

INFORMATION_SCHEMA

Page 10: Advanced O/R Mapping with Glorp

Mapping DatabaseField

DatabaseField•name•isPrimaryKey•type•nullable

table := self tableNamed: 'columns'.(aDescriptor newMapping: DirectMapping) from: 'name' to: (table fieldNamed: 'column_name').

COLUMNS•TABLE_NAME•COLUMN_NAME•DATA_TYPE•IS_NULLABLE

Page 11: Advanced O/R Mapping with Glorp

Mapping #isPrimaryKey

ST: a boolean valueDB: primary key constraints are entitiesColumns used in a constraint are listed inkey_column_usageFor a field, do any primary key constraintsexist that make use of itMapping a two-level join to a boolean

Page 12: Advanced O/R Mapping with Glorp

Mapping #isPrimaryKey(aDescriptor newMapping: DirectMapping) from: #isPrimaryKey

to: [:each |each primaryKeyConstraints notEmpty].

Direct mapping, but to an expression“each” is the field we’re referring toprimaryKeyConstraints is anotherrelationshipnotEmpty is a subselect operation

Page 13: Advanced O/R Mapping with Glorp

SubselectsIn queries, several “collection” operationsthat turn into different kinds of subselectsisEmpty/notEmptyselect:anySatisfy:/noneSatisfy:sqlCount (also aggregation)

read: Customer where: [:each | (each orders select: [:order | order amount > 1000]) sqlCount > 5].

Page 14: Advanced O/R Mapping with Glorp

Reading Schema Summary

sourceFields and targetFields worseInformation_schema variations, limitsWorks for Oracle, Postgresql, MySQLNo changes at all to the domain model

But easier because read-onlySeveral pseudoVariables

Good motivation for automaticmapping

Page 15: Advanced O/R Mapping with Glorp

Back to ActiveRecord

Glorp metadatadefined in DescriptorSystemMethods for tables, classes, mappingE.g. #descriptorForCustomer:Lists allTables, allClasses

Page 16: Advanced O/R Mapping with Glorp

ActiveRecord DescriptorSystem

All subclasses of ActiveRecordRead allTables from the database

For each class name, find table nameFind link tables from constraints or hints

For each inst var/field name, figure outthe mapping

Naming conventionDatabase constraints

Page 17: Advanced O/R Mapping with Glorp

Aside: Inflector

Ruby on Rails classKnows basic grammar forms (English)Knows class/inst var/field/table namingand capitalization

Person class -> PEOPLE tableOVERDUE_ORDER_ID -> overdueOrder

Big ball of regular expressions

Page 18: Advanced O/R Mapping with Glorp

Aside: Hints

Ruby on Rails uses class data to tell ithow to create relationships that areambiguoushasMany, hasAndBelongsToManytableNames (added)

Page 19: Advanced O/R Mapping with Glorp

Aside: Class Generation

Generate a packageClass for each database table

FilteredEmpty descriptor system with rootclass

Page 20: Advanced O/R Mapping with Glorp

Incremental Setup

We want to do as little work asnecessaryHow to “tweak” an automaticallygenerated mapping#mappingNamed:do:

self mappingNamed: #bankCode do:[:mapping | mapping type: Integer].

Page 21: Advanced O/R Mapping with Glorp

Rails Migrations

Define tables in Ruby codeMultiple versions, ordered by namingconventionFirst version fullSubsequent versions define how toupgrade and downgrade

In the database, store a versionnumber for the schemaRun the instructions in sequence

Page 22: Advanced O/R Mapping with Glorp

Smalltalk Migrations

We have full metadata definition oftablesKeep multiple classes

Subclasses?Modify the schema to conform to thenewestPrototype level

No upgrading instructions, can lose dataJumps directly from one to the other

Page 23: Advanced O/R Mapping with Glorp

Web Integration

Equivalent to automatic web formsMagritteGlorp (Ramon Leon)

Extend Magritte with additionalinformation about relationship typesGenerate Glorp descriptors based onMagritte metadata

Page 24: Advanced O/R Mapping with Glorp

Web Integration

GlorpActiveRecordMagritteSupportMagritte metadata based on Glorp

Assume Magritte editor based on datatype

Glorp metadata based on database

Page 25: Advanced O/R Mapping with Glorp

References

GLORPhttp://www.glorp.orghttp://glorp.sourceforge.net

Ruby on Railshttp://www.rubyonrails.org/Lots of other links

Page 26: Advanced O/R Mapping with Glorp

The End

Page 27: Advanced O/R Mapping with Glorp

Subselects

In SQL terms, a nested queryMany different uses

tend to make the brain hurtGlorp provides various shortcuts forspecific Smalltalk semantics, plus ageneral mechanism

sometimes also make the brain hurtstill settling on semantics, naming

Page 28: Advanced O/R Mapping with Glorp

Subselect Example

e.g.… where: [:each | each membersanySatisfy: [:eachMember | eachMembername like: 'Alan%']].

SELECT <project fields>

FROM PROJECT t1

WHERE EXISTS (

SELECT <whatever> FROM MEMBER s1t1 WHERE

s1t1.proj_id = t1.id)

Page 29: Advanced O/R Mapping with Glorp

AggregatingTwo forms of aggregatingAt the query level

aQuery retrieve: [:each | each value sum]Puts an aggregate into the fields of the SQLSELECT ... SUM(t1.value)

Within a where clausewhere: [:each | (each value sqlSum) > 10]Creates a subselect of that aggregateSELECT ... WHERE (SELECTSUM(s1t1.value) FROM ... WHERE ...)> 10

min, max, average, count, etc.

Page 30: Advanced O/R Mapping with Glorp

Still More Aggregating

Also within a where clause expression count: [:x | x attribute]

or more generally expression

count: [:x | x attribute]

where: [:x | x something = 5].

More awkward than expression sqlCount

Not really more powerful

Page 31: Advanced O/R Mapping with Glorp

General Aggregations

General facilityread: GlorpCustomer

where: [:each | each

(each aggregate: each accounts

as: #countStar

where: [:acct | acct price > 1000]])

= 1].

Really awkwardMore general

Only requires the underlying function toexist

Page 32: Advanced O/R Mapping with Glorp

Select:

count:where: suggests a moreSmalltalk-like form

where: [:each |

(each users select: [:eachUser |

eachUser name like: 'A%])

sqlCount > 3].

Or we could apply other operationse.g. anySatisfy: to the filteredcollection.

Page 33: Advanced O/R Mapping with Glorp

Fully General Subselects

A subselect is represented by a query.aCustomer accounts

anySatisfyExists: [:eachAccount |

eachAccount in:

(Query

read: GlorpBankAccount

where: [:acct |

acct balance < 100])]].

Very general, but awkwardOften putting queries into block temps,setting retrieve: clauses, etc.

Page 34: Advanced O/R Mapping with Glorp

Correlated Subselects

Are the internal selects effectively constants,or do they refer back to things in outerqueriesSlower in database, but more powerful

read: StorePackage where: [:each |

| q |

q := Query read: StorePackage

where: [:eachPkg |

eachPkg name = each name].

q retrieve: [:x | x primaryKey max].

each username = 'aknight' & (each primaryKey= q)].

Page 35: Advanced O/R Mapping with Glorp

OK, No More Subselects

Yes, these are complicatedSometimes you need themThe tests are a good resource for codefragments to copy fromOr just experiment until you get SQL(and results) you want