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