Emerging Technology
Fall Semester, 2004 Glorp |
||
---|---|---|
© 2004, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 09-Nov-04 |
CS 683 Emerging Technologies Fall Semester, 2004 Doc 26 Glorp
Some Problems with Objects & Relational Databases
Copyright ©, All rights reserved. 2004 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA. OpenContent ( http://www.opencontent.org/opl.shtml) license defines the copyright on this document.
CS 683 Fall 04 | Doc 26, Glorp Slide # 2 |
Glorp web site http://glorp.org/
Glorp Tutorial, Roger Whitney
Hibernate in Action, Bauer & King, Manning, 2005
CS 683 Fall 04 | Doc 26, Glorp Slide # 3 |
CS 683 Fall 04 | Doc 26, Glorp Slide # 4 |
CS 683 Fall 04 | Doc 26, Glorp Slide # 5 |
Transaction that tracks all object read from database
Following are equivalent
session beginUnitOfWork. foundPerson := session readOneOf: Person where: [:each | each lastName = 'Chan']. foundPerson firstName: 'RamJet'. session commitUnitOfWork
session inUnitOfWorkDo: [foundPerson := session readOneOf: Person where: [:each | each lastName = 'Chan']. foundPerson firstName: 'Ramjet']
SQL executed
SELECT t1.first_name, t1.last_name FROM PEOPLE t1 WHERE (t1.last_name = 'Chan') LIMIT 1 Begin Transaction UPDATE PEOPLE SET first_name = 'Jose',last_name = 'Chan' WHERE last_name = 'Chan' (0.06 s) Commit Transaction
CS 683 Fall 04 | Doc 26, Glorp Slide # 6 |
session beginUnitOfWork. person := Person first: 'Pete' last: 'Chan'. session register: person. session commitUnitOfWork.
person := Person first: 'Pete' last: 'Chan'. session beginUnitOfWork. session register: person. session commitUnitOfWork.
SQL executed
Begin Transaction INSERT INTO PEOPLE (first_name,last_name) VALUES ('Pete','Chan') (0.019 s) Commit Transaction
CS 683 Fall 04 | Doc 26, Glorp Slide # 7 |
session inUnitOfWorkDo:
[foundPerson :=
session
readOneOf: Person
where: [:each | each lastName = 'Chan'].
session delete: foundPerson]
SQL executed
Begin Transaction DELETE FROM PEOPLE WHERE last_name = 'Chan' (0.043 s) Commit Transaction
CS 683 Fall 04 | Doc 26, Glorp Slide # 8 |
foundPerson := session readOneOf: Person where: [:each | each lastName = 'Chan']. Time passes and database may be modified by other process session refresh: foundPerson
CS 683 Fall 04 | Doc 26, Glorp Slide # 9 |
Need to register the object in the second unit of work
foundPerson := session readOneOf: Person where: [:each | each lastName = 'Chan']. Blah blah session inUnitOfWorkDo: [ session register: foundPerson. foundPerson firstName: 'Jose']
CS 683 Fall 04 | Doc 26, Glorp Slide # 10 |
Avoid doing this in GLORP
foundPerson := session readOneOf: Person where: [:each | each lastName = 'Chan'] session := GlorpSession new. session system: (PersonDescriptor forPlatform: login database). session accessor: accessor. session inUnitOfWorkDo: [ x := session refresh: foundPerson. x firstName: 'Roger']
CS 683 Fall 04 | Doc 26, Glorp Slide # 11 |
Explicitly
session beginUnitOfWork. Blah Blah session rollbackUnitOfWork.
Implicitly
session inUnitOfWorkDo: [ blah. Blah Some exception is raised blah]
CS 683 Fall 04 | Doc 26, Glorp Slide # 12 |
The goal of O/R layers is to avoid using SQL directly
login := Login new database: PostgreSQLPlatform new; username: 'usernameHere'; password: 'passwordHere'; connectString: '127.0.0.1_test'. accessor := DatabaseAccessor forLogin: login. accessor login. result := accessor basicExecuteSQLString: 'select 1 + 1'. result next first “return 2”
accessor logout.
CS 683 Fall 04 | Doc 26, Glorp Slide # 13 |
byLastName := Query readManyOf: Person. byLastName orderBy: #lastName; orderBy: #firstName. result := session execute: byLastName.
CS 683 Fall 04 | Doc 26, Glorp Slide # 14 |
Read methods in Session
readOneOf: |
readOneOf:where: |
readManyOf: |
readManyOf:where: |
readManyOf:limit: |
readManyOf:where:limit |
readManyOf always returns a collection, which may be empty
session readManyOf: Person limit: 2 session readManyOf: Person where: [:each | each firstName = 'Sam'] limit: 3
CS 683 Fall 04 | Doc 26, Glorp Slide # 15 |
sam := session readOneOf: Person where: [:each | each firstName = 'Sam']. result := session readManyOf: Person where: [:each | each <> sam] limit: 4.
CS 683 Fall 04 | Doc 26, Glorp Slide # 16 |
More operations exist, but require more detailed situations
result := session readManyOf: Person where: [:each | each id >= 1]. result := session readManyOf: Person where: [:each | each firstName like: 'S%' ]. result := session readManyOf: Person where: [:each | (each firstName like: 'S%') not ]. result := session readManyOf: Person where: [:each | each firstName notNIL].
result := session readManyOf: Person where: [:each | (each firstName like: 'S%' ) & (each lastName = 'Olson')].
CS 683 Fall 04 | Doc 26, Glorp Slide # 17 |
Example
Person with multiple email addresses
Smalltalk defineClass: #EmailAddress superclass: #{Core.Object} indexedType: #none private: false instanceVariableNames: 'userName host id ' classInstanceVariableNames: '' imports: '' category: 'GlorpExperiments'
name: aNameString host: aHostString ^(super new) userName: aNameString; host: aHostString
Plus standard accessor methods
CS 683 Fall 04 | Doc 26, Glorp Slide # 18 |
Smalltalk defineClass: #Person superclass: #{Core.Object} indexedType: #none private: false instanceVariableNames: 'firstName lastName id emailAddresses ' classInstanceVariableNames: '' imports: '' category: 'GlorpExperiments'
first: firstNameString last: lastNameString ^self new setFirst: firstNameString last: lastNameString
setFirst: firstNameString last: lastNameString firstName := firstNameString. lastName := lastNameString. emailAddresses := OrderedCollection new. addEmailAddress: anEmailAddress emailAddresses add: anEmailAddress emailAddresss ^emailAddresses
CS 683 Fall 04 | Doc 26, Glorp Slide # 19 |
Column |
Description |
id |
Primary Key for table |
user_name |
User name of the email address |
host |
The email host |
person_id |
Foreign key to person table |
Column |
Description |
id |
Primary Key for table |
first_name |
First name of person |
last_name |
Last name of person |
Id |
First_name |
Last_name |
1 |
Roger |
Whitney |
2 |
Leland |
Beck |
Id |
User_name |
Host |
Person_id |
1 |
whitney |
cs.sdsu.edu |
1 |
2 |
whitney |
rohan.sdsu.edu |
1 |
3 |
whitney |
math.sdsu.edu |
1 |
4 |
beck |
cs.sdsu.edu |
2 |
CS 683 Fall 04 | Doc 26, Glorp Slide # 20 |
Smalltalk defineClass: #GlorpTutorialDescriptor superclass: #{Glorp.DescriptorSystem} indexedType: #none private: false instanceVariableNames: '' classInstanceVariableNames: '' imports: ' Glorp.* ' category: 'GlorpExperiments'
allTableNames ^#( 'PEOPLE' 'EMAIL_ADDRESSES') constructAllClasses ^(super constructAllClasses) add: Person; add: EmailAddress; yourself classModelForEmailAddress: aClassModel aClassModel newAttributeNamed: #id. aClassModel newAttributeNamed: #userName. aClassModel newAttributeNamed: #host. classModelForPerson: aClassModel aClassModel newAttributeNamed: #id. aClassModel newAttributeNamed: #firstName. aClassModel newAttributeNamed: #lastName. aClassModel newAttributeNamed: #emailAddresses collectionOf: EmailAddress.
CS 683 Fall 04 | Doc 26, Glorp Slide # 21 |
descriptorForEmailAddress: aDescriptor | table | table := self tableNamed: 'EMAIL_ADDRESSES'. aDescriptor table: table. (aDescriptor newMapping: DirectMapping) from: #userName to: (table fieldNamed: 'user_name'). (aDescriptor newMapping: DirectMapping) from: #host to: (table fieldNamed: 'host'). (aDescriptor newMapping: DirectMapping) from: #id to: (table fieldNamed: 'id').
descriptorForPerson: aDescriptor | personTable | personTable := self tableNamed: 'PEOPLE'. aDescriptor table: personTable. (aDescriptor newMapping: DirectMapping) from: #firstName to: (personTable fieldNamed: 'first_name'). (aDescriptor newMapping: DirectMapping) from: #lastName to: (personTable fieldNamed: 'last_name'). (aDescriptor newMapping: DirectMapping) from: #id to: (personTable fieldNamed: 'id'). (aDescriptor newMapping: OneToManyMapping) attributeName: #emailAddresses; orderBy: #userName
CS 683 Fall 04 | Doc 26, Glorp Slide # 22 |
tableForEMAIL_ADDRESSES: aTable | personId | aTable createFieldNamed: 'user_name' type: (platform varChar: 50). (aTable createFieldNamed: 'host' type: (platform varChar: 50)). (aTable createFieldNamed: 'id' type: platform sequence) bePrimaryKey. personId := aTable createFieldNamed: 'person_id' type: platform int4. aTable addForeignKeyFrom: personId to: ((self tableNamed: 'PEOPLE') fieldNamed: 'id'). tableForPEOPLE: aTable (aTable createFieldNamed: 'id' type: platform sequence) bePrimaryKey. (aTable createFieldNamed: 'first_name' type: (platform varChar: 50)). (aTable createFieldNamed: 'last_name' type: (platform varChar: 50)).
CS 683 Fall 04 | Doc 26, Glorp Slide # 23 |
person := Person first: 'Sam' last: 'Whitney'. email := EmailAddress new. email host: 'cs.sdsu.edu'; userName: 'whitney'. person addEmailAddress: email. email := EmailAddress new. email host: 'rohan.sdsu.edu'; userName: 'whitney'. person addEmailAddress: email. session inUnitOfWorkDo: [session register: person].
The entire object graph is saved
CS 683 Fall 04 | Doc 26, Glorp Slide # 24 |
foundPerson := session readOneOf: Person where: [:each | each firstName = 'Sam'].
SQL executed
SELECT t1.id, t1.first_name, t1.last_name FROM PEOPLE t1 WHERE (t1.first_name = 'Sam') LIMIT 1
Note the emailAddresses are not fetched. A proxy is used. When you try to access an email address they are then fetched
foundPerson emailAddresses first
SQL executed
SELECT t1.user_name, t1.host, t1.id FROM EMAIL_ADDRESSES t1 WHERE (t1.person_id = 1) ORDER BY t1.user_name
CS 683 Fall 04 | Doc 26, Glorp Slide # 25 |
foundPerson := session readOneOf: Person where: [:person | person emailAddresses anySatisfy: [:address | address host ='cs.sdsu.edu']]
Generates & runs the SQL
SELECT t1.id, t1.first_name, t1.last_name FROM PEOPLE t1 WHERE EXISTS (SELECT t2.id FROM EMAIL_ADDRESSES t2 WHERE ((t2.host = 'cs.sdsu.edu') AND (t1.id = t2.person_id))) LIMIT 1
CS 683 Fall 04 | Doc 26, Glorp Slide # 26 |
Example
id |
title |
1 |
Code Complete |
2 |
Palm OS |
3 |
Cat in the Hat |
Id |
First_name |
Last_name |
1 |
Sam |
Hinton |
2 |
Martin |
Fowler |
customer_id |
book_id |
1 |
3 |
1 |
1 |
2 |
3 |
CS 683 Fall 04 | Doc 26, Glorp Slide # 27 |
Smalltalk defineClass: #Book superclass: #{Core.Object} indexedType: #none private: false instanceVariableNames: 'id title ' classInstanceVariableNames: '' imports: '' category: 'GlorpExperiments'
Standard accessor methods not shown
CS 683 Fall 04 | Doc 26, Glorp Slide # 28 |
Smalltalk defineClass: #Person superclass: #{Core.Object} indexedType: #none private: false instanceVariableNames: 'firstName lastName id booksOnOrder ' classInstanceVariableNames: '' imports: '' category: 'GlorpExperiments'
first: firstNameString last: lastNameString ^self new setFirst: firstNameString last: lastNameString
setFirst: firstNameString last: lastNameString firstName := firstNameString. lastName := lastNameString. booksOnOrder := OrderedCollection new. addBook: aBook booksOnOrder add: aBook books ^booksOnOrder
CS 683 Fall 04 | Doc 26, Glorp Slide # 29 |
Smalltalk defineClass: #GlorpTutorialDescriptor superclass: #{Glorp.DescriptorSystem} indexedType: #none private: false instanceVariableNames: '' classInstanceVariableNames: '' imports: ' Glorp.* ' category: 'GlorpExperiments'
allTableNames ^#( 'PEOPLE' 'BOOKS_ON_ORDER' 'BOOKS') constructAllClasses ^(super constructAllClasses) add: Person; add: Book; yourself classModelForBook: aClassModel aClassModel newAttributeNamed: #id. aClassModel newAttributeNamed: #title. classModelForPerson: aClassModel aClassModel newAttributeNamed: #id. aClassModel newAttributeNamed: #firstName. aClassModel newAttributeNamed: #lastName.
CS 683 Fall 04 | Doc 26, Glorp Slide # 30 |
descriptorForBook: aDescriptor | table | table := self tableNamed: 'BOOKS'. aDescriptor table: table. (aDescriptor newMapping: DirectMapping) from: #title to: (table fieldNamed: 'title'). (aDescriptor newMapping: DirectMapping) from: #id to: (table fieldNamed: 'id'). descriptorForPerson: aDescriptor | personTable | personTable := self tableNamed: 'PEOPLE'. aDescriptor table: personTable. (aDescriptor newMapping: DirectMapping) from: #firstName to: (personTable fieldNamed: 'first_name'). (aDescriptor newMapping: DirectMapping) from: #lastName to: (personTable fieldNamed: 'last_name'). (aDescriptor newMapping: DirectMapping) from: #id to: (personTable fieldNamed: 'id'). (aDescriptor newMapping: ManyToManyMapping) attributeName: #booksOnOrder; referenceClass: Book
CS 683 Fall 04 | Doc 26, Glorp Slide # 31 |
tableForBOOKS: aTable (aTable createFieldNamed: 'title' type: (platform varChar: 100)). (aTable createFieldNamed: 'id' type: platform sequence) bePrimaryKey tableForPEOPLE: aTable (aTable createFieldNamed: 'id' type: platform sequence) bePrimaryKey. (aTable createFieldNamed: 'first_name' type: (platform varChar: 50)). (aTable createFieldNamed: 'last_name' type: (platform varChar: 50)). tableForBOOKS_ON_ORDER: aTable | custKey bookKey | custKey := aTable createFieldNamed: 'customer_id' type: (platform int4). aTable addForeignKeyFrom: custKey to: ((self tableNamed: 'PEOPLE') fieldNamed: 'id'). bookKey := aTable createFieldNamed: 'BOOK_ID' type: (platform int4). aTable addForeignKeyFrom: bookKey to: ((self tableNamed: 'BOOKS') fieldNamed: 'id').
CS 683 Fall 04 | Doc 26, Glorp Slide # 32 |
Add some Books
session inUnitOfWorkDo: [books := #( 'Code Complete' 'Palm OS' 'Cat in the Hat' ) collect: [:each | Book title: each ]. session registerAll: books. session register: (Person first: 'Sam' last: 'Hinton'). session register: (Person first: 'Martin' last: 'Fowler')].
Read some books
cat := session readOneOf: Book where: [:each | each title = 'Cat in the Hat']. code := session readOneOf: Book where: [:each | each title = 'Code Complete'].
Order some books
session beginUnitOfWork. sam := session readOneOf: Person where: [:each | each firstName = 'Sam']. sam addBook: cat; addBook: code. martin := session readOneOf: Person where: [:each | each firstName = 'Martin']. martin addBook: cat. session commitUnitOfWork.
CS 683 Fall 04 | Doc 26, Glorp Slide # 33 |
Books ordered by Sam
sam := session readOneOf: Person where: [:each | each firstName = 'Sam']. sam books
People who ordered “Cat in the hat”
waitingForCat := session readManyOf: Person where: [:each | each booksOnOrder anySatisfy: [:book | book title like: 'Cat%']].
Copyright ©, All rights reserved.
2004 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.