Using VisualAge ObjectExtender

Markus Muetschard, Marianne Schmid, Bernd Kaponig, David Singleton

International Technical Support Organization

http://www.redbooks.ibm.com

SG24-5258-00

SG24-5258-00 International Technical Support Organization

Using VisualAge Smalltalk ObjectExtender

February 1999 Take Note! Before using this information and the product it supports, be sure to read the general information in Appendix G, “Special Notices” on page 407.

First Edition (February 1999)

This edition applies to the IBM VisualAge Smalltalk ObjectExtender Feature Version 4.5a of the IBM VisualAge Smalltalk Enterprise Version 4.5a product family.

Sample Code on the Internet! The code for the samples in this book is available as SG245258.zip on:

. ftp://www.redbooks.ibm.com/redbooks/SG245258

Download and unzip the sg245258.zip file and read the aaRead.txt file.

Comments may be addressed to: IBM Corporation, International Technical Support Organization Dept. QXXE Building 80-E2 650 Harry Road San Jose, California 95120-6099

When you send information to IBM, you grant IBM a non-exclusive right to use or distribute the information in any way it believes appropriate without incurring any obligation to you.

© Copyright International Business Machines Corporation 1999. All rights reserved Note to U.S Government Users – Documentation related to restricted rights – Use, duplication or disclosure is subject to restrictions set forth in GSA ADP Schedule Contract with IBM Corp. Contents

Figures...... xi

Tables...... xvii

Preface...... xxi How the Book Is Organized ...... xxi The Team That Wrote This Redbook ...... xxii Comments Welcome ...... xxiii

Part 1. Introductory Tour ...... 1

Chapter 1. Concepts ...... 3 1.1 Application Layers ...... 5 1.2 Persistence Layers...... 6 1.2.1 Business Objects ...... 7 1.2.2 Schemas ...... 8 1.2.3 Maps ...... 9 1.3 Transactions ...... 11 1.3.1 Shared Transaction ...... 11 1.3.2 Top-Level Transactions ...... 12 1.3.3 Nested Transactions...... 12 1.3.4 Transacted Variables ...... 13 1.4 Frameworks ...... 14 1.5 Data Abstractions...... 15 1.5.1 Model Metadata ...... 15 1.5.2 Application Scripts ...... 15 1.5.3 Application Data ...... 16 1.6 Metadata and Its Storage ...... 16

Chapter 2. Development Paths ...... 19 2.1 Forward or Top-Down: Start with the Object Model ...... 19 2.2 Backward or Bottom-Up: Start with the Database ...... 20 2.3 Outside-In: Map the Model to the Database Schema ...... 22 2.4 Whatever YOU Like ...... 22

Chapter 3. Tools ...... 23 3.1 Browsers ...... 23 3.1.1 Model Browser ...... 24 3.1.2 Schema Browser ...... 27 3.1.3 Map Browser ...... 32 3.1.4 Status Browser ...... 35

© Copyright IBM Corp. 1999 iii 3.2 System Transcript ...... 36 3.3 Smalltalk Class Editor and VisualAge Script Editor...... 37 3.4 Inspectors ...... 38

Chapter 4. Sample Scenarios ...... 41 4.1 The Employee and Department Sample Scenario ...... 41 4.2 The Library Sample Scenario ...... 42

Part 2. Your First Samples ...... 45

Chapter 5. Employee Top-Down ...... 47 5.1 Define the Model (Metadata) ...... 47 5.1.1 Create New Model ...... 47 5.1.2 Define Classes ...... 49 5.1.3 Define Attributes...... 50 5.1.4 Define Relationships...... 54 5.2 Generate Code for the Model Classes ...... 54 5.2.1 Set Model Code Generation Options ...... 54 5.2.2 Start Model Code Generation ...... 56 5.3 Generate Schema and Mapping (Metadata and Database) ...... 57 5.3.1 Generate Image Schema ...... 57 5.3.2 Verify Local Image Schema and Data Store with Scripts...... 58 5.3.3 Version, Release, and Save the Sample Work ...... 60 5.3.4 Generate Database Schema...... 61 5.3.5 Generate the DDL and the Database Tables ...... 66 5.4 Generate Service Classes ...... 69 5.4.1 Set the Service Code Generation Options ...... 69 5.4.2 Start Service Code Generation ...... 70 5.5 Activate a Data Store ...... 71 5.6 Run Sample Headless with Scripts...... 72 5.6.1 Run Scripts Stepwise ...... 73 5.6.2 Monitor Headless Sample with Status Browser...... 75 5.7 Do Some Smalltalk Inspection ...... 76 5.7.1 Use the Basic Smalltalk Inspector (#inspect) ...... 76 5.7.2 Use the ObjectExtender Enablement for the Inspectors ...... 78 5.7.3 Look at All Instances of a Class (#allInstances) ...... 79 5.8 Create Views with Transactions ...... 83 5.8.1 Use the Basic Transaction Parts...... 83 5.8.2 Use the BusinessTransaction Part ...... 90 5.9 Run Sample with User Interface ...... 96 5.9.1 Add Error Handling and Error Events ...... 97 5.9.2 Add Error Information for the User ...... 99 5.9.3 Add Database Error Handling ...... 100

iv Using VisualAge Smalltalk ObjectExtender 5.10 Stand-alone Application (GUI with Image Schema) ...... 101 5.11 Summary and Conclusions ...... 102

Chapter 6. Employee Bottom-Up...... 105 6.1 Import Schema from Relational Database (Metadata) ...... 106 6.2 Generate Model Definition and Mapping (Metadata)...... 109 6.3 Generate Code to Support Model and Services ...... 111 6.4 Run Sample Headless with Scripts...... 113 6.5 Adding the User Interface...... 115 6.5.1 Create a List View ...... 116 6.5.2 Create a Detail View...... 119 6.6 Run and Monitor the List and Detail View GUI ...... 122

Chapter 7. Employee and Department Top-Down...... 127 7.1 Define Model (Metadata) ...... 127 7.1.1 Create New Model ...... 128 7.1.2 Define Classes ...... 128 7.1.3 Define Attributes...... 128 7.1.4 Define Associations ...... 129 7.2 Generate Code for the Model Classes ...... 135 7.2.1 Generate Model Code into Different Applications ...... 135 7.2.2 Review Generated Model Class Code ...... 136 7.3 Generate Schema and Mapping: Metadata and Database ...... 138 7.3.1 Review the Schema and the Mapping...... 139 7.3.2 Generate Conforming Physical Names ...... 140 7.3.3 Match with the DB2 Sample Database (Optional) ...... 143 7.3.4 Change the Column Types ...... 143 7.3.5 Change the Key Column Types ...... 144 7.4 Generate Physical Database ...... 145 7.5 Generate Service Classes ...... 146 7.6 Run Sample Headless with Scripts...... 146 7.6.1 Set up Sample Objects ...... 148 7.6.2 Navigate the Objects ...... 154 7.7 Run Sample with User Interface ...... 161 7.7.1 Single Window GUI for Employee Creation and Maintenance . . 161 7.7.2 Convenience Attributes and Methods ...... 164 7.7.3 GUI with List and Detail Views Supporting Drag and Drop . . . . 170 7.7.4 Transaction Save GUIs...... 190

Chapter 8. Employee and Department Bottom-Up ...... 195 8.1 Import Schema from Relational Database (Metadata) ...... 196 8.1.1 Select Tables and Import Schema ...... 196 8.1.2 Review and Adjust Imported Schema ...... 197 8.2 Generate Model Definition and Mapping (Metadata)...... 201

v 8.2.1 Review and Adjust the Generated Model Definitions...... 203 8.2.2 Review and Adjust the Generated Mappings ...... 205 8.3 Generate Model Code Classes...... 209 8.4 Generate Service Classes ...... 210 8.5 Run Sample Headless with Scripts...... 211 8.5.1 Accessing the Data in the Database ...... 211 8.5.2 Navigating the Object Links ...... 211 8.6 Run Sample with User Interface ...... 214

Chapter 9. Employee and Department - Outside-In ...... 227 9.1 Preparation Work ...... 227 9.2 Create the Map ...... 227 9.3 Create Table Maps...... 228 9.4 Map Attributes to Columns ...... 229 9.5 Map Associations to Foreign Key Relationships ...... 230 9.6 Generate the Services ...... 231 9.7 Run the Sample ...... 232

Chapter 10. Summary and Conclusion ...... 237

Part 3. Advanced Details ...... 239

Chapter 11. Many-to-Many Associations ...... 241 11.1 Define the Model ...... 241 11.2 Define Schema and Mapping ...... 244 11.3 Test Sample ...... 246 11.3.1 Run Sample Headless with Scripts ...... 246 11.3.2 Hiding Implementation Details ...... 246 11.3.3 Run Sample with User Interface ...... 251

Chapter 12. Inheritance ...... 257 12.1 Single Table Inheritance Mapping ...... 258 12.1.1 Define the Model ...... 259 12.1.2 Define the Schema ...... 260 12.1.3 Define the Mapping ...... 262 12.1.4 Test Single Table Inheritance Mapping Sample ...... 264 12.2 Multiple Table Inheritance Mapping ...... 269 12.2.1 Define the Model ...... 270 12.2.2 Define the Schema ...... 270 12.2.3 Define the Mapping ...... 272 12.2.4 Test Multiple Table Inheritance Mapping Sample ...... 273

vi Using VisualAge Smalltalk ObjectExtender Chapter 13. Selective Queries or Custom Queries...... 277 13.1 Problem ...... 277 13.2 Solution ...... 278 13.3 Add a New SQL Query String to the QueryPool ...... 279 13.4 Add a New Method to the Home Collection Class...... 279 13.5 Add a Service to the Service Object ...... 280 13.6 Run Sample Headless with a Script ...... 282

Chapter 14. Nested Transactions ...... 283 14.1 Scenario for the Transaction ...... 283 14.2 The Nested Transaction Problem ...... 284 14.3 How Not to Create the Transaction Tree ...... 285 14.4 A Better Way to Create the Transaction Tree ...... 286 14.5 The Easy Way to Create a Transaction Tree ...... 296 14.6 Define Model (Metadata) ...... 298 14.7 Generate Schema (Metadata) ...... 300 14.8 Generate Mappings (Metadata) ...... 303 14.9 Generate Service Classes ...... 304 14.10 Hiding the Auxiliary CategoryLink Class ...... 305 14.10.1 Deletion of a Subcategory and Its Associated Link Object . . 305 14.10.2 Finding the Subcategories Directly ...... 306 14.10.3 Setting and Getting the Parent Category Directly ...... 306 14.10.4 Adding and Removing a Subcategory Directly ...... 307 14.11 Add Some Convenience to the Category ...... 308 14.11.1 Reference String Methods ...... 308 14.11.2 All Instances Sorted ...... 309 14.12 Run Samples Headless with Scripts ...... 309 14.13 The Category Management System Application ...... 310 14.13.1 Category List View ...... 310 14.13.2 Category Detail View ...... 317

Chapter 15. Concurrent Transactions ...... 325 15.1 Isolation Policies ...... 325 15.2 Optimistic Locking ...... 326 15.2.1 Collision Detection in the Image ...... 326 15.2.2 Collision Detection on the Back-End Data Store ...... 332 15.3 Pessimistic Locking ...... 335 15.4 Summary of Transaction Characteristics ...... 336

Chapter 16. Binary Large Objects ...... 337 16.1 Create a New Part Named GifImage ...... 337 16.2 Define the Part’s Public Interface ...... 337 16.3 Provide the Part’s Implementation ...... 338

vii Chapter 17. Lite Collections ...... 341 17.1 Define Model, Schema, and Mapping ...... 341 17.2 Create a View with a Lite Collection ...... 342 17.3 Create a View with a BLOB ...... 345

Appendixes ...... 349

Appendix A. Overview of the Examples in This Book ...... 351

Appendix B. Naming Conventions ...... 353

Appendix C. Downloadables, ConfigMaps, Applications ...... 355

Appendix D. Workspaces and System Transcript Output ...... 361 D.1 Rb20HDL.wsp - Headless Test Workspace ...... 361 D.2 Rb30DBCR.txt - DB Table Creation Report ...... 365 D.3 Rb30ObjW.wsp - System Transcript (Output Only) ...... 366

Appendix E. Library Management System ...... 377

Appendix F. Some Survival Stuff...... 389 F.1 VapModelBrowserControl>>#deleteAssociation...... 389 F.2 VapSchemaColumnEditorModel>> #areForeignKeysValid ...... 389 F.3 Required Self-Associations ...... 391 F.4 Non-Required, One-to-Xxx Association ...... 392 F.4.1 Forward: Nil-Ability ...... 393 F.4.2 Backward: Individual Access Solution (Overriding) ...... 394 F.4.3 Backward: Generic Access Solution (New Inheritance) ...... 396 F.5 SQL Execution Order in Multiple Table Inheritance Mapping ...... 398 F.6 Transaction>>#hasModifications Support...... 400 F.7 Transaction Name Form ...... 405 F.8 List Connections Utility ...... 405

Appendix G. Special Notices ...... 407

Appendix H. Related Publications...... 409 H.1 International Technical Support Organization Publications ...... 409 H.2 Redbooks on CD-ROMs ...... 409

How to Get ITSO Redbooks ...... 411 How IBM Employees Can Get ITSO Redbooks ...... 411 How Customers Can Get ITSO Redbooks...... 412 IBM Redbook Order Form ...... 413

viii Using VisualAge Smalltalk ObjectExtender Glossary ...... 415

List of Abbreviations...... 417

Index ...... 419

ITSO Redbook Evaluation...... 443

ix x Using VisualAge Smalltalk ObjectExtender Figures

1. Class Diagram for the Employee and Department Model...... 3 2. Class Diagram with All Associations and Roles Annotation ...... 4 3. Application Layers and Components ...... 5 4. Components of the Persistence Layers at Development and at Runtime ...... 6 5. Association Editor: Department-Employees Association ...... 8 6. Foreign Key Relationship Editor with workDepartment Foreign Key ...... 9 7. Property Map Editor with Class Attribute and Table Column Mapping ...... 10 8. Property Map Editor with departmentToEmployee Association ...... 10 9. A Nested Transaction Tree...... 12 10. VisualAge Smalltalk ObjectExtender: Frameworks...... 14 11. Development Paths in a Relational Database Environment ...... 21 12. VisualAge Smalltalk ObjectExtender Tools Menu...... 23 13. Model Browser: Model Rb01Employee...... 24 14. Class Editor: Class Employee...... 25 15. Attribute Editor: Attribute employeeNumber ...... 25 16. Association Editor: Association Department-Employees ...... 26 17. Schema Browser: Schema Rb01Employee01 (Part 1 of 2) ...... 27 18. Schema Browser: Schema Rb01Employee01 (Part 2 of 2) ...... 28 19. Table Editor: Employee Table...... 29 20. Column Editor: empno Column...... 30 21. Foreign Key Relationship Editor: workDepartment...... 31 22. Map Browser: Map Rb01Employee (Part 1 of 2) ...... 32 23. Map Browser: Map Rb01Employee (Part 2 of 2) ...... 33 24. Property Map Editor: Attributes...... 34 25. Map Property Editor: Associations (Relations) ...... 34 26. Status Browser: View Menu ...... 35 27. System Transcript with Log and ObjectExtender Tools Menu ...... 36 28. Smalltalk Class Editor Comment Showing the Textual Summary ...... 37 29. VisualAge Script Editor Comment Showing the Textual Summary...... 38 30. ObjectExtender Tools Menu: Enable Inspectors Choice ...... 40 31. Library Management System: Overview...... 43 32. Library Management System: Loan Management Use Cases ...... 43 33. Library Management System: Class Diagram ...... 44 34. Class Diagram Focusing on the Employee Class...... 47 35. Model Browser: Models Menu - New Model and Save Model ...... 48 36. Model Browser: Textual Summary of New (Empty) Rb20Employee Model...... 48 37. Storage Application and Class for Rb20EmployeeModel ...... 49 38. Model Browser: Classes Menu - New, Edit, and Delete Class ...... 49 39. Class Editor: New Rb20Employee Class ...... 50 40. Model Browser: Rb20Employee Model with (Empty) Employee Class...... 50

© Copyright IBM Corp. 1999 xi 41. Model Browser: Attributes Menu - New, Edit, and Delete Attribute ...... 52 42. Attribute Editor: Attribute Named birthDate of Type Date...... 52 43. Model Browser: Rb20Employee Class with Attributes ...... 53 44. Class Editor: Making empNo the Object ID of Class Rb20Employee...... 53 45. Model Code Generation Options and Class Selection Dialogs...... 54 46. Code Application and Classes for Rb20EmployeeModel ...... 56 47. Class Selection Dialog when Starting the Generation ...... 57 48. Image Schema Application and Service Classes ...... 58 49. Generated Schema Rb20Employee ...... 62 50. Table Editor: Rb20Employee ...... 63 51. Column Editor: Column empNo of Rb20Employee Table...... 64 52. Map Browser: Rb20Employee Map ...... 65 53. DDL to Create the Rb20Employee Table ...... 66 54. Database Connection Dialog ...... 67 55. System Transcript Showing Results of Creating the Database ...... 68 56. Service Code Generation Options for DB2 Database...... 69 57. Services Generated for Rb20Employee and DB2 ...... 70 58. Conceptual View of Activating a Data Store ...... 71 59. DataStore>>#activate Method ...... 72 60. Debugger Stopped on “self halt.” ...... 73 61. Result of Making a Single Employee Object Persistent ...... 74 62. First Inspection of the Attributes through the Instance Variables ...... 77 63. Inspecting an Attribute through the Getter ...... 77 64. Second Inspection of the attributes through the Instance Variables...... 78 65. Enabled Inspector Showing the Values of the Instance Variables ...... 79 66. Initial Result of Rb20Employee allInstances: No Instances ...... 80 67. Result after One Read and an Attribute Access ...... 81 68. Two Instances After an Update to an Attribute...... 81 69. Object with Original Attribute Values ...... 82 70. Object with Changed Attribute Values...... 82 71. Employee View1 Using the sharedTransaction and topLevelTransaction ...... 84 72. Parts List of the Rb20EmployeeView 1, 2 and 4, and 3 ...... 85 73. View with the sharedTransaction and topLevelTransaction Parts ...... 90 74. View with the businessTransaction Part ...... 90 75. Transaction Framework Connections of Rb20 EmployeeView 1 through 4 ...... 92 76. Public Interface Editor with the validationError Event...... 97 77. errorPrompter Properties ...... 99 78. Validation Error Dialog ...... 99 79. SQL Error Passed by the topLevelTransaction...... 100 80. Local Image Data Store Dedicated Rb20EmployeeView ...... 101 81. DB2 Sample Database: Employee Table with Columns...... 105 82. Schema Browser: Import / Export Menu...... 106 83. Select Tables Dialog: All Tables of the DB2 Sample Database ...... 107

xii Using VisualAge Smalltalk ObjectExtender 84. Imported Database Schema Rb21Employee ...... 107 85. Imported Table: Still Logical Employee and without Primary Key...... 108 86. Error message for undefined Primary Key Columns after Import ...... 109 87. Generated Model Rb21Employee...... 110 88. Generated Map Rb21EmployeeRb21employee ...... 111 89. Model Generation Options for the Rb21Employee Model ...... 112 90. Service Code Generation Options for the Rb21Employee Model...... 112 91. Database Connection Information for the Rb21Employee Model...... 113 92. Result of Inspecting Rb21employeeHome singleton allInstances ...... 114 93. Extended Trace of Rb21employeeHome allInstances ...... 114 94. List Window with Multiple Single Windows ...... 115 95. Rb21EmployeeListView ...... 116 96. Rb21EmployeeDetailView ...... 119 97. List and Detail View with the Deletion Confirmer in Action ...... 122 98. One Plus Four Object Versions of Employee 000030...... 123 99. View and Inspect Transactions and Transaction Statistics...... 124 100.Fife (1+4) Instances of Employee 000030 in allInstances ...... 125 101.Class Diagram for Employee and Department Sample ...... 127 102.Association Editor View Opens with Name Templates ...... 130 103.has Association Naming Relationships ...... 130 104.WorksIn Association Naming Relationships...... 132 105.One-to-One Association: Manages or isManagedBy ...... 133 106.Administers Association ...... 134 107.Model Browser: Department Class...... 134 108.Rb30 Sample Code Generation Options ...... 135 109.Model Classes for the Employee and Department Sample ...... 137 110.Rb30Department Class with Methods ...... 138 111.Generated Schema for Rb30EmployeeTable ...... 139 112.Generated Schema for Rb30Department Table...... 140 113.Table Column Editor and Foreign Key Relationship Editor ...... 141 114.Extended Physical Naming Support...... 142 115.Inconsistent Foreign Key Relationships ...... 145 116.Data Sample for Navigation ...... 147 117.Inspecting the TOP Department in the Debugger ...... 154 118.Access Methods Implemented by Rb30DepartmentHome ...... 155 119.Usual Access Methods of Rb30DepartmentHome...... 155 120.Access Methods of Rb30Department...... 156 121.Version V0.2... Enabled Inspector on the S1B Department ...... 160 122.Version V0.1... Enabled Inspector on the S1B Department ...... 161 123.Single Window View: Rb30EmployeeView ...... 162 124.The Read-Only referenceString Attribute in the Public Interface Editor ...... 165 125.Rb30DeaprtmentListView...... 170 126.Rb30EmployeeListView ...... 171

xiii 127.Rb99SingleReferenceForm with Drag and Drop Support ...... 172 128.Rb99SingleReferenceForm Parts List ...... 173 129.Rb30DepartmentDetailView ...... 176 130.Rb30DepartmentDetailView Parts List ...... 177 131.Rb30EmployeeDetailView ...... 180 132.Rb30EmployeeDetailView Parts List ...... 181 133.Rb30Department List and Detail View with Data ...... 189 134.Rb30Employee List and Detail Views with Data ...... 189 135.Department Detail Views after a Drag and Drop of an Employee ...... 191 136.Map Browser: Defining a Part of the Optimistic Predicate ...... 192 137.DB2 Sample Database: Employee Table with Columns ...... 195 138.Select Tables Dialog: Select Department and Employee Table...... 196 139.Imported Database Schema Rb31EmpDept ...... 197 140.Foreign Key Relationship Editor: employee_workDepartment...... 198 141.Foreign Key Relationships from Rb30EmpDept Schema ...... 199 142.Foreign Key Relationships of the Rb31EmpDept Schema...... 200 143.Generated Rb1employee Model Class ...... 201 144.Generated Rb31Employee Table Map...... 202 145.Generated Rb1department Model Class ...... 202 146.Generated Rb31Department Table Map ...... 203 147.administrativeDepartment_departments Association ...... 204 148.manager_managedDepartment Association ...... 204 149.workDepartment_employees Association ...... 205 150.Employee VapValidationErrors <>...... 206 151.Department VapValidationErrors <> ...... 206 152.Property Map Editor for Department (see Note below) ...... 207 153.Property Map Editor Open for Employee ...... 208 154.Adjusted Rb31Department Table Map ...... 209 155.Model Code Generation Options for Rb31EmpDept Model ...... 210 156.Employees of Department A00 ...... 212 157.Department D11 of Employee 000170 ...... 212 158.Manager (Employee) 000010 of Department A00 ...... 213 159.Managed Department of Employee (Manager) 000010 ...... 213 160.Rb31DeptView to Manage Departments in the Composition Editor...... 215 161.Rb31DeptView at Runtime to Manage Departments at Runtime ...... 217 162.Rb31DeptView Parts List ...... 223 163.New Datastore Map Dialog ...... 228 164.Table Map Creation Dialog...... 229 165.Property Map Editor with Map Types and Table Columns ...... 230 166.Metadata Validation Error: Column Overlap...... 230 167.Associations Property Map Editor ...... 231 168.Rb30EmpDeptRb31EmpDeptTreeView ...... 232 169.Library Management System: Title and Category Analysis View...... 241

xiv Using VisualAge Smalltalk ObjectExtender 170.Library Management System: Title and Category Design View ...... 241 171.Association between Title and TitleToCategory ...... 243 172.Association between TitleToCategory and Category ...... 243 173.Rb50TitleToCategory Class ...... 244 174.Rb50 Title2Category to Rb50Category Foreign Key Relationship ...... 245 175.Rb50TitleView ...... 251 176.Rb50TitleDetailsView in Composition Editor ...... 254 177.Library Management System: Book and Its Status ...... 257 178.Example of Three Books and Their Status Mapped into a Single Table ...... 258 179.Settings for the One-to-One Association between Book and Status ...... 259 180.Definition of Borrowed As Subclass of Status ...... 260 181.Schema for the Book-Status Example after Automatic Generation ...... 261 182.Table Map for Class Status ...... 262 183.Table Map for Class Available ...... 263 184.allInstances of the Superclass and Subclass Homes...... 266 185.Example of Three Books and Their Status Mapped to Multiple Tables ...... 269 186.Definition of the Foreign Key Relationship between Available and Status...... 271 187.Definition of the Status Table Map (Root Inheritance Table Map) ...... 272 188.Definition of the Available Table Map (Leaf Inheritance Table Map) ...... 273 189.Typical Transaction Construct ...... 284 190.Nested Transaction Views ...... 285 191.Possible Construct for Recreating the Transaction Tree ...... 287 192.View Tree with Nested Transactions at Runtime ...... 290 193.Select Transaction to Inspect ...... 291 194.Inspection of the Top-Level Transaction and Transaction Tree ...... 292 195.Rb80TransactionExample2View ...... 292 196.Typical businessTransaction Construct ...... 297 197.Class Diagram of Title-Book-Category Management System Model ...... 298 198.ER Diagram and Schema of the Title-Book-Category Management System. . . . 301 199.Rb80CategoryList1View...... 311 200.Rb80CategoryList2View and Rb80CategoryList3View Extracts ...... 312 201.Rb80CategoryDetailView in Composition Editor ...... 317 202.Rb80DetailView at Runtime ...... 318 203.featureNames of signaller in Rb80DetailView ...... 319 204.Rb90TitleView ...... 327 205.Rb90TitleDetailsView ...... 330 206.Collision Detection in Sibling Nested Transactions ...... 332 207.Definition of a Lite Collection for Rb11Employee ...... 342 208.Rb11EmployeeView Part ...... 343 209.Rb11EmployeeDetailsView ...... 347 210.Library Management System: Use Case List and Sample Use Case ...... 381 211.Library Management System: Overview Diagram ...... 385 212.Library Management System: Book Management Use Case Diagram ...... 385

xv 213.Library Management System: Customer Management Use Case Diagram . . . . 386 214.Library Management System: Book Loan Management Use Case Diagram. . . . 386 215.Library Management System: Class Diagram ...... 387 216.Zap to Enable Deletion of Unfinished Class Associations ...... 389 217.Error When Trying to Change a Key Column Type ...... 390 218.Zap to Enable Direct Foreign Key Relationship Column Type Change ...... 390 219.Key Column Type Definition Inconsistency Warning ...... 391 220.Inconsistent Foreign Key Relationships ...... 391 221.Zap for Required Self Referencing Associations (Stack OVerflow) ...... 392 222.Zap of Non-Required One-to-One Associations Model Code Generator ...... 393 223.Zap of Generated Non-Required One-to-One Associations Model Code ...... 394 224.Dubugger: Non Managing Rb30Employee>>#managedDepartment...... 394 225.Overriding #objectForForeignKey: Method Just Before Saving It ...... 395 226.Class for Method Visibility and Source, and Class for the Method Save ...... 396 227.New Part Dialog for the Rb99EnhancedRelationship Class...... 397 228.Rb99EnhancedRelationship>>#objectForForeignKey: Method ...... 397 229.Prerequisites for Rb99EmpDeptApp ...... 398 230.SQL Sequence for Multiple Table Inheritance Mapping...... 400 231.Sequence of a Modification Notification ...... 401 232.Public Interface: hasModifications Attribute of Transaction ...... 404 233.Form with Script to Display the Transaction Name ...... 405 234.DfksListConnectionsView in Composition Editor ...... 406 235.DfksListConnectionsView at Runtime Listing Its Own Connections...... 406

xvi Using VisualAge Smalltalk ObjectExtender Tables

1. Map Property Type Prefixes ...... 33 2. Model-Related Names for the Rb20Employee Sample ...... 48 3. Attributes of the Rb20Employee Class ...... 51 4. Class Hierarchy of Persistent Objects in ObjectExtender ...... 55 5. Class Hierarchy of Persistent Objects in ObjectExtender ...... 56 6. Columns for the Rb20Employee Table ...... 63 7. Connections of Rb20EmployeeView1 (Part 1 of 2) ...... 88 8. Connections of Rb20EmployeeView1 (Part 2 of 2) ...... 89 9. Connections Unique to Rb20EmployeeView2 ...... 93 10. Connections Unique to Rb20EmployeeView3 ...... 93 11. Connections Unique to Rb20EmployeeView4 ...... 94 12. Connections of Rb21EmployeeListView ...... 118 13. Connections of Rb21EmployeeDetailView...... 121 14. Attributes for the Rb30Employee Class ...... 128 15. Attributes for the Rb30Department Class ...... 129 16. VapDatabasePhysicalRules Class: Major Extending Methods ...... 142 17. Columns for the Rb30Employee Table ...... 144 18. Columns for the Rb30Department Table ...... 144 19. Connections of Rb30EmployeeView ...... 163 20. Reference String and fullName Methods of Employee ...... 166 21. Employee Modifications to Signal Changes of Derived Attributes ...... 167 22. Department referenceString Methods ...... 168 23. Department Modifications to Signal Changes of Derived Attributes ...... 169 24. UndefinedObject Extension referenceString ...... 169 25. Connections of Rb99SingleReferenceForm...... 174 26. Drag and Drop Support Events and Scripts in Rb99SingleReferencesForm...... 174 27. Promoted Features in Rb99SingleReferenceForm ...... 175 28. Rb30Department Methods for the Drag-Drop Support ...... 179 29. Connections of the Rb30DepartmentDetailView (Part 1 of 4) ...... 182 30. Connections of the Rb30DepartmentDetailView (Part 2 of 4) ...... 183 31. Connections of the Rb30DepartmentDetailView (Part 3 of 4) ...... 184 32. Connections of the Rb30DepartmentDetailView (Part 4 of 4) ...... 185 33. Connections of the Rb30EmployeeDetailView (Part 1 of 3)...... 186 34. Connections of the Rb30EmployeeDetailView (Part 2 of 3)...... 187 35. Connections of the Rb30EmployeeDetailView (Part 3 of 3)...... 188 36. Foreign Key Relationships for Rb31EmpDept Schema ...... 199 37. Reference Attributes in the Rb31department Public Interface...... 218 38. Reference Methods of Rb31department ...... 218 39. Reference Attributes in the Rb31employee Public Interface ...... 219 40. Reference Methods of Rb31employee ...... 219

© Copyright IBM Corp. 1999 xvii 41. Signalled Attributes in the Rb31department Public Interface...... 220 42. Signalled Methods in the Rb31department Class ...... 220 43. Add.../Remove... Departments Methods in Rb31department Class ...... 221 44. Add.../Remove... Employees Methods in Rb31department Class ...... 222 45. Connections of Rb31DeptView (Part 1 of 3) ...... 224 46. Connections of Rb31DeptView (Part 2 of 3) ...... 225 47. Connections of Rb31DeptView (Part 3 of 3) ...... 226 48. Map Creation Information ...... 228 49. Rb30EmpDeptRb31EmpDeptTreeView Scripts (Part 1 of 2)...... 233 50. Rb30EmpDeptRb31EmpDeptTreeView Scripts (Part 2 of 2)...... 234 51. Connections of Rb30EmpDeptRb31EmpDeptTreeView ...... 235 52. Rb50Title and Rb50Category Attributes ...... 242 53. Rb50Title, Rb50Category, and Rb50TitleToCategory Tables ...... 245 54. Connections of Rb50TitleView...... 253 55. Connections of Rb50TitleDetailsView ...... 255 56. Tables, Columns, and Attributes for Status Superclass...... 270 57. Tables, Columns, and Attributes for the Subclasses of Status ...... 271 58. Connections of a Typical Transaction Construct ...... 284 59. Main Connections of Rb80TransactionExample2View ...... 289 60. Connections of Rb80TransactionExample2View (Part 1 of 2)...... 293 61. Connection of Rb80TransactionExample2View (Part 2 of 2)...... 294 62. Transaction ID Scripts of Rb80TransactionExample2View ...... 295 63. Title Scripts of Rb80TransactionExample2View ...... 296 64. Connections of Rb80TransactionExample3View...... 297 65. Attribute Details for the Title, Category, and Book Classes ...... 299 66. Association Details for the Title, Category, and Book Classes ...... 300 67. Application Names for Rb80 Model, Schema, and Mapping ...... 300 68. Schema Definitions for Rb80 Sample Tables ...... 302 69. Foreign Key Definitions for the Rb80 Sample ...... 303 70. Mapping Details for the Rb80 Sample ...... 304 71. Connections of the Rb80CategoryList1View ...... 314 72. Connections of the Rb80CategoryList2View ...... 315 73. Connections of the Rb80CategoryList3View ...... 316 74. Connections of the Rb80CategoryDetailView (Part 1 of 4) ...... 320 75. Connections of the Rb80CategoryDetailView (Part 2 of 4) ...... 321 76. Connections of the Rb80CategoryDetailView (Part 3 of 4) ...... 322 77. Connections of the Rb80CategoryDetailView (Part 4 of 4) ...... 323 78. Connections of Rb90TitleView...... 329 79. Connections of Rb90TitleDetailsView ...... 331 80. Attributes of Class Rb11Employee ...... 341 81. Connections of Rb11EmployeeView ...... 344 82. Connections of Rb11EmployeeDetailsView...... 346 83. Overview of Sample Applications in This Book ...... 351

xviii Using VisualAge Smalltalk ObjectExtender 84. Naming Conventions ...... 353 85. Downloadable File RBOE.zip and Its Contents ...... 356 86. Application Configuration Maps ...... 357 87. Application Utility Configuration Maps ...... 357 88. Import/Export Rb00ConfigMap with All Application (Part 1 of 3) ...... 358 89. Import/Export Rb00ConfigMap with All Application (Part 2 of 3) ...... 359 90. Import/Export Rb00ConfigMap with All Application (Part 3 of 3) ...... 360 91. Import/Export Rb01ConfigMap with ObjectExtender Modifications) ...... 360 92. Library Management System: Requirements...... 378 93. Library Management System: Concepts 1...... 378 94. Library Management System: Concepts 2...... 379 95. Library Management System: Actors...... 380 96. Library Management System: Main Use Cases with Hyper Link Marks ...... 382 97. Library Management System: Extending and Used Use Cases 1 with Link Marks . . 383 98. Library Management System: Extending and Used Use Cases 2 with Link Marks . . 384 99. New Transaction Methods to Notify Modifications ...... 402 100.New and Modified TransactionView Methods to Notify Modifications ...... 402 101.Modified Version Method to Notify Modifications...... 402 102.New and Modified BusinessTransaction Methods to Notify Modifications ...... 403 103.New and Modified TransactionView Methods to Notify Modifications ...... 404

xix xx Using VisualAge Smalltalk ObjectExtender Preface

VisualAge Smalltalk ObjectExtender is the transaction and persistence framework feature of the IBM VisualAge Smalltalk Enterprise product family. ObjectExtender is the sister component to the Persistence Builder of the VisualAge for Java product.

ObjectExtender enables your object models to persist in relational data stores as well as provides linkages to legacy data systems. Creating the persistence layer is accomplished using the ObjectExtender tool set. The tool set helps you describe the business objects in your model that will persist in a data store. The tools generate the supporting code that services your persistent business as well as data definition language for relational databases to create the tables.

ObjectExtender supports various development paths—from the model top-down to the database, from the database bottom-up to the model, and mapping a model to a database schema—with the options to mix full generation, partial generation, and manual entered definitions for the components along the path.

This redbook is intended for analysts, designers, software developers, application experts, and technical project managers who want to become familiar with ObjectExtender. A basic understanding of object and relational technology is assumed. All developed sample applications and test scripts are available as downloadable sources.

The code for the samples in this book is available as SG245258.zip on: ftp://www.redbooks.ibm.com/redbooks/SG245258

How the Book Is Organized

The book is divided into three parts:

Part 1 contains an introduction to ObjectExtender transaction and persistence framework, describes the concepts and functions of the tool including its look and feel, and provides a general understanding and work path guidelines on how to use the tool to go through the development process. Part 1 also includes the introduction to the problem domains used for the samples in this redbook.

Part 2 contains five samples—your first samples—a step by step, simple to challenging hands-on opportunity for you. After practicing through the samples you have a sound understanding how ObjectExtender can solve the

© Copyright IBM Corp. 1999 xxi persistence and transaction problems in your VisualAge Smalltalk applications.

Part 3 contains advanced details, such as how to use ObjectExtender to handle mass data problems, more complex object models, nested and concurrent transactions, and special data, such as binary large objects.

The appendixes contain overview about the samples, the naming conventions, the downloadables. They also include some workspaces, some sample system transcript output, and some survivals.

The Team That Wrote This Redbook

This redbook was produced by a team of specialists from around the world working at the International Technical Support Organization San Jose Center.

Markus Muetschard is an IT and Object-Oriented Consultant at the IBM International Technical Support Organization, San Jose Center. He writes extensively and teaches IBM classes worldwide on all areas of object-oriented technology, with a focus on the VisualAge (Smalltalk) product family. Before joining the ITSO, Markus worked in the IT Consulting department of IBM Switzerland, consulting and mentoring for object-oriented customer projects.

Marianne Schmid is project leader and developer at Credit Suisse and at the STC of OTI (Software Technology Center of Object Technology International Inc. Ottawa, Canada) in Zurich, Switzerland. She has over 15 years of experience in banking and information technology for banking. Marianne’s areas of expertise include analysis, design, and implementation of object-oriented applications and frameworks with legacy system integration.

Bernd Kaponig is an IT Architect with IBM’s Object Technology Practice in Austria. He has 14 years of experience in the software development field. He holds a Master of Science degree in Computer Science from Technology University Vienna. His areas of expertise include object-oriented analysis, design and implementation, as well as distributed object architectures.

David Singleton is a Software Engineer in Germany. David has over 30 years of experience in computing, in a variety of fields. David currently works for LogicLine Gmbh, an IBM partner firm near Stuttgart in Germany. His areas of expertise include real time systems, database systems and object orientation. David has had articles published on the application of object oriented techniques in C++. David has a Master of Science degree in Computer Science from Imperial College, London, Great Britain.

xxii Using VisualAge Smalltalk ObjectExtender Thanks to the following people for their invaluable contributions to this project:

Joe R. Winchester, Dibbe A. Edwards, and Brian Lang of IBM VisualAge Development in the RTP (Research Triangle Park), Raleigh, NC, USA

Elsa Barron, Joan Bow, Mary Comianos, Maggie Cutler, Shirley Hentzell, Jean Gedeon, Pat McCarthy, Ueli Wahli, and Laymond M. Pon of the International Technical Support Organization, San Jose Center.

Comments Welcome

Your comments are important to us!

We want our redbooks to be as helpful as possible. Please send us your comments about this or other redbooks in one of the following ways: • Fax the evaluation form found in “ITSO Redbook Evaluation” on page 443 to the fax number shown on the form. • Use the electronic evaluation form found on the Redbooks Web sites: For Internet users http://www.redbooks.ibm.com For IBM Intranet users http://w3.itso.ibm.com • Send us a note at the following address: [email protected]

xxiii xxiv Using VisualAge Smalltalk ObjectExtender Part 1. Introductory Tour

© Copyright IBM Corp. 1999 1 2 Using VisualAge Smalltalk ObjectExtender Chapter 1. Concepts

This chapter describes the VisualAge Smalltalk ObjectExtender concepts, frameworks, and tool components that you need to know to use the product. In describing concepts and frameworks, it is often necessary to talk in somewhat theoretical terms. To avoid this, we use a sample application, the well-known DB2 Employee sample database. We use this sample in part 1 of the book to illustrate the concepts, tools, and development scenarios. In part 2 we develop same sample applications, using ObjectExtender and the Employee database.

In the Employee database, a company administrator maintains data about employees and departments. Employees have a name, phone number, work department, employee number, and other attributes. Departments have a name, a manager, an administrative department (organizational hierarchy), a department number, and other attributes.

Looking at this sample in an object-oriented way, there are two objects, the Employee and the Department. These objects are linked to one another because it is necessary to know: • Who works in which department? • Which departments are administered by other departments? • Which employee manages a department, and which department is it?

Figure 1 shows a class diagram for this employee-department modeled in Unified Modeling Language (UML) notation.

isAdministeredBy >

1 Department Employee * departmentNumber employeeNumber name < worksIn name manager * 1 phoneNumber workDepartment adminDepartment isManagedBy > ...... 0..1 0..1

Figure 1. Class Diagram for the Employee and Department Model

© Copyright IBM Corp. 1999 3 Note that the association names and their direction in the class diagram are somewhat biased: they remind one of the relations and foreign keys in the relational Employee database sample. However, with association names in both directions and the roles annotated, the bias does not matter (Figure 2).

isAdministeredBy > < administers

departments 1 Department Employee departmentNumber employeeNumber * name name administrative manager Department phoneNumber adminDepartment workDepartment ...... < worksIn

* has > 1 work employees Department

isManagedBy > 0..1< manages 0..1 managed manager Department

Figure 2. Class Diagram with All Associations and Roles Annotation

4 Using VisualAge Smalltalk ObjectExtender 1.1 Application Layers ObjectExtender separates the task of implementing a system into three application layers (Figure 3): • Application programming. This layer is primarily concerned with the user interface and with the management of updates as complete and consistent units of work (or transactions). • Object modeling. This layer is primarily concerned with the definition and implementation of business objects and the relationships among these business objects. • Data access programming. This layer is primarily concerned with the storage and retrieval of data in the underlying database. Together with the object modeling layer, this layer handles the mapping between business objects and database tables. Often, particularly where a new front end is being implemented for a legacy database, there is no one-to-one mapping between the business objects and the related database tables.

Dialogs Application Programming Transactions

Object Business Modeling Objects

Services Data Access Programming Data Store

Figure 3. Application Layers and Components

Concepts 5 1.2 Persistence Layers ObjectExtender provides a full set of tools to support the development of the mappings between business objects and the underlying database, so that the business object storage can be made persistent. In the development environment, ObjectExtender provides a set of browser tools that allow you to develop object models and create mappings to the underlying database schema. Figure 4 illustrates the persistence layers, the browsers used to manage the metadata at development time, and the generated classes at runtime.

At the bottom of the runtime environment stack is the services layer. This layer is responsible for establishing the connection with the physical database. We discuss it in more detail in 1.5, “Data Abstractions” on page 15. In the sections that follow we explain how a business object can be mapped to a schema.

Development Environment Runtime Environment Browsers Models Classes and Instances Transactions with Object Versions

Object Business Meta Model Objects and Model Relationships

Home Collections

Map Maps

Data- Services Schema base Schema

Figure 4. Components of the Persistence Layers at Development and at Runtime

6 Using VisualAge Smalltalk ObjectExtender 1.2.1 Business Objects The simple example has only two objects and therefore two classes: Employee and Department.

For the purposes of this discussion, we consider only a subset of the Employee class, with the following significant attributes: • employeeNumber, for employee number • lastName, for last name or name

Observant readers will notice that one attribute is apparently missing from the simple list of attributes: the attribute that indicates in which department this employee works (workDepartment).This is a deliberate omission because workDepartment is not an attribute of the Employee class. Instead, it is a link (association in UML terms) between the Employee class and the Department class, supporting the worksIn association shown in Figure 2 on page 4.

If you want to try this, use the ObjectExtender Model Browser and it’s tools to create the object model, create the classes called Employee and Department, and add the attributes and associations. In the model, the association worksIn respectively has in Figure 2 on page 4 is now named Department-Employees, because it is a one-to-many association between these two classes, as shown in Figure 5.

Notice that the related roles, workDepartment and employees are preserved and used to describe the same roles of the Department for the Employee and vice versa.

Observant readers will also notice that the association is navigable in both directions. It is possible to find the employees in a particular department from that Department object. Similarly, one can identify the department in which an employee works from the relevant Employee object. You should distinguish between this object-oriented view of the Employee sample and the relational view of that same sample. In the relational view, you cannot find out which employees belong to a department by asking the Department table. Instead, you have to query the Employee table as well.

There is also another association between the Department class and the Employee class, namely, isManagedBy, which will now be named Department-Employee, indicating a one-to-one association. If the class names do not adequately specify the roles, or there are multiple associations between two classes and the Classname1-Classname2 naming pattern creates the same or indistinct names for them, consider why the Role1-Role2

Concepts 7 naming pattern or a combination of both. The association names would read: Department-Employees and Department-Manager or WorkDepartment-OccupiedEmployees and ManagedDepartment-ManagingEmployee.

Figure 5. Association Editor: Department-Employees Association

Adding the remaining attributes for the Employee class (for example, birthDate, phoneNumber) is a simple graphical exercise left for Part 2, “Your First Samples” on page 45 of this book.

1.2.2 Schemas A physical database with tables (for a relational database) is required to support the object model. If the application was developed completely from scratch, the ObjectExtender schema generation of the Schema Browser with tools, could be used to create a schema. ObjectExtender creates the necessary data definition language (DDL) for the database or creates the tables, indices, and constraints directly from the schema.

Here, this is not necessary because the DB2 SAMPLE database is supplied with DB2. The ObjectExtender Schema Browser can therefore be used to interrogate DB2 for the tables to reverse-engineer a schema definition for use by ObjectExtender. Because the database does not include the necessary

8 Using VisualAge Smalltalk ObjectExtender constraints, you have to add foreign key definitions. Figure 6 shows the relevant definition for the worksIn relationship.

Figure 6. Foreign Key Relationship Editor with workDepartment Foreign Key

1.2.3 Maps If a database schema for a new application is generated directly from the object model, ObjectExtender automatically generates the necessary mappings between the business objects and the database tables. As it is, existing database tables are available and have been used to generate an ObjectExtender schema from the tables. Mappings must be created between the business objects and the database tables.

ObjectExtender also supplies a Map Browser to help with this task. The Map Browser with tools is used to create mappings between class attributes and table columns, as shown in Figure 7.

Class associations must be mapped to table foreign keys, as illustrated in Figure 8.

Concepts 9 Figure 7. Property Map Editor with Class Attribute and Table Column Mapping

Figure 8. Property Map Editor with departmentToEmployee Association

The managedDept association is not mapped to a column in the Employee table because the table contains no information that says which (if any), department the employee manages. This information is supplied through the Employee object by the EmployeeToDepartmentRelationship and is derived from the manager column in the Department table.

10 Using VisualAge Smalltalk ObjectExtender 1.3 Transactions As an environment for developing data processing applications, ObjectExtender naturally supports the concept of the transaction. It supports a number of different types of transaction, as described below. With the exception of the global shared transaction, all transactions must be owned by a parent transaction.

The ObjectExtender transaction supports a variety of synchronization and collision or conflict management schemas.

Synchronization between application and data memory spaces is achieved by various synchronization schemes. These schemes define when modified objects in the application memory are sent to the database and when objects are refreshed from the database. For example, a deferred write schema would imply that modified objects are first recorded within a transaction. When the transaction is committed, the modified objects are automatically written to the database all at once.

Collision management schemes provide different approaches according to the different types of transactions defined. Transactions with a high penalty for failure, for example, could have a pessimistic collision prevention scheme, whereas transactions with a low penalty for failure—that is, where it is worth the risk of failure to gain the efficiency—could have an optimistic collision detection scheme. A flexible collision management scheme is based on the properties of the transaction, the domain class, and the data store. For example, within a transaction there may be participating objects that are not ycandidates for collision. When the transaction directs its participants to their resources, the objects that are not collision candidates do nothing because they have no resources that require locking. The net effect of collision management strategies depends entirely on what the underlying data store supports.

1.3.1 Shared Transaction ObjectExtender requires that every VisualAge application has a minimum of one transaction active at any one time. To ensure that this is always the case, ObjectExtender implements the global shared transaction. This transaction is created when a VisualAge application is started and remains in existence until the application finishes. The shared transaction has one restriction that other transactions lack: it is read-only.

Concepts 11 If your only aim when developing an application is to browse data (that is, no updates, inserts, or deletes), the shared transaction supplies all the transaction functionality that you need.

The shared transaction also acts as the parent transaction for all top-level transactions.

1.3.2 Top-Level Transactions Top-level transactions are always child transactions of the global shared transaction. They supply all of the functionality required for simple interactions with the database involving browsing, inserts, updates, and deletes. A number of top-level transactions can be active at any one time.

1.3.3 Nested Transactions It is sometimes necessary to divide one overall transaction down into a number of subparts. For example, as shown in Figure 9, a transaction consists of three subtransactions A, B, and C. Subtransaction A consists of two subparts, A1 and A2. This pattern of top-level transactions and multiple level subtransactions is called a nested transaction.

Top-Level Transaction

Subtransaction ASubtransaction B Subtransaction C

Subtransaction A1 Subtransaction A2

Figure 9. A Nested Transaction Tree

The transaction has been divided into parts because the design requires that it be possible to roll back individual subtransactions without having to go back to the beginning of the transaction. Dividing of transactions may be driven by user interface considerations. For example, say a user has to enter a lot of information on a number of screens to complete a new customer entry. If that user makes a mistake partway through, it is cumbersome to have to go back to the very beginning to correct the error.

12 Using VisualAge Smalltalk ObjectExtender 1.3.4 Transacted Variables At any point in time, it is possible to have several transactions in existence. However, only one of these transactions is the current transaction. ObjectExtender provides facilities to allow switching between transactions. For variables holding a transient (non-persistent) object, this does not present a problem. However, for variables holding a persistent object, where changes to the object through the variable can be committed or rolled back, it is important to know which transaction is associated with the object and the variable so that the commitment or rollback can be correctly handled.

It is therefore possible, in advanced scenarios such as running parallel transactions, that the normal association between a variable and a transaction may be lost. ObjectExtender uses the current transaction as the context for changing an object through a variable. If the variable was actually associated with another transaction, the object will be altered in the context of the wrong transaction. Actually the wrong object is changed, because each transaction creates and keeps a version of each object changed in its context—its associated object version.

ObjectExtender provides, through the transacted variable, a mechanism for explicitly ensuring that a transacted variable is associated with a specific transaction, and also access to the object in the transacted variable. Whenever a change is made to an object through such a transacted variable, the change is made only within the context of the associated transaction.

On a rollback of a transaction, each associated object version is discarded. On a commit of a transaction, the changes in each associated object version are merged into the object version, from where it was derived. Therefore, the changes in the committed transaction are merged to the parent transaction. If the object version is the version read from the persistence, and so the associated transaction is the top-level transaction, the objects become the new persistent version and are written to the persistent storage.

ObjectExtender detects collisions, also called conflicts, while merging changes into higher object versions or transaction levels as well as to persistent storage. A collision occurs, if a second merge to the same level of object version and transaction from a logically lower level is initiated. Merge policies can be defined that ObjectExtender can handle collisions automatically or invoke application-defined code that handles the collision. Unhandled collisions roll back the transaction that tries to commit.

Concepts 13 1.4 Frameworks ObjectExtender is a framework of frameworks, developed to reduce the amount of code that application developers subsequently need to produce. The ObjectExtender framework consists of the frameworks shown in Figure 10.

ObjectExtender Framework

Modeling Framework Relationship Framework

Transaction Framework

Mapping Framework Data Store Framework

Figure 10. VisualAge Smalltalk ObjectExtender: Frameworks

Each of these frameworks addresses a different part of the development task: • Modeling framework. The modeling framework, supported by the Model Browser, allows you to build up the definition of an object model interactively, specifying the object classes in the model, the attributes of each class, and the associations among the various classes. The framework then provides tools for automatic code generation (and re-generation after amendment of the model). The generated code provides places for you to insert code for special processing, such as business rule validation, and preserves such code should the automatic code regeneration be invoked again. • Relationship framework. In any real-world application, the classes in the object model are related to one another. The ObjectExtender relationship framework, supported by all three browsers, allows you to specify the relationships between objects and the foreign key relationships between the tables in the underlying data store. Code generation to allow navigation along the relationship paths is provided.

14 Using VisualAge Smalltalk ObjectExtender • Transaction framework. ObjectExtender provides a set of transaction objects to control access to the underlying data store to ensure that all changes to the data store are applied in a consistent and controlled manner. • Mapping framework. Once the object model and the underlying data schema are defined, it is necessary to map the one to the other. The mapping framework, supported by the Mapping Browser, enables you to do that. • Data Store framework. The data store framework, supported by the Schema Browser, allows you to build definitions of the underlying physical data store. If a new data store is being developed, the framework generates the requisite DDL to build the required data store. If a legacy relational data store is available, the framework can interrogate the data store to build the requisite ObjectExtender view of the data store.

1.5 Data Abstractions ObjectExtender holds information about the application in a number of levels, each representing a different level of abstraction of the overall model design and implementation. It is important that you are aware of these levels and can distinguish among them. We summarize them below:

1.5.1 Model Metadata We call the data that ObjectExtender needs to store about the design of a particular application metadata, to distinguish it from the application data (such as the data stored in the Employee or Department tables) that the application must process to carry out its normal function.

The metadata is stored as part of the application in the application image. It is not available to the developed application at runtime.

1.5.2 Application Scripts ObjectExtender uses the model metadata to generate real model application scripts (code). These scripts are also stored as part of the application in the application image. They are executed at runtime to process the application data.

You can extend the generated scripts to add to or change the generated functionality.

Concepts 15 1.5.3 Application Data The data on which the application operates can be stored in a number of ways, depending on application design considerations. For example, the data can be stored in a (legacy) database, for which no Smalltalk binding exists, or the access has to go through (legacy) non-Smalltalk access modules. In this case, it may be necessary to develop special-purpose code in the services layer to call the database application programming interfaces (APIs) or the access modules directly. ObjectExtender generates the Smalltalk stubs to fill in the special-purpose code as well as their invocation in the framework.

If the database has an open data base connectivity (ODBC) driver, standard ODBC calls are inserted to access the database when the services layer is generated. Similarly, ObjectExtender can automatically generate code to access an IBM DB2 database through the command language interface (CLI).

Finally, it is possible to hold the application data as persistent data within the local image. This is useful for small stand-alone applications and for application development. The same model is used whether the connection is with a database or with the local image, so the application design and code are unaffected. Only the generated services need to be swapped to use either the database or the local image data store. The local image data store service object has a protocol to save the data store to a file, load it from a file, and reset (empty) it—invoked from the status tool or Smalltalk code.

1.6 Metadata and Its Storage The three main pieces that are defined to support a Object Extender application are the model, the schema, and the data store map. The information for each of these is stored in a class in the development repository. When a model, schema, or map is first created, you can edit without having to save it. In this case all changes are recorded in the image and are only available to you in your image. To save the work and make it available to other users, use the Save option from each of the browsers. This option stores the details of the model, map, or schema in a class in the development repository to which the image is attached. If a model, map, or schema requires saving, the browser indicates this in a text pane on the lower half of the window.

After a model, map, or schema has been saved, it can be loaded by other users into their images by loading the storage class in the same way any other Smalltalk class is loaded from the repository into the image. (To load a class into the image, it must be a version, or if it is still an edition, the user of the image must be the developer of the edition.)

16 Using VisualAge Smalltalk ObjectExtender After loading the classes that hold the metadata information for a model, map, or schema into the image, the browsers are not automatically refreshed. You have to use the Load Available menu option. This option will hydrate the models, maps, or schema in the image from the list of loaded classes that are holding the metadata information. As you work with the model, map, or schema in the browser, the changes are recorded locally in your image. The browser signals that the class is out of step with the image by indicating that it is dirty and requires saving. If you decide not to save the changes you can revert the model, map, or schema to the version last saved to the storage class by using the Revert menu option. To store your work in the development repository, use the Save menu option to take the definitions (which are always the definitions the browsers show) of the model, map, or schema from your image and store them in the repository. From here, you can version the classes and applications, using the VisualAge Organizer or one of the other development repository browsers, and then another user can load and work on them.

It is good practice to version the class containing the metadata for the model, map, or schema before another user loads it into his or her image. Once the storage class has been versioned, it can be moved between development repositories, using the Import and Export menu options. When the class is exported, it takes the metadata with it, allowing ObjectExtender models, maps, and schemas to be moved between repositories.

Concepts 17 18 Using VisualAge Smalltalk ObjectExtender Chapter 2. Development Paths

The development paths you decide to use depend on whether you develop your application from scratch or use legacy (data access) code or existing (relational) databases. Figure 11 gives an overview of the three typical development paths in a relational database environment: • Forward or top-down: Start with the model • Backward or bottom-up: Start with the (legacy) database • Outside-in: Map the object model to the database schema

2.1 Forward or Top-Down: Start with the Object Model The forward or top-down path is the typical path for developing an application from scratch (Figure 11 on page 21).

The first step is to define the model by the metamodel. From then on everything else is just a matter of clicking a button: generating the business object classes, the database schema, mapping, and service classes and creating the database. You can control every generation step and make adjustments to the generation options as well as to the results created by ObjectExtender.

For situations beyond average requirements, such as complex models, special inheritance implementations, or tuned database access, the forward or top-down path is still the most appropriate. Even if you have to attach the legacy (data access) code and database, the generated results up to the schema are a good basis for starting with the changes and adjustments.

Currently, the metamodel has to be entered manually in the model definition support of ObjectExtender. Import or capturing support will be available soon: a bridge to VisualAge (Smalltalk) UML Designer, IBM’s object-oriented modeling and design tool, supports the import of object models and designs into ObjectExtender.

The forward or top-down path is also typical for prove of concept, prototypes, rapid application development (RAD), "check-outs," and initial versions of an applications. The operational and subsequent versions and extensions of the applications will then be developed along the path described in 2.3, “Outside-In: Map the Model to the Database Schema” on page 22. This path guarantees the option for a clean object-oriented implementation of the business model as well as for implementing state-of-the art database implementations.

© Copyright IBM Corp. 1999 19 For prototyping (and RAD) ObjectExtender offers an even faster option than automatic generation of the schema, map, and database: the image schema and image data store generation feature. With this feature all you have to do to get ready for testing or showing a persistent model at live is define the model. Generating the model classes and the image schema from the model definition is just a matter of clicking a button.

The generation of the image schema from the model definition generates only a data store, no schema, no map, and no service classes. The data store is kept in the image as a local image data store and can be saved as a binary object dump to a file and later reloaded. Therefore, using the image schema generation feature of ObjectExtender does not require a database system to be installed. The feature is also very convenient for rerunning scenarios with prototypes for demonstration purposes. (You could even imagine a stand-alone, single user "application" using the image schema and image data store as a valid option....) The image schema generation feature is covered in 5.3.1, “Generate Image Schema” on page 57.

2.2 Backward or Bottom-Up: Start with the Database The backward or bottom-up path (Figure 11) is typical for a new application or application extension based on a given database.

There is no focus on real object-oriented modeling of the business, just easy and fast usage of the given database in the object oriented VisualAge development and deployment environment.

The first step is to capture the existing database implementation as a database schema in ObjectExtender. From then on all you have to do is click a button: generating a metamodel and mapping, business classes, and service classes. You can control every capture or generation step and make adjustments to the options as well as to the results produced by ObjectExtender.

For situations beyond average requirements, such as complex table dependencies, absence of reference definition in the database, (performance related) redundancies in the tables, the captured and generated results require manual changes and adjustments.

ObjectExtender supports capturing the database implementation by importing the schema from the database.

20 Using VisualAge Smalltalk ObjectExtender Development Paths (in Relational DB Environment)

Meta Model Mappings Database Service Database Model Classes (Attr/Assoc) Schema Classes (DDL) create Table... B RT B B RT RT

(1) Forward or Top-Down: Start with the Model Define Obj Generate Generate Model Model Classes Service Classes

Generate Schema from Model Export Schema to DB (Mapping is generated too) (Generate DB, Tables,...)

(2) Backward or Bottom-Up: Start with the (Legacy) Database Generate Model from Schema Import Schema to DB Implement (Mapping is generated too) (Generate Schema from DB) Database

Generate Generate Model Classes Service Classes

(3) Outside-In: Map the Model to the Database Define Obj Map the Model to the Schema or vice versa Import Schema to DB Implement Modeling (Generate Schema from DB) Database

Generate Generate Model Classes Service Classes

Runtime RT Component ObjectExtender Automated Tasks Manual Tasks Tasks Done in/by Development (Generation/Import/Export/...) in ObjectExtender Some Other Tool(s) B Comp. in Browser

Figure 11. Development Paths in a Relational Database Environment

Development Paths 21 2.3 Outside-In: Map the Model to the Database Schema The outside-in or mapping path offers the best options—clean object-oriented modeling and freedom in implementing the database—and is a combination of the top-down and bottom-up paths (Figure 11).

Mapping may become difficult, however: What do you do if you cannot find a reasonable mapping? You go back to your business object modeler and database designer to discuss alternatives, for which you can find reasonable mappings. If the database is given and already heavily used in other databases, an adaptive change of the database is not an option. The changes have to be made in the object model, the mapping, and the services. Changes in the object model and in the service requires some dedicated coding.

The Outside-In or mapping path is the best match for maintenance. Changes to an application require consideration on both ends, the business model end and the database end. Work must to be performed cautiously in a cooperative manner among business modelers, object modelers, implementers, and database designers and administrators. The model, schema, and map browser and editor facilities of ObjectExtender support implementers in making valuable proposals and doing the proof of concept of designs. The browsers provide nice and compact textual summaries for proposals and documentation (see 3.1, “Browsers” on page 23).

2.4 Whatever YOU Like You can still go with the easy paths, but you must do the tasks step by step and manually. Running the tests manually gives you the most freedom in naming when defining the elements. ObjectExtender does its best to generate reasonable names. But you have to set up your conventions and adhere to them.

22 Using VisualAge Smalltalk ObjectExtender Chapter 3. Tools

The VisualAge Smalltalk ObjectExtender tools are started in the System Transcript window through the ObectExtender Tools menu (Figure 12) as well as in almost every tool window.

Figure 12. VisualAge Smalltalk ObjectExtender Tools Menu

3.1 Browsers In this section we provide a brief, pictorial overview of the browsers available with ObjectExtender. For further details see the VisualAge Smalltalk ObjectExtender User’s Guide and Reference.

© Copyright IBM Corp. 1999 23 3.1.1 Model Browser The Model Browser (Figure 13) is used to create, select, and define a model. The text pane of the Model Browser shows a textual summary of the recently selected model element. The textual summary is visible in other tools also (see 3.3, “Smalltalk Class Editor and VisualAge Script Editor” on page 37).

Figure 13. Model Browser: Model Rb01Employee

The model definition process is supported by the Class Editor (Figure 14 on page 25), Attribute Editor (Figure 15 on page 25), and Association Editor (Figure 16 on page 26), which you use to define new classes and edit existing

24 Using VisualAge Smalltalk ObjectExtender classes. Class attributes can be added, amended, or deleted. Associations between different classes can be defined.

Figure 14. Class Editor: Class Employee

Figure 15. Attribute Editor: Attribute employeeNumber

Tools 25 Figure 16. Association Editor: Association Department-Employees

Of note is the Lite Collections tab in the Class Editor (Figure 14 on page 25). Lite Collections are useful for retrieving a subset of the information from a particular object in the database without completely instantiating each object that is retrieved. This feature can improve performance when you retrieve large sets and sets of heavy rows (that is, rows with a lot of data). For further details about this feature, see the ObjectExtender User’s Guide and Reference and Chapter 17, “Lite Collections” on page 341.

26 Using VisualAge Smalltalk ObjectExtender 3.1.2 Schema Browser The Schema Browser (Figure 17) is used to define the underlying physical data store.

Figure 17. Schema Browser: Schema Rb01Employee01 (Part 1 of 2)

Tools 27 Figure 18. Schema Browser: Schema Rb01Employee01 (Part 2 of 2)

The Table Editor (Figure 19) and Column Editor (Figure 20 on page 30) provide facilities to add, delete, and edit column definitions in the table.

Use Foreign Key Relationship Editor (Figure 21 on page 31) to define relationships between database tables. You specify which other tables provide foreign keys for the table and which columns in the table are foreign keys.

The various dialogs making up the Schema Browser provide entry fields for both the logical and physical names of tables, columns within tables, and foreign key relationships. The logical names should be chosen to be meaningful within the context of the application. If no physical names are entered, ObjectExtender uses the logical names as the physical names.

28 Using VisualAge Smalltalk ObjectExtender Note: When you enter physical names for your tables, columns, and foreign keys, you must take into account any limitations of the underlying database. For example, the DB2 database currently limits table and column names to 18 characters.

If you let ObjectExtender derive the physical names from the logical names, ObjectExtender makes useful truncations and solves duplicates by adding numbers. The maximum name length value can be set by executing the following Smalltalk code: VapDatabasePhysicalRules maximumLength: 18.

Further customization of building physical names can be achieved through subclassing the VapDatabasePhysicalRules class.

Figure 19. Table Editor: Employee Table

Tools 29 Figure 20. Column Editor: empno Column

Of note is the Converter of Type Details in Column Editor (Figure 20). A Converter defines how a column value is converted to an object and vice versa. For most column types the VapConverter default works fine, because ObjectExtender is able to derive the object class from the column type and vice versa. For Boolean, Charcter, and String objects the the VapCharToBoolean, VapCharToString, and VapTrimStringStringConverter are supplied. VapTrimStringConverter is the most appropriate converter for Strings, because it removes trailing blanks on reading from the database.

30 Using VisualAge Smalltalk ObjectExtender Figure 21. Foreign Key Relationship Editor: workDepartment

Tools 31 3.1.3 Map Browser The Map Browser (Figure 22) is used to map the associations between the object model created with the Model Browser and the database schema created with the Schema Browser.

The Map Browser can also, on command, generate the initial code required for the Services layer.

Figure 22. Map Browser: Map Rb01Employee (Part 1 of 2)

32 Using VisualAge Smalltalk ObjectExtender Figure 23. Map Browser: Map Rb01Employee (Part 2 of 2)

In the Property Maps, two types of properties are shown (Figure 22 on page 32 and Table 1). Table 1. Map Property Type Prefixes Prefix Property Type

a attribute property

r association (relationship) property

Maps prefixed with an a represent attribute property maps between attributes in the object model and columns (Figure 24 on page 34).

Maps prefixed with an r represent associations (r stands for relationship) between interclass associations and foreign key relationships in the database schema (Figure 25 on page 34).

Tools 33 Figure 24. Property Map Editor: Attributes

Figure 25. Map Property Editor: Associations (Relations)

34 Using VisualAge Smalltalk ObjectExtender 3.1.4 Status Browser The Status Browser supplies a variety of useful functions. Figure 26 shows the View menu options.

Figure 26. Status Browser: View Menu

The most noteworthy options of the Cleanup and Activate menus are: • Clear Home Collection Cache and Clear Relationship Cache. These two commands enable you to empty the caches that ObjectExtender builds up internally, to optimize database access times. • Release All Transactions. This command ensures that there are no transactions active in the ObjectExtender system; all outstanding transactions are discarded. • Global ObjectExtender Reset. This command is sometimes necessary to restore ObjectExtender to a known state after errors have occurred during debugging. • Activate Data Store. This is perhaps the most useful Status Browser command. Before a debug session can be started, it is necessary to establish a connection with the database. This command carries out the necessary connection initialization.

Tools 35 Most of the options can be invoked directly from Smalltalk scripts (see, for example, the commented script in 5.3.2, “Verify Local Image Schema and Data Store with Scripts” on page 58) and most have no dialogs. Errors throw exceptions that can be caught in the scripts. Some of the options are also available in the ObjectExtender Tools menu of the System Transcript (see below).

3.2 System Transcript The System Transcript displays a log of the system operation (Figure 27). Its menus also provide a useful starting point for accessing the various services of the Smalltalk and ObjectExtender development environment.

Figure 27. System Transcript with Log and ObjectExtender Tools Menu

Trace logging can be set on a packaged image, too, through a startup argument, either –vapBasicTrace or –vapDetailedTrace. The tracing is written to a log file whose location is defined with the –l parameter (A lower-case L, not the numeral 1).

36 Using VisualAge Smalltalk ObjectExtender To start an image called ABT.ICX and set its tracing level to be a basic trace there is the command line argument: ABT.EXE –iABT.ICX –vapBasicTrace –lC:\Temp\VapLog.TXT

Tracing an image can slow down performance because of the overhead of logging the information to the Transcript, a process that involves switching. This performance drop is typically significant, so use tracing during development to help understand the application while it is being built and debugged, but do not switch on tracing in packaged images unless an issue can be resolved by looking at the trace.

The ObjectExtender Tools menu shows a number of useful options. We recommend that you select the Basic Trace option as a minimum during development.

3.3 Smalltalk Class Editor and VisualAge Script Editor ObjectExtender stores the definitions of the models, maps, and schemas not only as data in classes but also as textual summaries in the comment of the classes. Without starting a dedicated ObjectExtender tool, you can see the actual definition by looking at the comment of the storage class in the Smalltalk Class Editor VisualAge Script Editor (Figure 28 and Figure 29).

Figure 28. Smalltalk Class Editor Comment Showing the Textual Summary

Tools 37 Figure 29. VisualAge Script Editor Comment Showing the Textual Summary

3.4 Inspectors The Smalltalk inspectors still work as before, but "What you see is NOT what you get,..." at least sometimes, because of ObjectExtender’s optimization, caching, and transaction context dependencies (see also 5.7, “Do Some Smalltalk Inspection” on page 76).

Looking at the attribute values of a business object by examining the instance variables directly tells only half the story—as do the Smalltalk inspectors. Basically and simply for every new transaction created, a new object version with unset instance variables is created, like a temporary workspace in the new transaction. Values for the instance variables of the new created version in the new created transaction are retrieved from the version in the previous transaction, which was the context when the new transaction was created. Changes to the object in the context of the new transaction are effective only on the newly created object version.

On a commit of the new transaction, the new object version tries to merge its changed attributes with the attributes of the version of the previous

38 Using VisualAge Smalltalk ObjectExtender transaction. If another transaction came first and merged first, the new transaction backs out with a rollback.

An object, created in the first created transaction retrieves and sets its instance value variables from the data object. The data object is read from the persistent storage. The object writes its instance values on a commit to the data object that is written to the persistent data storage.

For each instance variable, two kinds of setters and getters are created, for example, lastName: and lastName, and primLastName: and primLastName. The setters and getters with the prefix prim are the actual or primary instance variable accessors. Their code looks the same as the code of the usual setters and getters generated by VisualAge without ObjectExtender, except for some part of the getter code. The prim getter sets the instance variables after accessing the data objects for attributes and the persistent storage for associated business objects. Therefore only after a prim getter—and of course prim setter— invocation, the instance variable of an object shows the actual value. But you never, ever invoke the prim setters and getters from outside the object, because they are private methods. You use the ordinary—public—setters and getters, through which the prim setters and getters are applied on the right versions of the business objects.

The bad news is that the Smalltalk inspectors show only half the truth and you should not use them to set object instance variables. The good news is that the Smalltalk inspectors never intrude on the business objects.

You can find two versions of an ObjectExtender-oriented inspector extension among the downloadables (VapInspectorUtilitiesApp application in Appendix C, “Downloadables, ConfigMaps, Applications” on page 355). This extension enables the Smalltalk inspectors to look at and change the attributes of a business object.

The V0.1 [ JRW 08/28/98 ] a version of the inspector is not 100% unintrusive to the objects it observes. The inspectors can look for the public setters and getters. On a read, public getters are fired and do a versionForRead lookup on the business object manager. If there is no version in the current transaction, one will be retrieved and possibly, depending on the repeatableRead status of the current transaction and its parent transaction, SQL locks will be established. Karl Werner Heisenberg’s Uncertainty Principle (cit. "What we observe is not nature itself, but nature exposed to our method of questioning." in Isaac Asimov’s Book of Science and Nature Quotations, ed. Jason Shulman & Isaac Asimov, 1988) is valid not only for observing atomic particles, but also for observing instance variables of business objects, when using this inspector extension version.

Tools 39 The version V0.2 [ JRW 09/07/98 ] a shows more technical information but is 100% unintrusive to the object it observes.

Loading either version of the inspector enablements into the image adds the Enable Inspectors choice to the ObjectExtender Tools menu (Figure 30). To control the enablement from Smalltalk, use this code: VapInspectorUtilitiesApp enhancedInspectorsEnabled: aBoolean

Figure 30. ObjectExtender Tools Menu: Enable Inspectors Choice

40 Using VisualAge Smalltalk ObjectExtender Chapter 4. Sample Scenarios

The examples in this book are derived from two scenarios: 1. The employee and department scenario 2. The library scenario

Appendix A, “Overview of the Examples in This Book” on page 351 lists all samples with content hints and the so-called project names. The project names are part of the VisualAge Smalltalk application names. The naming conventions are the subject of Appendix B, “Naming Conventions” on page 353.

When we developed the set of samples that we use in this book, we faced a special naming convention requirement. To use the same names for the classes in the sample implementations, you can have just one sample under development or loaded in just one image at a time. So you either unload the current sample to load another one, or you run separate images for each sample. For ease of use, however, we decided to use one image to hold all the samples. To differentiate the classes in the sample implementations, we prefix each sample and its contained classes and tables with an appropriate prefix, such as Rb20.

4.1 The Employee and Department Sample Scenario The employee and department scenario has a system that maintains information about employees and departments of a company. This scenario is introduced in Chapter 1, “Concepts” on page 3 with class diagrams on page 3.

Part 2, “Your First Samples” on page 45 uses the employee and department scenario to familiarize you with the tools, frameworks, and development paths ObjectExtender. The sample sequence starts with just one class, matching one database table, using the forward or top-down development path outlined in Chapter 2, “Development Paths” on page 19. Then, sample by sample, more complexity is added, such as transactions in GUIs, the backward or bottom-up development path, a second class, and a basic relation between the two classes.

The actual sample database delivered with DB2 provides a number of tables in addition to the basic Employee and Department tables (Figure 83 on page 107). However, for illustration purposes, the employee and department sample scenario uses just two classes (and their associated database tables);

© Copyright IBM Corp. 1999 41 namely, the Department and the Employee classes. The relationships between these two classes are shown in Figure 2 on page 4.

Working with the employee and department sample scenario in Part 2, “Your First Samples” on page 45, you will develop a sound understanding of ObjectExtender’s tools, frameworks, and development paths—the bread and butter of ObjectExtender. But there is jam, too.

Part 3, “Advanced Details” on page 239 again uses the employee and department sample scenario to illustrate ObjectExtender’s support of handling mass data situations, such as binary large objects (BLOBs), large sets of heavy rows, and large objects.

4.2 The Library Sample Scenario The library scenario has a system, that maintains the main business information of a library—Library Management System: Information about book titles, copies of these book titles, and customers who have books on loan.

The analysis of the library problem domain and the design of a library system is the subject of the IBM Redbook, Using VisualAge UML Designer, SG24-4997. VisualAge (Smalltalk) UML Designer is IBM’s object-oriented modeling tool to roundtrip engineer Smalltalk and forward engineer Java code.

Figure 31 through Figure 33 summarize the design of the Library Management System. For more details, for example the requirements, the use case descriptions, etc. see Appendix E, “Library Management System” on page 377.

Part 3, “Advanced Details” on page 239 recalls the library scenario and the design of the library system, and selects various aspects of it to show how to use ObjectExtender to make (the library) objects persistent in a relational database. It covers such topics as inheritance; hierarchical composition, such as a bill of material; many-to-many associations; user-defined selective queries; and nested and concurrent transactions.

42 Using VisualAge Smalltalk ObjectExtender Figure 31. Library Management System: Overview

Figure 32. Library Management System: Loan Management Use Cases

Sample Scenarios 43 Figure 33. Library Management System: Class Diagram

Note: The status attribute—with the values available, borrowed, overdue, lost, and reserved—was removed from the Book (book copy) class and converted into the Status class hierarchy (Figure 33). The Status class hierarchy implements the state design pattern to make the loan part of the system a reusable framework.

44 Using VisualAge Smalltalk ObjectExtender Part 2. Your First Samples

© Copyright IBM Corp. 1999 45 46 Using VisualAge Smalltalk ObjectExtender Chapter 5. Employee Top-Down

The Employee Top-Down sample looks at just one class, the Employee class (Figure 34) and follows the forward or top-down development path.

isAdministeredBy > departments < administers 1

Department Employee *administrative departmentNumber Department employeeNumber name name manager < worksIn phoneNumber adminDepartment * has > 1 workDepartment work employees ...... Department

isManagedBy > 0..1 < manages 0..1 managed manager Department

Figure 34. Class Diagram Focusing on the Employee Class

For more information about the development path see 2.1, “Forward or Top-Down: Start with the Object Model” on page 19 and Figure 11 on page 21). The sample uses the name prefix Rb20.

5.1 Define the Model (Metadata) The starting point for defining the model is the Model Browser. The model is defined in four stages as described below.

5.1.1 Create New Model To create and save a new model with the Model Browser (Figure 35), select Models->New Model... from the menu bar or New Model... from the Models list pop-up menu to create the new model. Then save the model by selecting Models->Save Model.

© Copyright IBM Corp. 1999 47 Figure 35. Model Browser: Models Menu - New Model and Save Model

When asked for names, use the names listed in Table 2 or create your own names, consulting Appendix B, “Naming Conventions” on page 353. Table 2. Model-Related Names for the Rb20Employee Sample Item Name

Model name Rb20Employee

Application name for storage (of the model) Rb20EmployeeModelApp

Class name for storage (of the model) Rb20EmployeeModel

Creating and saving a new (empty) model adds the Rb20EmployeeModel to the list of (loaded) Models. Select the model and review its textual summary in the text pane (Figure 36).

Figure 36. Model Browser: Textual Summary of New (Empty) Rb20Employee Model.

48 Using VisualAge Smalltalk ObjectExtender A quick visit to the VisualAge Organizer window shows the newly created application and the associated storage class for the model (Figure 37).

Figure 37. Storage Application and Class for Rb20EmployeeModel

5.1.2 Define Classes Once you have the model defined, you can add classes. For this first sample, only one class is required: the Employee class. Again, in the Model Browser (Figure 38) select the Rb20Employee model and then select Classes->New Class... from the menu bar or New Class... from the Model Classes list pop-up menu to open the Class Editor.

Figure 38. Model Browser: Classes Menu - New, Edit, and Delete Class

In the Class Editor (Figure 39) enter the new class name, Rb20Employee, and click on OK.

Employee Top-Down 49 Figure 39. Class Editor: New Rb20Employee Class

Figure 40 shows the Model Browser window after you have created the class and saved the model again.

Figure 40. Model Browser: Rb20Employee Model with (Empty) Employee Class

5.1.3 Define Attributes Table 3 lists the attributes to define the Rb20Employee class.

Note that, to comply with Smalltalk naming conventions, the attribute names must start with a lower case letter. It is possible to argue, from a strictly data-driven approach, that the only required attribute is empNo, as it is the primary key. However, in deciding on what qualifies an attribute as “required,” we have taken a more business-oriented approach. Note further that the names are derived from the Employee table of the DB2 sample database (Figure 81 on page 105).

50 Using VisualAge Smalltalk ObjectExtender Table 3. Attributes of the Rb20Employee Class Attribute Name Attribute Type Required Comment

1 birthDate Date No Birthdate

2 bonus ScaledDecimal No Bonus

3 comm ScaledDecimal No Commission

4 edLevel Integer Yes Education level

5empNo String Yes (Object ID) Employee number

6firstNme String Yes First name

7 hireDate Date No Hire date

8 job String No Job name

9 lastName String Yes Last name

10 midInit String Yes Middle initial

11 phoneNo String No Phone number

12 salary ScaledDecimal No Salary

13 sex String No Sex (M or F)

14 workDept String No Working department

To add attributes, in the Model Browser select the Rb20Model and the Rb20Employee class and open the Attribute Editor (Figure 42 on page 52) either directly or indirectly through the Class Editor.

To open the Attribute Editor: • Directly: Select Attributes->New Attribute... from the menu bar or New Attribute... from the Class Attributes list pop-up menu (Figure 41). • Indirectly through the Class Editor: Select Classes->Edit Class... from the menu bar or Edit Class... from the Model Classes list pop-up menu. In the Class Editor on the Attributes notebook page click on New... (see Figure 14 on page 25).

Employee Top-Down 51 Figure 41. Model Browser: Attributes Menu - New, Edit, and Delete Attribute

In the Attribute Editor (Figure 42 on page 52) specify attribute properties Name, Type, and Value required. Remember that to comply with Smalltalk naming conventions, the Name must start with a lower-case letter.

Figure 42. Attribute Editor: Attribute Named birthDate of Type Date

Figure 43 shows the Rb20Employee class with all attributes defined and the model saved. Note that not all attributes are shown in the Class Attributes list. Scroll through the attributes as well as through the textual summary to review the definition.

The textual summary shows a very significant item of information, Oid: Undefined, Which means that an object identifier has not been defined for this class. If at this point you try to generate the supporting model code, selecting Models->Generate... in the menu bar, you will get an error message saying that no object identifier has been defined.

52 Using VisualAge Smalltalk ObjectExtender Figure 43. Model Browser: Rb20Employee Class with Attributes

You must define a suitable object identifier for the object. As noted in Table 3 on page 51, the attribute to be used as the object identifier is empNo. Use the Class Editor to select the empNo attribute and make it the object identifier (Figure 44). Once again, save the model when you have made these changes.

Figure 44. Class Editor: Making empNo the Object ID of Class Rb20Employee

Employee Top-Down 53 5.1.4 Define Relationships There are no relationships to be defined in this sample as there is only one class. You will see how to define relationships when we introduce the Department class.

5.2 Generate Code for the Model Classes Before you generate the code for the model classes, you have to check the model generation options.

5.2.1 Set Model Code Generation Options In the Model Browser select Models->Model Code Generation Options from the menu bar or just Model Code Generation Options from the Models list pop-up menu. Make sure that the correct model—Rb20Employee—is selected. The left-hand window in Figure 45 shows a sample model code generation options dialog.

Figure 45. Model Code Generation Options and Class Selection Dialogs

The Model application name is the name of the application where ObjectExtender will store the generated code. ObjectExtender takes the model name Rb20Employee and appends ModelApp to create the default name, Rb20EmployeeModelApp. In a selection dialog at generation time you select the model classes for which you want to generate the code (right-hand window in Figure 45). These options are very convenient for large models and during maintenance. For large models it is preferable to store the code of each class or each reasonable cluster of classes in a separate application. During maintenance it is preferable to generate selectively—only for changed and new classes.

54 Using VisualAge Smalltalk ObjectExtender Because the Rb20 sample has only one class, there is no need to think about other names and clusters, but you do have to think about the ModelApp suffix. If you do not want to store the generated model code in the same application as you stored the model definition—and we suggest that you do not —change the suffix to App, so that the Model application name is Rb20EmployeeApp. (See the Note on page 354 in Appendix B, “Naming Conventions” on page 353).

The Generate VisualAge parts and Generate persistent classes options must be checked (as they are by default), Otherwise you will not get all of the generated code that you require later on for the sample.

The Default persistent class root option defines BOWithDataObject as the root class for the generated model code class (see class hierarchy in Table 4). Table 4. Class Hierarchy of Persistent Objects in ObjectExtender Class Definition

Object nil subclass: #Object instanceVariableNames: '' classVariableNames: 'Dependents ' poolDictionaries: 'SystemPrimitiveErrors SystemExceptions '

BusinessObject Object subclass: #BusinessObject instanceVariableNames: 'bom ' classVariableNames: '' poolDictionaries: 'VapExceptions '

BOWithDataObject BusinessObject subclass: #BOWithDataObject instanceVariableNames: 'dataObject ' classVariableNames: '' poolDictionaries: ''

Rb20Employee BOWithDataObject subclass: #Rb20Employee instanceVariableNames: 'empNo lastName... job’' classVariableNames: '' poolDictionaries: ''

A development setup would usually define its own Default persistent class root class that has the BOWithDataObject as the superclass and contains the generic code common for all business objects. The class hierarchy in Table 5 includes such a Rb-framework default class root with the name RbBOWithDataObject.

Employee Top-Down 55 Table 5. Class Hierarchy of Persistent Objects in ObjectExtender Class Definition

Object

BusinessObject

BOWithDataObject

RbBOWithDataObject inserted Rb-framework supeclass

Rb20Employee

5.2.2 Start Model Code Generation In the Model Browser select Models->Generate... to start the model class code generation. In the selection dialog select the model classes for which you want to generate the code (right-hand window in Figure 45). Actually you deselect the model classes for which you do not want to create the code, because the dialog opens with all model classes preselected. Confirm your selection by clicking on OK and look at the trace messages in the Transcript window (Figure 27 on page 36).

If you now look at the VisualAge Organizer window and compare it with the Model Browser window in Figure 37 on page 49, you see, as shown in Figure 46, that (a new application and) new classes have been created for the model.

Figure 46. Code Application and Classes for Rb20EmployeeModel

56 Using VisualAge Smalltalk ObjectExtender Three new classes have been created: • Rb20Employee. This class is the primary or business object class. • Rb20EmployeeHome. This class is a helper class responsible for retrieving, holding existing, and creating new instances of the Rb20Employee class. • Rb20EmployeeKey. This class is responsible for administering the object identity attribute.

5.3 Generate Schema and Mapping (Metadata and Database) If you want to run the sample with the database simulated in your image, continue with 5.3.1, “Generate Image Schema” on page 57. If you have DB2, or an equivalent ODBC database installed on your system and connected to the VisualAge Smalltalk integrated development environment (IDE), you can use it now by going to 5.3.4, "Generate Database Schema," on page 61 or after you have checked out the persistence of your model with the image schema.

5.3.1 Generate Image Schema The purpose of using the image as the database is to simplify the (early) development stage: immediate testing is possible, no database definitions and alterations are required, you do not interfere with other developers, and sample test data sets can be saved to and loaded from files.

To generate the image schema use the Model Browser and select Models-> Generate Image Schema... from the menu bar (Figure 35 on page 48). In the model class selection dialog confirm the selection by clicking on OK (Figure 47).

Figure 47. Class Selection Dialog when Starting the Generation

Employee Top-Down 57 Take a look at the VisualAge Organizer to see the service application and service classes ObjectExtender generates for the image schema (Figure 48).

Figure 48. Image Schema Application and Service Classes

Because the image schema generation completes all work up to the access of the image data store, no further work is required to get ready for testing.

5.3.2 Verify Local Image Schema and Data Store with Scripts Run the script sequence presented below. It leads you in 15 steps through a verification scenario. The scripts are saved in the Rb20LIS.wsp workspace of the downloadables (see Appendix C, “Downloadables, ConfigMaps, Applications” on page 355.) For each step swipe over the lines through the line with the dashed arrow (--->) at the end and execute the selection. The output of the system is shown in italics. Activate the detailed trace in the System Transcript by selecting ObjectExtenter Tools->Detailed Trace. "01 Perform a global reset of ObjectExtender" System vapReset " --->" *** Resuming ->{0}(Shared)( > 28578) "02 Activate the Rb20EmployeeHome's data store" Rb20EmployeeDataStore singleton activate " --->" "03 Clear the local image data store" ImageServiceObject reset " --->"

58 Using VisualAge Smalltalk ObjectExtender "04 Ask the Rb20EmployeeHome for all instances" Transcript cr; show: (Rb20EmployeeHome singleton allInstances) printString; cr "returns an empty OrderedCollection --->" '>>>executing allInstances'OrderedCollection() "05 Create and make persistent a new instance in a Transaction" ’1stTransaction’. "... just a transaction name" Rb20EmployeeHome singleton create empNo: '100001'; firstNme: 'David'; midInit: 'A'; lastName: 'Singleton' edLevel: 9; job: 'ObjectExtender Specialist'. Transaction current commit " --->" *** Resuming ->{0}(1stTransaction)( > 22792) *Committing* {1}(1stTransaction)( > 22792 > 7044)2w '>>>executing insert' >>>>committing "06 Ask the Rb20EmployeeHome for all instances" Transcript cr; show: (Rb20EmployeeHome singleton allInstances) printString; cr "returns an OrderedCollection with the instance with empNo='100001' --->" '>>>executing allInstances'OrderedCollection(a Rb20Employee('100001') ) "07 Save the local image data store to the file Rb20Emp1.lis" ImageServiceObject save " --->" "08 Clear the local image data store" ImageServiceObject reset " --->" "09 Ask the Rb20EmployeeHome for all instances" Transcript cr; show: (Rb20EmployeeHome singleton allInstances) printString; cr "returns an empty OrderedCollection --->" '>>>executing allInstances'OrderedCollection() "10 Load the local image data store from the file Rb20Emp1.lis" ImageServiceObject load "--->" "11 Ask the Rb20EmployeeHome for the instance with key=’100001’ (Do it in a transaction to perform an update)" | employee | Transaction begin: ’2ndTransaction’. employee := Rb20EmployeeHome singleton findByEmpNo: ’100001’. Transcript cr; nextPutAll: employee firstNme. employee firstNme: ’Jonathan’. Transcript nextPutAll: ’ ---> ’; nextPutAll: employee firstNme; cr. Transaction current commit

Employee Top-Down 59 "returns the instance with empNo='100001' and updates firstNme --->" *** Resuming ->{0}(2ndTransaction)( > 17826) David ---> Jonathan *Committing* {1}(2ndTransaction)( > 17826 > 7044) "12 Ask the Rb20EmployeeHome for the instance with key=’100001’ (Do it in a transaction to perform a delete)" | employee | Transaction begin: ’3rdTransaction’. employee := Rb20EmployeeHome singleton findByEmpNo: ’100001’. Transcript cr; nextPutAll: employee firstNme; cr. employee markRemoved. Transaction current commit "returns the instance with empNo='100001' and removes the instance --->" *** Resuming ->{0}(3rdTransaction)( > 25656) Jonathan *Committing* {1}(3rdTransaction)( > 25656 > 7044) "13 Ask the Rb20EmployeeHome for the instance with key=’100001’ Rb20EmployeeHome singleton findByEmpNo: ’100001’ "returns nil, because the instance with key='100001' was removed --->" '>>>executing find by key'nil "14 Clear the local image data store ImageServiceObject reset " --->" "15 Perform a global reset of ObjectExtender" System vapReset " --->" *** Resuming ->{0}(Shared)( > 28578)

5.3.3 Version, Release, and Save the Sample Work At this point, we suggest versioning and releasing (and saving by exporting) the sample work you have done so far, especially if you want to continue with 5.3.4, “Generate Database Schema” on page 61: 1. Version and release all classes 2. Version all applications 3. Create a new configuration map (Rb20EmployeeLISConfigMap) 4. Add all versioned applications to the new configuration map 5. Version the new configuration map 6. Export the versioned configuration map as a save. (We exported our configuration map to file Rb20LISD.dat; see Appendix C, “Downloadables, ConfigMaps, Applications” on page 355).

If you do not have a database system installed, or if you want to go on with the image schema, continue with 5.5, “Activate a Data Store” on page 71.

60 Using VisualAge Smalltalk ObjectExtender 5.3.4 Generate Database Schema In 5.3.1, “Generate Image Schema” on page 57 we use an in-image database. In this section we demonstrate the use of a physical database, with the IBM DB2 database. Although DB2 comes with the relevant Employee table, for the purposes of this example, we assume that it is necessary to generate the tables from scratch.

Note: Before you continue, version and release (and save by exporting) the sample work you have done so far, and—if you generated the local image schema—perform an Activate->Clear local image data store and a Cleanup->General ObjectExtender reset in the Status Tool/Browser. To version and release (and save) your sample work, follow the steps in 5.3.3, “Version, Release, and Save the Sample Work” on page 60. You actually do not have to unload Rb20EmployeeServicesApp for the local image data store because the services application for database data store will be called differently.

The relevant model, Rb20Employee, already exists. The next stage is to generate a schema and map from the model. In the Model Browser select Models->Generate schema from model from the menu bar.

Note: ObjectExtender generates both the schema and the corresponding map.

ObjectExtender derives the logical names for the schema entities from the model entities, chooses VAP for the (high-level) table qualifier, VapConverter for the column data converter, and VARCHAR(12) for the column type for all String-attribute-related columns. To verify the schema, open the Schema Browser (Figure 49) by selecting ObjectExtender Tools-> Browse Schemas from the menu bar in, for example, the System Transcript Select schema Rb20Employee in the Schemas list and browse the textual summary of the schema in the bottom pane of the browser. Select table Rb20Employee from the Tables list to list the columns with their specifications in the Columns list.

ObjectExtender’s choices, namely, VAP, VapConverter, and VARCHAR(12) are technically correct and work (almost) perfectly, but they do not exactly reflect the environment, user interface, and business needs. For all characterlike columns use the VapTrimStringConverter converter to get the usual string concatenation and entry field behavior in today’s GUIs—except the data is sensitive to leading and trailing blanks and must be processed as is.

In the Schema Browser select the Rb20Employee schema and the Rb20Employee table and then select Tables->Edit Table... to open the

Employee Top-Down 61 Table Editor and change the high-level qualifier, VAP, to USERID, our choice (Figure 50). The qualifier is a standard DB2 access qualifier (typically the name used to log on to the DB2 database). If you are unsure of which qualifier to use in your environment, consult your database administrator.

Figure 49. Generated Schema Rb20Employee

In the Table Editor select a column and click on the Edit... button to open the Column Editor and change the column’s Type, Converter, and Length. You can also open the Column Editor on a Columns from the Schema Browser by double-clicking on the column in the columns list... or by selecting the column and choosing Columns->Edit column... from the menu bar.

62 Using VisualAge Smalltalk ObjectExtender Figure 50. Table Editor: Rb20Employee

Use the information listed in Table 6 to change the columns (in bold). The proposed column types and lengths have been chosen to match those in the employee table in the DB2 sample database. Table 6. Columns for the Rb20Employee Table Column Column Type Not Column Column Type Not Name with Length Null Name with Length Null

1 birthDate DATE No 8 job CHAR(8) No

2 bonus DECIMAL(9,2) No 9 lastName VARCHAR(15) Yes

3 comm DECIMAL(9,2) No 10 midInit CHAR(1) Yes

4 edLevel SMALLINT Yes 11 phoneNo CHAR(4) No

5 empNo CHAR(6) Yes 12 salary DECIMAL(9,2) No

6firstNmeVARCHAR(12) Yes 13 sex CHAR(1) No

7 hireDate DATE No 14 workDept CHAR(3) No

Employee Top-Down 63 Figure 51. Column Editor: Column empNo of Rb20Employee Table

After changing the qualifier and the columns, save the schema. In the Schema Browser select Schemas->Save Schema... from the menu bar. Use Rb20EmployeeSchemaApp for the application and Rb20EmployeeSchema for the storage class.

As mentioned before, the map was generated together with the schema. Because type changes of columns do not affect mappings, and the mappings are OK, only the map has to be saved—after a review.

To review the map, open the Map Browser (Figure 49) by choosing ObjectExtender Tools->Browse Maps from the menu bar in, for example, the System Transcript. Select map Rb20EmployeeRb20Employee in the Maps list and browse the textual summary of the map in the bottom pane of the browser. Select the Rb20Employee class in the Persistent Classes list and the Rb20Employee (primary) map in the Table Maps list to review the mappings in the Property Maps list. The lower-case a in parentheses indicates an attribute mapping (see Table 1 on page 33). ObjectExtender generates by default a Simple mapping (see the Property Map Editor in

64 Using VisualAge Smalltalk ObjectExtender Figure 24 on page 34). The name in parentheses is the (logical) column name to which the attribute (or association) is mapped.

Figure 52. Map Browser: Rb20Employee Map

Note that none of the attributes is part of the optimistic predicate. The optimistic predicate is used in the optimistic locking of a persistent class to detect—by a read and compare before write—an update concurrence conflict—Last comes, last wins. The business rules and the business processes would define the critical attributes (and associations), which then become candidates. A generic approach to protect against overwriting is to define a time stamp or update counter attribute and declare it as the only part of the optimistic predicate. ObjectExtender chooses optimistic locking by default. Pessimistic locking can be enabled by choosing

Employee Top-Down 65 Persistent Classes->Enable pessimistic locking on a selected persistent class in the Map Browser.

Note further that ObjectExtender concatenates the name of the model name and the name of the schema to create the name of the map. If you create the map manually, you may prefer to use just the "Project" name, Rb20Employee, as listed in Appendix B, “Naming Conventions” on page 353.

In the Map Browser select Datastore Maps->Save Datastore Map to save the map. Use Rb20EmployeeMapApp for the application name and Rb20EmployeeMap for the storage class. Storing the map in the same application as the schema is a valid alternative.

5.3.5 Generate the DDL and the Database Tables It is possible to view the DDL code that the Schema Browser would use to create the physical database tables (Figure 53).

Figure 53. DDL to Create the Rb20Employee Table

66 Using VisualAge Smalltalk ObjectExtender There are several ways of generating the physical database tables. For example, it would be possible to copy and paste the DDL script displayed in the Schema Browser window into a text file and then run the text file from a DB2 command window. However, perhaps the simplest way is to get the Schema Browser to do the work itself. In the Schema Browser (Figure 82 on page 106) select Schemas->Import / Export Schema-> Export Entire Schema to Database. When the Schema Browser executes this command, it asks for the database connection information as shown in Figure 54. Use the relevant parameters given to you by your database administrator.

Note: For the RedBook ObjectExtender samples we created the RBOE database, added the system-defined user USERID (with password PASSWORD), and granted it all rights. Watch for the case of the password! Some systems are case sensitive To create the database in DB2 (UDB, Version 5): Start the Command Center program, open a Control Center with the hierarchy tool button, expand the hierarchy and work with the popup menus on the hierarchy items; for example, on the DB2 item select Start from the pop-up menu to start DB2, or on the Databases item select Add...from the pop-up menu to add a database and so on. If you need to start or stop a DB2 administration server, in a system command window use DB2ADMIN START or STOP. You also can start or stop DB2 in a system command window with DB2START and DB2STOP.

Figure 54. Database Connection Dialog

When you click on the OK button, the Schema Browser establishes the necessary database connection, creates the tables, and makes the alter for

Employee Top-Down 67 the key. The result of creating the database is displayed in the System Transcript window (Figure 55).

Figure 55. System Transcript Showing Results of Creating the Database

Figure 55 shows that an error occurred during the schema creation. However, with a little more thought, this error is to be expected. The Schema Browser issues a Drop Table... command before the Create Table... command, just in case the table already exists. This is relatively standard with such scripts, and you can safely ignore the error message.

68 Using VisualAge Smalltalk ObjectExtender 5.4 Generate Service Classes In 5.3, "Generate Schema and Mapping (Metadata and Database)," on page 57, we created the schema for an in-image database and for a physical database hosted by DB2. For the in-image database, the service classes were created automatically as part of the in-image database creation. For the physical database case, you have to create the service classes in a separate task. Using the Map Browser follow the steps described below.

5.4.1 Set the Service Code Generation Options In the Map Browser select Datastore Maps->Generation Options... to check the service generation options (Figure 56).

Figure 56. Service Code Generation Options for DB2 Database

When the dialog is first presented, the Database connection info field is likely to be blank. Use the Change button to establish an automatic connection with the database, as in Figure 54. If you leave the field blank or specify the information only partly, you are prompted for the missing information at runtime—when using the services or connection the first time. We accepted the default proposal for the Services application name, but you can use another name if you prefer.

Note that you do not have to save the map, even though you changed the services code generation options. The options are only transient and not part of the map.

Employee Top-Down 69 5.4.2 Start Service Code Generation To start the generation of the code for the service classes, select Datastore Maps->Generate Services from the menu bar in the Schema Browser. When this operation is complete, you are ready to test the sample without developing a GUI.

Use the VisualAge Organizer to see the applications and classes generated for the services (Figure 57).

Figure 57. Services Generated for Rb20Employee and DB2

Note that the name of the data store for the database is different from the name of the data store for the local image schema. The database data store name is derived from the map name—a concatenation of the model and schema name. The local image schema data store has no map and no dedicated schema—it uses a generic schema, so the data store name consists only of the model name because there is no other name from which to derive it.

70 Using VisualAge Smalltalk ObjectExtender 5.5 Activate a Data Store In Chapter 1, “Concepts” on page 3 you were introduced to application layers and persistence layers (Figure 3 on page 5 and Figure 4 on page 6). Both layers have a similar pattern to successfully separate the model from the storage. Now, we want to make the model code, without changing it, talk and listen to various data stores. How can we do that? By activating a data store. But how do the homes, that is, logical storage, know about the data store and vice versa? In the generation process, first the model code is generated—with a special focus on the homes. Then with the knowledge of the homes, the services sets are generated, such as the services for the local image or the database and—the most important—the data store, which is the physical storage.

Selecting and activating a data store is like connecting the logical storage—the homes—with the physical storage—the data store (Figure 58).

Home Collections of a Model

Activate Data Store

Services Sets

Data Stores

Image Relational Database Stub/... Schema Schema Schema

Figure 58. Conceptual View of Activating a Data Store

Employee Top-Down 71 At runtime, when the data store is activated, the activate method shown in Figure 59 is invoked. The activate method is implemented in the abstract DataStore class and inherited by all concrete data store classes.

AnyDataStore(DataStore)>>#activate

activate

"Set up the home collections we service."

self initializeSessionPool. homeCollections := LookupTable new. self currentHomes with: self serviceObjectClasses do: [ :eachHome :eachServiceObject | homeCollections at: (eachHome class name asString) > put: ( eachHome > dataStore: self; "<---- the very data store" > serviceObjectClass: eachServiceObject) ]

Figure 59. DataStore>>#activate Method

The last three lines assign the data store and services to the home,— and the last five lines assign the homes to the data store.

From the above discussion we can conclude that many data stores can be in the image for one and the same model, but only one of them can be active at a time.

To actually activate a data store execute one of the following Smalltalk expressions: • XyzAbcDataStore singleton activate • VapBrowserSupport vapActivateDataStore

The latter expression can be used only at development time.

5.6 Run Sample Headless with Scripts In this section, we explore the use of the ObjectExtender development environment to test the object class without developing a GUI (that is, run the sample headless). You may believe (as we believed at first) that developing a GUI is the simplest way to test a class. However, running the sample headless enables you to see the system in operation and understand it better than would be possible with a GUI.

72 Using VisualAge Smalltalk ObjectExtender To run the sample headless, you need a Smalltalk window in which you can type text. You can use any of the script editing windows but it is a lot simpler and easier to use a workspace window. In the VisualAge Organizer (or almost any Smalltalk window), select the File->New Workspace (or File->New) from the menu bar to open a new workspace window. Save the contents from time to time to a file to keep your work for reference and reuse. See the printout of our workspace with the system replies included, in Appendix D.1, “Rb20HDL.wsp - Headless Test Workspace” on page 361. For the downloadable file, see Appendix C, “Downloadables, ConfigMaps, Applications” on page 355.

5.6.1 Run Scripts Stepwise Type the following Smalltalk code into the window, select it with the cursor, click the right-hand mouse button, and select the Inspect command: self halt. Rb20EmployeeHome singleton allInstances

The first line opens the debugger window, as shown in Figure 60. Using the debugger in this way, you can step through the code and see what happens at each stage.

Figure 60. Debugger Stopped on “self halt.”

When you click on the Resume button, you are likely to be presented with an error message telling you that ”There is no active data store for a Rb20EmployeeHome.” In this case, you must activate the data store by using the Activate->Activate Data Store command in the ObjectExtender Status Browser or by executing on of the following Smalltalk expressions:

Employee Top-Down 73 Rb20EmployeeDataStore singleton activate "for the image data store" Rb20EmployeeRb20employeeDataStore singleton activate "for the DB d.s." VapBrowserSupport vapActivateDataStore "you are aksed what data store"

(ObjectExtender named the data store Rb20EmployeeDataStore when the image schema (and services) were generated, and Rb20EmployeeRb20employeeDataStore when the database schema (and services were generated.)

See 5.5, “Activate a Data Store” on page 71 for more information about the relationship between data stores and homes.

Inspecting the result of the Rb20EmployeeHome singleton allInstances expression shows that an OrderedCollection is delivered. Further inspection of the contents of this OrderedCollection shows that there is no data in the database. This is not surprising as the database has been created from scratch and is therefore empty.

The next stage is to populate the database. As discussed in 1.3, “Transactions” on page 11, a transaction is necessary to handle the interaction with the database. The following code snippet inserts a single record into the database (only the values for the mandatory fields are used): Transaction begin. Rb20EmployeeHome singleton create empNo: 'A12345'; firstNme: 'Fred'; midInit: 'Q'; lastName: 'Smith'; edLevel: 18. Transaction current commit.

You can once again inspect the result of the Rb20EmployeeHome singleton allInstances expression. This time, the OrderedCollection inspector shows that our database now contains one record, as illustrated in Figure 61.

Figure 61. Result of Making a Single Employee Object Persistent

74 Using VisualAge Smalltalk ObjectExtender When you use a DB2 database, you can also use the standard DB2 command environment to see what is in the database.

It would be quite useful to be able to see what our object actually contained in the way of attributes. This is discussed further in 5.7, "Do Some Smalltalk Inspection," on page 76.

5.6.2 Monitor Headless Sample with Status Browser At this point, we recommend that you use the Status Browser to explore the options available for displaying status information for the sample that we have just created, so that you are familiar with what is displayed. The available outputs are too numerous to describe here in detail; however, two important facilities are offered by the Status Browser. • On the Cleanup menu, the following commands (whose names are self-explanatory) are useful for partially or completely resetting the system during testing: • Clear Home Collection Cache... • Clear Relationship Cache... • Reset Data Store... • Release All Transactions • Global ObjectExtender Reset • On the Activate menu: • Activate Data Store. The use of this command has already been covered. All data stores must be activated before they can be used. • Load Local Image Data Store. This command allows a previously saved copy of the local image data store to be reloaded into the image data store. • Save Local Image Data Store. This command allows a copy of the local image data store to be written to a separate file so that it can be reloaded in the future (for example, for testing). • Clear Local Image Data Store. This command empties the local image data store of all its contents.

Further information is available in the ObjectExtender help files.

Employee Top-Down 75 5.7 Do Some Smalltalk Inspection ObjectExtender does some tricky things with the basic inspection such as: Now you see me, now you don’t!

This next section will give you a good understanding of why inspecting persistent objects in a transaction-oriented environment is different from inspecting transient objects.

The example was developed with the DB2 database, so you can inspect the table contents through the DB2 command line interface. Sometimes looking at the image and the database at the same time can be very useful for debugging. However, understanding this section is not necessary for understanding subsequent sections. You may want to proceed directly to 5.8, “Create Views with Transactions” on page 83.

5.7.1 Use the Basic Smalltalk Inspector (#inspect) In (5.6, "Run Sample Headless with Scripts," on page 72), we use a script to insert a single object into our database. Now we want to use the VisualAge tools to inspect that object so that we can understand how the ObjectExtender persistence framework operates.

The inspect example uses the script presented below. The halts are built in to let you look at the results that we want to discuss. Clicking on the Resume button in the debugger will bring you to the next result. You do not have to worry about opening or closing the inspectors and debuggers; the script does that for you automatically. (The only thing you might have to do is move the debugger window, to see the inspector window.) "01" | employee | "02" System vapReset. "03" Rb20EmployeeDataStore singleton activate. "04" Transaction begin. "05" employee := Rb20EmployeeHome singleton allInstances first. "06" (DbgInspector on: employee) open; halt; close. "07" (DbgInspector on: employee lastName) open; halt; close. "08" (DbgInspector on: employee) open; halt; close. "09" Transaction current commit

The steps in this inspection example are these: 1. Declares a temporary variable employee 2. Carries out a global ObjectExtender reset

76 Using VisualAge Smalltalk ObjectExtender 3. Activates the Rb20EmployeeDataStore 4. Begins a transaction 5. Sets the temporary variable to the first (the one and only) item in the database 6. Inspects the temporary variable. The result you get should be similar to that in Figure 62. To show the values, select all instance variables by swiping over the list while holding down the (selection) mouse button.

Figure 62. First Inspection of the Attributes through the Instance Variables

Note that the only attribute that appears to be set is that used for the object identifier, empNo. At first view, this is rather surprising. The explanation is that ObjectExtender uses concept called lazy read and lazy set, and the standard debugger that is usually used in Smalltalk does not know anything about it. The debugger displays only the values of the instance variables. ObjectExtender holds a physical record read from the database but does not set the object instance variables until required. 7. Inspects the lastName attribute. Smith, the lastName we entered, is displayed, (Figure 63).

Figure 63. Inspecting an Attribute through the Getter

Employee Top-Down 77 8. Repeats step 4. This time, as shown in Figure 64, lastName has a valid value.

Figure 64. Second Inspection of the attributes through the Instance Variables

The other instance variables that were set (for example, edLevel) are still shown as nil, as they have not been individually accessed. Repeating step 5 for the other attributes would result in their values being shown when the Employee object is once again inspected. 9. Terminates the transaction with a commit.

5.7.2 Use the ObjectExtender Enablement for the Inspectors Import the V1.0 [ JRW 08/28/98 ] a version of the VapInspectorUtilitiesApp in the downloadable RBOEZD.DAT library into your library and load it into your image (see Appendix C, “Downloadables, ConfigMaps, Applications” on page 355). Enable the inspectors by selecting ObjectExtender Tools-> Enable Inspectors in the System Transcript (Figure 30 on page 40). Repeat the script in 5.7.1, “Use the Basic Smalltalk Inspector (#inspect)” on page 76 with the enabled inspectors and compare the inspector windows you get with the disabled inspectors.

For example, the enabled inspector in Figure 65 shows the values of the set instance variables (empNo, firstName, midInit, lastName, edLevel), whereas the disabled inspector in Figure 62 on page 77 shows only nils. Furthermore, the enabled inspector hides the ObjectExtender instance variables of the business object (bom and dataObject).

78 Using VisualAge Smalltalk ObjectExtender Figure 65. Enabled Inspector Showing the Values of the Instance Variables

As explained in 3.4, “Inspectors” on page 38, the enabled inspectors access the attributes of a business object through public getters and setters. Business logic is executed, the persistent values are read and set, ...and the business object is—or at least behaves—"changed."

5.7.3 Look at All Instances of a Class (#allInstances) It is now instructive to change the value of one of the attributes and see what happens. Use the script presented below, which behaves just like the script in 5.7, “Do Some Smalltalk Inspection” on page 76. Again, just look at the opening inspectors, and then continue by clicking on the Resume button in the debugger. You do not have to close the inspector or debugger windows that open automatically. Note: Disable the inspector enablement in the ObjectExtender Tools menu of the System Transcript to get the windows that match Figure 66 through Figure 70. "01" | employee | "02" System vapReset. "03" Rb20EmployeeDataStore singleton activate. "04" Transaction begin. "05" (DbgInspector on: Rb20Employee allInstances) open; halt; close. "06" employee := Rb20EmployeeHome singleton allInstances first. "07" employee firstNme. "08" (DbgInspector on: Rb20Employee allInstances) open; halt; close. "09" employee firstNme: ’Mike’. "10" (DbgInspector on: Rb20Employee allInstances) open; halt; close. "11" Transaction current commit. "12" employee := nil. "13" (DbgInspector on: Rb20Employee allInstances) open; halt; close

Employee Top-Down 79 The steps in this allInstances of a Class sample are: 1. Declares a temporary variable employee 2. Carries out a global ObjectExtender reset 3. Activates the Rb20EmployeeDataStore 4. Begins a transaction 5. Inspects allInstances of the Employee class The inspector displays an array with the number of instances of the Rb20Employee class that have been instantiated and are now held in the Smalltalk image. The display should show that no instances have been instantiated (Figure 66). Note that this allInstances method is actually implemented in the Behavior class, a class very far down within Smalltalk. Do not use it within your normal code as it will conflict with the way that ObjectExtender handles persistent objects.

Figure 66. Initial Result of Rb20Employee allInstances: No Instances

If the display shows that some instances are present (and this happened to us once), the instances are probably held by some dangling reference. To get rid of them, follow these steps: 1. Close all windows with potential references—debuggers and inspectors too—and get rid of all globals you might have used for testing and check class variables that might be direct or indirect references. 1. Use the ObjectExtender Status Tool/Browser menu options or the corresponding Smalltalk expressions, such as • Transaction reset • System vapReset 2. If this does not help, use this expression • System abtScrubImage Note: Never, ever, use the expression you used to use in the plain Smalltalk world: AClass allInstances do: [:each | each become: nil]. You

80 Using VisualAge Smalltalk ObjectExtender must forget that you have ever seen it. Using this "ugly" piece of code is certain to mess up your image and ObjectExtender’s object bookkeeping. 6. Sets the temporary variable to the first item in the database 7. Accesses the firstNme attribute firstNme through the getter 8. Repeats step 5. Inspects the number of instances of the Rb20Employee class that have been instantiated in step 6. The display (Figure 67) shows that there is one instance of the class, and the lastName instance variable shows a value different from nil. (To see the values of the Employee object, double-click on the number 1 and select the entries by swiping over them while holding down the (selection) mouse button.)

Figure 67. Result after One Read and an Attribute Access

9. Changes firstNme to ’Mike’ 10.Repeats step 5 again The code inspects the number of instances of the Rb20Employee class that have been instantiated in step 6 and changes one attribute through the setter in step 9. This time there are two instances (Figure 68).

Figure 68. Two Instances After an Update to an Attribute.

Employee Top-Down 81 At first sight this is somewhat surprising. We have changed frstNme and suddenly we have two copies of the same object! This is the ObjectExtender persistence framework at work. Figure 69 shows the contents of the original object, read from the database, and Figure 70 on page 82 shows the contents of the changed object. In both cases, all instance variables that were previously set when the database record was created have now been set and displayed.

Figure 69. Object with Original Attribute Values

Figure 70. Object with Changed Attribute Values.

82 Using VisualAge Smalltalk ObjectExtender The surprising thing in Figure 69 on page 82 is that firstNme is still Fred. After all, we have just changed firstNme to Mike. However, Figure 70 shows the attributes in the second instance, and here, firstNme is Mike. The reason for the difference is quite simple. The persistence framework is working. The transaction is not committed, so the framework needs to keep two instances of the object. The first instance is the one that was read from the database, with the original firstNme, "Fred." Perhaps another transaction will need to access the object and, as the change has not yet been committed, that second transaction must get the original copy. The second instance is the updated version with the new firstNme, "Mike." 11.Commits the transaction 12.Clears the temporary with nil (releases the object) 13.Repeats step 5 again. Inspects the number of existing instances—1—as in Figure 67 on page 81, but with the new firstNme.

5.8 Create Views with Transactions In previous sections, for example, in 5.5, “Activate a Data Store” on page 71 and 5.6, “Run Sample Headless with Scripts” on page 72 we cover interacting with the database through scripts. However, it does not take much to set up a nice GUI to run the sample. In this section we present four views. They all use the same GUI part but attach to the transaction framework in two different ways, as described below.

5.8.1 Use the Basic Transaction Parts Figure 71 shows the Rb20EmployeeView1—the first and only version—that attaches to the transaction framework by using the basic transaction parts sharedTransaction and topLevelTransaction.

Employee Top-Down 83 Figure 71. Employee View1 Using the sharedTransaction and topLevelTransaction

The key features of this version are listed after the parts list shown in Figure 72, on the left. Detailed connection information is listed in Table 7 on page 88. The connections are made following the guidelines set in the ObjectExtender User’s Guide and Reference.

84 Using VisualAge Smalltalk ObjectExtender Figure 72. Parts List of the Rb20EmployeeView 1, 2 and 4, and 3

The key features of Rb20 EmployeeView are: • A Window with: • A container with columns to show the most significant attributes of all instances in a scrollable list (containerEmployees). In a real application, the list would show only a user preselected collection. • Entry fields in a scrollable form (scrolledForm) to allow a new or selected employee to be edited (...Fields for the attributes—not shown in Figure 72—are in empDetailForm, the only contents of the scrolledForm).

Employee Top-Down 85 The entry fields are all text entry fields, but appropriate converters have been selected in the Open Settings dialogs for each field to handle conversion between strings and the underlying database type (such as date). It would be possible to use a combo box, for example, for the sex field, as a combo box has only two allowable values, M and F. However, we need to use this field to demonstrate some problems and their solution, and that is not possible with a combo box. • Buttons to create a new employee entry and to reset, save, or delete entries (pbNew, pbReset, pbSave, and pbMarkForDelete). • An employeeHome to manage the collection and creation of Employee objects • The sharedTransaction systemwide for the overall transaction control. • A topLevelTransaction to handle each individual database transaction. The rootCommittedOrRolledback event is connected to the beginChild: action of the sharedTransaction. The normalResult event of this connection is in turn connected to the self action of the topLevelTransaction. In this way, whenever a transaction completes or rolls back, another one is created. We need to use the rootCommittedOrRolledback event so that we also generate a new transaction if the current transaction is rolled back when the Reset button is clicked or a transaction fails. The rootCommittedOrRolledback event is also connected to the container items action, with employeeHome allInstances as a parameter, to refresh the list. • A transacted employee variable to hold the current employee details. It is important to note that there is a connection between the transaction attribute of the employee and the self attribute of the topLevelTransaction, so that the transacted variable can identify its owning transaction. • Two connections from the employee object to the empNo field. The first is the connection between the empNo attribute and the field itself. The second is an indirect connection through the trueElseFalse comparator between the isNotPersistent attribute of the transacted employee variable and the editable attribute. This second connection is essential. It ensures that, once an object is created, its object identifier, the empNo attribute, cannot be changed accidentally. An alternative for the second connection is the direct connection from the isNotPersistent to enabled. The direct connection to editable produces a walkback, because the isNotPersistent attribute value of the transacted employee variable is sometimes nil, and the editable attribute of a field works only with booleans, whereas the enabled attribute can handle nil too—by interpreting nil as false.

86 Using VisualAge Smalltalk ObjectExtender • Some helper parts doing comparisons and conversions: • concatenator (Rb99StringConcatenator in Rb99UtilityPartsApp) concatenates the string1..6 input attributes to the signaled and read-only string attribute to build up a reasonable window title: firstNme, midInit, lastName, the class in square brackets, and the view name. • trueElseFalse (Rb99TrueFalseNilComparator in Rb99UtilityPartsApp) converts the boolean input attribute to the signaled and read-only trueElseFalse attribute with the values true for true and false for everything else. The actual input is the isNotPersistent attribute of the transacted employee variable to set the editable attribute of the empNo key field—the object id field. notNil (Rb99NilComparator in Rb99UtilityPartsApp) compares the object input attribute with nil and returns the signaled, read-only notNil attribute with the values true for nil and false for everything else. The actual input is self of the transacted employee variable to set the enabled attribute of the scrolledWindow—and so of the entry fields. • An errorPrompter—added later in 5.9.2, “Add Error Information for the User” on page 99. • A helper script, timeNowAsStringFromTimeStamp, supplying the topLevelTransaction with a name. The transaction name is displayed in the view and serves as identification when looking at the transactions in the ObjectExtender Status Tool/Browser, View->Transaction Statistics. • All the time a (topLevelTransaction) is active. Whenever this active transaction is terminated with a commit or rollback by the user a new topLevelTransaction is created automatically through the committedOrRolledback event of the terminated transaction to enable the next transaction controlled object changes. The last active topLevelTransaction is closed automatically by closing the window (no developer connection is required).

For the Rb20EmployeeView2 through Rb20EmployeeView4, the systemwide sharedTransaction and topLevelTransaction are part of the businessTransaction or enhancedBusinessTransaction.

Employee Top-Down 87 Table 7. Connections of Rb20EmployeeView1 (Part 1 of 2) Source (S) Feature of S Target (T) Feature of T

1 concatenator string Window title

2 containerEmployees selectedItem employee self

3 containerEmployees selectionIsValid pbMarkForDelete enabled

4 employee birthDate birthDateField object

5 employee lastName lastNameField object

6 employee midInit midInitField object

7 employee phoneNo phoneNoField object

8 employee job jobField object

9 employee comm commField object

10 employee bonus bonusField object

11 employee salary salaryField object

12 employee edLevel edLevelField object

13 employee sex sexField object

14 employee empNo empNoField object

15 employee workDept workDeptField object

16 employee firstNme firstNmeField object

17 employee hireDate hireDateField object

18 employee isPersistent tbIsPersistent selection

19 employee isNotPersistent trueElseFalse boolean

20 employee self notNil object

21 employee firstNme concatenator string1

22 employee midInit concatenator string3

23 employee lastName concatenator string5

24 employee validationError errorPrompter promptFor:

25 employeeHome allInstances containerEmployees items

Note: Connections with number and source in bold are unique to Rb20EmployeeView1.

88 Using VisualAge Smalltalk ObjectExtender Table 8. Connections of Rb20EmployeeView1 (Part 2 of 2) Source (S) Feature of S Target (T) Feature of T

26 notNil notNil scrolledForm enabled

27 pbCancel clicked topLevelTransaction rollback

28 pbMarkForDelete clicked employee remove

29 pbNew clicked employeeHome create

30 (pbNew,clicked --> normalResult employee self employeeHome,create)

31 pbSave clicked topLevelTransaction commit

32 (pbSave,clicked --> errorResult errorPrompter promptFor: topLevelTransaction,commit)

33 topLevelTransaction rootCommittedO sharedTransaction beginChild: rRolledback

34 topLevelTransaction transactionName transactionNameField object

35 topLevelTransaction committedOrRoll containerEmployees items edback

36 topLevelTransaction self employee transaction

37 (topLevelTransaction,committe value employeeHome allInstances dOrRolledback --> containerEmployees,items)

38 (topLevelTransaction,rootCom normalResult topLevelTransaction self mittedOrRolledback --> sharedTransaction,beginChild:)

39 (topLevelTransaction,rootCom transactionName Rb20EmployeeView1 timeNowAsStri mittedOrRolledback --> ngFromTimeSt sharedTransaction,beginChild:) amp

40 trueElseFalse trueElseFalse empNoField editable

41 Window openedWidget sharedTransaction beginChild:

42 (Window,openedWidget --> normalResult topLevelTransaction self sharedTransaction,beginChild:)

Note: Connections with number and source in bold are unique to Rb20EmployeeView1.

Employee Top-Down 89 5.8.2 Use the BusinessTransaction Part Figure 73 shows that 6 links are required to manage the basic parts sharedTransaction and topLevelTransaction (or 8 links with the transaction naming): 1. Two (or Three) on openedWidget to create the initial transaction 2. Two on commitedOrRolledBack to refresh the items of the container 3. Two (or Three) on commitedOrRolledBack to create the next transaction

1.

2.

3.

Figure 73. View with the sharedTransaction and topLevelTransaction Parts

Replacing the sharedTransaction and the topLevelTransaction with the businessTransaction part (Figure 74) reduces the number of links from 6 to only 1: 1. One on the transaction event—triggered through the commit, the rollback, (and the request for a transaction from the transacted employee variable)—to refresh the items of the container

1.

Figure 74. View with the businessTransaction Part

The view (available in the downloadables as Rb20EmployeeView2) with its unique connections listed in Table 8 on page 89 has almost the same behavior

90 Using VisualAge Smalltalk ObjectExtender as the Rb20EmployeeView1. Almost, because the transaction naming is not (easily) supported, and the items in the container belong to the same transaction as the edited employee. The latter you can observe, when editing, for example, the firstNme of an item. Every key stroke is also reflected in the firstNme of the item in the container. Actually, in the view with the basic transaction parts the container is filled in the context of the read-only, ever- present sharedTransaction. The context of a transaction means: That the transaction was the current one, whereas in the view with the businessTransaction part the container is filled in the context of the transaction of the businessTransaction, which is also the transaction of the transacted employee variable.

A minor addition can solve the missing transaction naming as shown in the overview Figure 75, third extract from the top, taken from the Rb20EmployeeView3 (also a downloadable), with its unique connections listed in Table 10 on page 93. The transaction of the businessTransaction can be torn off. This tornoff transaction of bT (...of businessTransaction) has (at runtime) the same behavior as the topLevelTransaction.

To keep the items of the container out of the employee editing transaction context and in the shared transaction context requires another container fill solution than the direct and indirect connection from items to allInstances. A script, for example, looking like this: fillContainer "Fill the containerEmployees in the SharedTransaction Context" | currentTransactionSave | currentTransactionSave := Transaction current. Transaction shared resume. (self subpartNamed: 'containerEmployees') items: (self subpartNamed: 'employeeHome') abtAtAttribute: #allInstances. currentTransactionSave resume triggered from the view’s openedWidget event and from the businessTransaction’s transaction(event) would do the job. Verify the script in the VisualAge Smalltalk Composition Editor by temporarily replacing the two connections to the items with the event-to-script connections described above. The script is already defined in the downloadable.

The Transaction class supplies a convenience method if you want to force the data to be read from the database topLevelDo: aBlock. The script would then look like this (see page 94 after Table 11):

Employee Top-Down 91 Figure 75. Transaction Framework Connections of Rb20 EmployeeView 1 through 4

92 Using VisualAge Smalltalk ObjectExtender Table 9. Connections Unique to Rb20EmployeeView2 Source (S) Feature of S Target (T) Feature of T

43 businessTransaction transaction employee transaction

44 businessTransaction transaction containerEmployees items

45 (businessTransaction,transacti value employeeHome allInstances on --> containerEmployees,items)

46 pbCancel clicked businessTransaction rollback

47 pbSave clicked businessTransaction commit

48 (pbSave,clicked --> errorResult errorPrompter promptFor: businessTransaction,commit)

Table 10. Connections Unique to Rb20EmployeeView3 Source (S) Feature of S Target (T) Feature of T

49 businessTransaction transaction employee transaction

50 businessTransaction transaction transaction of bT self

51 businessTransaction transaction containerEmployees items

52 (businessTransaction,transacti value employeeHome allInstances on --> containerEmployees,items)

53 pbCancel clicked businessTransaction rollback

54 pbSave clicked businessTransaction commit

55 (pbSave,clicked --> errorResult errorPrompter promptFor: businessTransaction,commit)

56 transaction of bT self transaction of bT transactionNa me

57 transaction of bT transactionName transactionNameField object

58 (transaction of bT, self --> value Rb20EmployeeView3 timeNowAsStri transaction of bT, ngFromTimeSt transactionName) amp

Employee Top-Down 93 Table 11. Connections Unique to Rb20EmployeeView4 Source (S) Feature of S Target (T) Feature of T

59 enhancedBusinessTransaction transaction employee transaction

60 enhancedBusinessTransaction rootCommittedO containerEmployees items rRolledback

61 enhancedBusinessTransaction commitError errorPrompter promptFor:

62 enhancedBusinessTransaction transactionName transactionNameField object

63 (enhancedBusinessTransaction value employeeHome allInstances ,rootCommittedOrRolledback --> containerEmployees,items)

64 pbCancel clicked enhancedBusinessTr rollback ansaction

65 pbSave clicked enhancedBusinessTr commit ansaction

fillContainer "Fill the containerEmployees in the TopLevelTransaction Context" Transaction topLevelDo: [ (self subpartNamed: 'containerEmployees') items: (self subpartNamed: 'employeeHome') abtAtAttribute: #allInstances ]

An enhancedBusinesTransaction that offers additional features, such as an event in a sharedTransaction context and an optional transaction naming, can even further simplify the usage (Figure 75, bottom extract), with its connections listed in Table 11 on page 94.

Comment: Similar to the transacted variable, for example, an AbtTransactedViewPart could be defined. This new defined part would: inherit the behavior of the AbtViewPart and the behavior of the transaction, know its model object in a transacted variable, and integrate user-caring support, such as the attribute isDirty (has unsaved changes), configurable and customizable confirmation pop-ups on close and on delete, and much more. This AbtTransactedViewPart would be a valuable relative to the legacy transaction or transaction program, hosted by a framework—transaction monitor—and complete and reliable in its application and use. A detailed view with all of the above functions results in a "spider-web" (Figure 129 on page 176) and makes the need for such an AbtTransactedViewPart obvious.

Back to the standard businessTransaction part:

94 Using VisualAge Smalltalk ObjectExtender In some view scenarios, such as Rb20EmployeeViews 1 through 4, when a transaction is committed developers sometimes want the window to remain open, and other times want it to close. For example, a Save & End or OK button might commit a view’s changes and close the view, whereas a Save or Apply button might commit the changes and keep the view open. (The keyword Save is used to indicate a write to the persistent storage, whereas OK and Apply imply a commit of a nested and not topLevelTransaction.)

Whenever a transaction is committed, its stage changes from active to committed and its parent transaction is resumed. Any attempt to resume the committed (or rolled back) transaction causes a VapTransactionFailure exception. You must therefore begin a new transaction to allow the user to remain in the view at the same transaction nesting level.

The businessTransaction part caters for such a scenario, as illustrated in Figure 75 on page 92, second and third extract from top. It has a read only attribute of transaction, which it will lazily initialize to a new TopLevelTransaction. (Lazy initialization means that the transaction is created when it is first asked for if it has not been previously created and is null.) When its transaction is committed—either through the commit and rollback methods on the businessTransaction part or by the transaction being committed elsewhere—the part creates a fresh transaction. When the transaction is regenerated, the part signals the transaction event. If the transaction attribute of the businessTransaction is connected to the transaction attribute of a TransactedVariable, the TransactedVariable refreshes itself with the version of its object in the new transaction, and the user is allowed to continue working with the transacted variable's contents.

By default the businessTransaction creates a new TopLevelTransaction the first time its transaction attribute is retrieved. Rather than create this as a child of the SharedTransaction, the businessTransaction creates a private read-only transaction that it uses as the parent of its transaction. The reason for this is that when a transaction is committed its parent will be resumed. If a TopLevelTransaction is committed, the SharedTransaction will be resumed. The SharedTransaction cannot have any new objects created into it, including read-only objects that are newly read in from a data store. Having the read-only transaction as the parent, however, ensures that in the gap between the transaction being committed and refreshed the current transaction can read and refresh objects. This behavior can be toggled with the createReadOnlyParent attribute on the businessTransaction.

It is also possible to explicitly set the parent transaction that the businessTransaction should use to create its transaction as a child of. This is done through the writable attribute parentTransaction. This allows views

Employee Top-Down 95 that have to support nested transactions to be done by connecting the transaction property of one businessTransaction to the parentTransaction attribute of another. The latter transaction will be a nested child of the former, and the businessTransaction parts will ensure that whatever combination of transactions are committed or rolled back both parts have valid transactions that are nested from one another.

Because the business transaction lazily initializes its transaction when it is first asked for it, there are some circumstances where this is done before the parent transaction is set. In such cases it may be the desired behavior that the previously lazily initialized transaction (which will not be a child transaction of the new parent transaction) should be rolled back. This is the behavior by default. If this is not the desired behavior, for example, when the parent transaction is intentionally set while navigating along the hierarchy path and therefore any previously generated transactions are left active, the boolean attribute rollbackTransactionWhenParentChanges should be toggled.

5.9 Run Sample with User Interface We recommend that you now use the newly created GUI to create, edit, and delete employee records.You are soon likely to notice that the application has some quirks: • The sex field accepts any single-character string instead of only M or F. When more than one character is entered in this field, the update transaction aborts (look at the System Transcript window to see this). The abort is signaled from the DB2 database itself, with the error message that the value is too long. However, the user gets no other obvious indication that anything has gone wrong. • The GUI is not very user supportive: there is no warning about unsaved changes when leaving or closing the window, new items do not show up in the list until the save(commit) is performed, items marked for delete are not marked in the list, there is no indication about pending changes, and no confirm dialog is given for the delete command. • The application reads all instances from the persistent storage. That is acceptable only for a sample. • On a rollback, there is no way to recover the pending changes. They are just lost. This is not so bad if you perform a commit after every new, changed or deleted employee, but when you do multiple changes—and the GUI implementation invites you to do that—it becomes worse. • And last, but not least, the application has no concurrence support built in.

96 Using VisualAge Smalltalk ObjectExtender 5.9.1 Add Error Handling and Error Events What is missing from the application at the moment is error handling. It is always a point for debate as to whether the error-handling code should be in the GUI or in the object itself. While recognizing that there are valid arguments for each case, we have chosen to take a firm object-oriented line in writing this sample. We chose to implement the error-handling code primarily in the object so that we could illustrate the implementing error handling.

To implement error handling for the sex field, we need to add code to the Rb20Employee object. Using the VisualAge Organizer, open the Rb20Employee object for editing. Using the Public Interface Editor, create a new event, validationError, as shown in Figure 76. This event passes one parameter, an AbtError object, and allows us to signal some details about the error.

Figure 76. Public Interface Editor with the validationError Event

Employee Top-Down 97 The next stage is to add some validation code. Using the script editor, open the code for the sex: method. The generated code looks like this: sex: aString "Set the value of the attribute sex in the current transaction. | newValue | newValue := aString.

"Begin user code {pre-Set}" "End user code"

self bom markModified. ^self bom versionDataForUpdate primSex: newValue

Note that there is a section specifically provided for user code. In this section, write the following code snippet: newValue notNil ifTrue: [ "because sex is not required" newValue := newValue asString asUppercase. #(’M’ ’F’) includes newValue ifFalse: [ | err | err := AbtError new errorText: ’Sex has to be M or F’; locus: ’in validation’. self signalEvent: #validationError with: err. ^err ] ].

This code snippet converts the input to an upper-case string (only M or F, not m or f). It then checks that it is either M or F. If not, it creates an AbtError object with an error message and signals the validationError message. If the sex: setter is used in a script, the error err is returned too and can be checked like a return code object: (employee sex:aVariable) isAbtError ifTrue: [...

Having inserted and saved this code chunk, you can try running the sample again. This time, looking at the System Transcript, you will see that lower- case entries in the sex field are converted to upper case and that erroneous entries are ignored. However, the application still has a small problem: The user has no idea that he or she has made a mistake. The user should be told this in an error message in either the message area of the application window or a separate error dialog window.

98 Using VisualAge Smalltalk ObjectExtender 5.9.2 Add Error Information for the User Add a MessagePrompter object named errorPrompter in the Composition Editor, with the settings shown in Figure 77.

Figure 77. errorPrompter Properties

Connect the validationError event of the Employee object to the promptFor: action of the errorPrompter object.

Running the application now produces a dialog box like that shown in Figure 78 whenever an invalid entry is made in the sex field. Note that you can leave the field, and the view still displays the erroneous input, although the object stays clean. Of course, with only two choices for this field, a better GUI design would use a combo box so that it would be impossible for the user to make an invalid entry.

Figure 78. Validation Error Dialog

The approach we have used is very general. Validating other fields requires only that we edit the relevant method code on the Rb20Employee object to generate a validationError event with appropriate error text when an attribute fails validation.

Employee Top-Down 99 Note: The solution proposed above cannot be the final solution when: the transacted variable is cleared, it triggers the validationError event with a nil argument, opening an empty error message window.

5.9.3 Add Database Error Handling As we have developed this sample, database errors have been displayed in the System Transcript as they occur. We should therefore add one more error handling feature to alert the user (or perhaps the developer during testing) when something unexpected happens with the database.

So, connect the errorResult event of the connection between the Save button and the topLevelTransaction to the promptFor action of the errorPrompter. When you run the sample again, try an erroneous operation (for example, try four characters for the work department). As a result, you are presented with the full SQL error message (Figure 79).

Figure 79. SQL Error Passed by the topLevelTransaction

Of course, we could have caught this error earlier, before it hit the database. The user interface can, in a matter of seconds, be modified to limit the length of this field to no more than three characters. We could also have implemented validation in the Employee object’s workDept: method.

Note: If you get an empty error(message)Prompter, have an errorPrompter for each of the error events: errorResult from the commit action of the transaction and validationError from the employee transacted variable. (The employee transacted variable sends on nil not only for the self event, which is correct, but also for dedicated events, such as the validationError, because they may be deferred or delayed events. Adding a Rb99NilComparator between the employee transacted variable and the errorPrompter, feeding its object attribute with the validationError event, and catching its notNilObject event for the promptFor: action of the errorPrompter solve the problem too.)

100 Using VisualAge Smalltalk ObjectExtender 5.10 Stand-alone Application (GUI with Image Schema) Figure 80 shows the GUI Rb20EmployeeViewLIS, that runs with the local image schema and data store as a stand-alone application (see 1.5.3, “Application Data” on page 16). ObjectExtender’s service class for the local image data store—ImageServiceObject—has the #save, #load, and #reset protocol to manipulate the local image data store as a whole.

Figure 80. Local Image Data Store Dedicated Rb20EmployeeView

The view extends Rb20EmployeeView3 in Figure 75 on page 92 with buttons and scripts to control the local image data store (LIS:) and the list: • Activate data store (usually executed in ObjectExtender status tool) • Save local image data store in a file. A file dialog asks for the file name.

Employee Top-Down 101 • Load local image data store from a file. A file dialog asks for the file name. • Reset—clear— the local image data store. All data is removed. • Refresh List—each load, reset, transaction start, and direct user request updates the list (#fillContainer) with employeeHome>>#allInstances.

5.11 Summary and Conclusions We have shown and discussed the first implementation of the employee sample, restricted to the Employee object.We: • Defined our model class, Rb20Employee • Generated the supporting model code • Created a corresponding schema automatically for a local image data store with an image database. Remember: only the data store and the service classes were generated, neither a schema nor map was generated • Verified the image database • Exercised the image database without a GUI (headless) • Created a new schema to work with the DB2 database • Created the physical database automatically using the new schema • Established a new set of mappings between our model class, Rb20Employee, and our schema, once the schema was generated • Generated the service classes and supporting code required to access the physical database, using the generation facilities of the Map Browser. • Exercised our access to the DB2 database, headless. • While exercising both the image database and the DB2 database, used some of the status monitoring tools and had a brief look at the way the persistence framework operated. • Built a small GUI application to access the Employee table in a more friendly manner. We developed the application quickly, following the guidelines in the ObjectExtender User’s Guide and Reference. • Used in the GUI the basic transaction parts sharedTransaction and topLevelTransation and experienced the simplification with the businessTransaction part. • Controlled which transaction is the current transaction to set up the "right" context for the various functions in the GUI • Added limited validation error handling to the application and learned how to add validation code to the object

102 Using VisualAge Smalltalk ObjectExtender • Added database error handling.

In our opinion, the main lessons learned from this sample are: • VisualAge ObjectExtender provides a very powerful framework for developing transaction processing applications in a short time. • The GUI reduces the need for application developers to get involved with the internal details of the Smalltalk system, although it does not eliminate the need entirely. • Design for error handling is essential. The designer needs to think through whether errors should be handled at the GUI level, or in the object itself or merely trapped when they eventually occur. • Object identifiers (normally the object’s primary key) are sacrosanct. Once an object has been created, do not attempt to change its object identifier. • There are two ways of implementing transactions: with the basic parts, such as the shared and top level transaction, or with the dedicated business transaction. The business transaction offers simplicity—especially in visual programming—but is limited in general purpose and option.

Employee Top-Down 103 104 Using VisualAge Smalltalk ObjectExtender Chapter 6. Employee Bottom-Up

The Employee Bottom-Up sample looks at just one table, the Employee table of the DB2 sample database (Figure 81) and follows the backward or bottom-up development path (see 2.2, “Backward or Bottom-Up: Start with the Database” on page 20 and Figure 11 on page 21). The sample uses the name prefix Rb21, except where parts from the previous sample are reused.

Figure 81. DB2 Sample Database: Employee Table with Columns

To exercise this sample, you should have the DB2 sample database created, and the DB2 database manager must be started. You can start the DB2 database manager in a system command window with db2start and create the DB2 sample database with db2sampl. The sample database installation uses your userid as the (high-level) qualifier (see also the Note on page 67). If the DB2 sample database is not created or not available, you can use the database and table you created in 5.3.5, “Generate the DDL and the Database Tables” on page 66.

© Copyright IBM Corp. 1999 105 6.1 Import Schema from Relational Database (Metadata) As you are working with an existing database, you have to generate the schema from the database. The first stage is to use the Schema Browser to create the necessary schema from the database.

From past experience, you know that you need to select suitable names for everything. So, when asked, name the schema Rb21Employee, and save the schema in a new application, Rb21EmployeeSchemaApp. Accept the default storage class name of Rb21EmployeeSchema when you save the schema for the first time.

Import the schema from the database by selecting Schemas-> Import / Export->Import Schema from Database ... from the menu bar of the Schema Browser (Figure 82).

Figure 82. Schema Browser: Import / Export Menu

Establish connection with the database in the same way as shown in Figure 54 on page 67—but with SAMPLE as Data source. Then select the table you want (Figure 83 on page 107).

106 Using VisualAge Smalltalk ObjectExtender Figure 83. Select Tables Dialog: All Tables of the DB2 Sample Database

After saving the newly generated schema (Figure 84), the Schema Browser shows that the new table has been created with the logical name Employee (Figure 85 on page 108).

Figure 84. Imported Database Schema Rb21Employee

Employee Bottom-Up 107 Figure 85. Imported Table: Still Logical Employee and without Primary Key

To avoid confusion with other applications that have been developed, change the logical name of the table by adding the Rb21 prefix, using the Table Editor (Figure 50 on page 63). Note that the Table Editor calls the name just Name.

Remember, two greater-than signs (">>") to the left of the column name indicate a primary key column (Figure 49 on page 62). The primary key columns are also listed in the Table Editor (Figure 50 on page 63). If ObjectExtender cannot detect the primary key columns and add the primary key definition (Figure 85 on page 108) because the captured table has no primary key definition in the database, you have to define the primary key manually, using the Table Editor. The EMPNO column has to be the primary key of the EMPLOYEE table.

108 Using VisualAge Smalltalk ObjectExtender 6.2 Generate Model Definition and Mapping (Metadata) In Chapter 5, “Employee Top-Down” on page 47, you developed a model for the Employee object. You can reuse this object to develop your model. However, you actually generate the model from the schema you have just created, selecting Schemas->Generate Model from Schema from the menu bar of the Schema Browser (Figure 82 on page 106).

If you forgot to define the primary key columns in 6.1, “Import Schema from Relational Database (Metadata)” on page 106, you receive the error message shown in Figure 86.

Figure 86. Error message for undefined Primary Key Columns after Import

Now open the Model Browser and find the new model that has been generated (Figure 87 on page 110).

The model has the same name as the logical name of the schema, namely, Rb21Employee, and its one and only class has the same name as the logical name of the table, namely, Rb21employee (note the lower-case emp...). If the model does not have the name RB21Rmployee, you might have forgotten to change the logical name of the schema originally derived from the physical name at import time. If you forgot, delete the model (and map), do the renaming as requested in 6.1, “Import Schema from Relational Database (Metadata)” on page 106, and generate the model once again.

Look at the attribute names in the textual summary created for your new Rb21Employee model class, as shown in Figure 87. Note that the attribute names have been taken directly from the database column names. It is not possible to change these names. If a project requires more meaningful names, the attributes must be entered manually rather than by using the automatic generation feature, and consequently the mappings have to be entered manually.

Employee Bottom-Up 109 Figure 87. Generated Model Rb21Employee

As you save the model, you are asked for the application name and the class name. In this case, use Rb21EmployeeModelApp and Rb21EmployeeModel, respectively.

ObjectExtender has also generated the mappings between the model and the schema for you.

Open the Map Browser and look at its contents. ObjectExtender has created a data store map named Rb21EmployeeRb21employee, as shown in the extract from the Map Browser in Figure 88. If you do not like this name, delete the map and enter the details manually.

110 Using VisualAge Smalltalk ObjectExtender Figure 88. Generated Map Rb21EmployeeRb21employee

6.3 Generate Code to Support Model and Services Now you have to generate the code to support the sample. This is a two-stage process. Follow these steps: 1. In the Model Browser, check that the model code generation options are as shown in Figure 89.

Employee Bottom-Up 111 Figure 89. Model Generation Options for the Rb21Employee Model

2. Use the Model Browser to generate the model code. 3. In the Map Browser, check that the service code generation options are as shown in Figure 90.

Figure 90. Service Code Generation Options for the Rb21Employee Model

4. Note that ObjectExtender has automatically generated a name for the service application. If no database connection is shown, you can specify a connection by clicking on the Change....button and completing the database connection dialog as shown in Figure 91.

112 Using VisualAge Smalltalk ObjectExtender Figure 91. Database Connection Information for the Rb21Employee Model

5. Use the Map Browser to generate the services.

6.4 Run Sample Headless with Scripts We have now completed the basic application, without any GUI. We need to test it briefly, as explained in 5.6, “Run Sample Headless with Scripts” on page 72: 1. Use the Status Browser to activate the data store Rb21EmployeeRb21employeeDataStore or a suitable window such as a Workspace window in which you execute this Smalltalk expression: Rb21EmployeRb21employeeDataStore singleton activate 2. Execute the following Smalltalk expression in a suitable window such as a Workspace window to query the database for all employees: Rb21employeeHome singleton allInstances inspect The result of this expression is a window similar to that shown in Figure 92.

Employee Bottom-Up 113 Figure 92. Result of Inspecting Rb21employeeHome singleton allInstances

If you had the Detailed Trace on, the System Transcript would show a trace like that shown (partially) in Figure 93 on page 114.

Figure 93. Extended Trace of Rb21employeeHome allInstances

114 Using VisualAge Smalltalk ObjectExtender The trace demonstrates that we have successfully established a connnection to the database and can read the rows from the database. The next stage is to add a GUI.

Before you go to work on a GUI, check that the schema, model, and map of the Rb21 sample are saved. Furthermore, you can version and release the generated model code classes, services code classes, and applications.

6.5 Adding the User Interface To add a GUI interface you could repeat the steps in 5.8, “Create Views with Transactions” on page 83, using the new class names for the Employee and EmployeeHome objects, namely, Rb21employee and Rb21employeeHome.

But for this sample, we go one step further. We show you how to develop a GUI with a list with all instances for selection, (Rb21EmployeeListView), and windows (Rb21EmployeeDetailView) for single instances for CRUD, create and insert, read, update, and delete (Figure 94). The windows for the single instances are opened in the List view by clicking on the New button for a create and insert and by double-clicking on a list item for an update or a delete. The Rb21views are classes in the Rb21EmployeeViewApp.

double-click List Bob Firstname Bob Alex Alex Xxxxxx Bobby Bob Firstname Xxxxxx Save Cancel Chris New Save Cancel Firstname New Refresh Xxxxxx Save Cancel click

Figure 94. List Window with Multiple Single Windows

After you complete this section and 6.6, “Run and Monitor the List and Detail View GUI” on page 122, there will still be room for improvement in designing and implementing water-and air-tight GUIs and transactions with VisualAge Smalltalk Enterprise and ObjectExtender.

Employee Bottom-Up 115 6.5.1 Create a List View Figure 95 shows the layout of Rb21EmployeeListView in the Composition Editor. Table 12 on page 118 lists all the connections.

Note: When you copy containerEmployees from the Rb21View, you must change a few things, because creating a model from a database and creating a database from a model do not exactly work the same way. First, you have to change the containerDetails (column) attribute names to lower case, for example, empNo to empno. Second, you must change the containerDetails converter of the midinit (middle initial) to Character. Double-click on the view item to open the settings dialog of the part and perform the adjustment.

3.

6. 5.

1. 4.

2.

Figure 95. Rb21EmployeeListView

The key features of the Rb21EmployeeListView are: • EmployeeHomeT, which is transacted by the sharedTransaction ensures, that allInstances are taken from the read-only transaction context (sharedTransaction).

116 Using VisualAge Smalltalk ObjectExtender • The "standard connection" between employeeHomeT allInstances and containerEmployees items is missing, because it would fire twice on startup, and the database would have to deliver all table entries twice. • The initial filling of the table—using the openedWidget event—is a hook on the refresh button click action. The refresh button clicked event gets the collection from the home and sets the containerEmployees items. • The connection firing sequences for the new the update and the actions delete are crucial to the correct working of the sample. If your sample does not work and your symptoms are, for example, nil values, you should check and change the connection orders on the parts by selecting Reorder Connections From from their pop-up menu: • Firing sequence for the new initiated by a click on the New Employee push button pbNew: 1. The initiating clicked event of the pbNew push button fires a new to the EmployeeDetailView factory. 2. The new on the factory grabs the sharedTransaction for the (promoted) parentTransaction of the businessTransaction of the just created detailView instance. 3. The torn-off employeeDetailView variable is set to the just created detailView instance.The normalResult event of the new sent to the EmployeeDetailView factory (in 2.): 4. Grabs a new employee instance from the transacted employeeHomeT by firing a create to the employeeHomeT 5. Sets the (promoted) tempEmployee (standard) variable of the torn-off employeeDetailView instance 6. Opens the employeeDetailView by firing the openWidget message to the employeeDetailView instance • Firing sequence for update/delete initiated by a double-click on an element in containerEmployees: The firing sequence is the same as for the new above, but with three small changes in the participating parts, the events, and the actions: • The initiating defaultActionRequested event of the containerEmployees sets internally the selectedItem and fires externally. • The new to the EmployeeDetailView factory (in 1.) is fired by the initiating defaultActionRequested event of containerEmployees, which first and internally sets its selectedItem to the double-clicked item. • The argument to set the (promoted) tempEmployee (standard) variable of the employeeDetailView (in 4.) is picked up from the just internally set selectedItem of containerEmployees.

Employee Bottom-Up 117 Table 12. Connections of Rb21EmployeeListView Source (S) Feature of S Target (T) Feature of T

1 ( (containerEmployees,defaultActionRequested value containerE selectedItem --> EmployeeDetailView,new),normalResult --> mployees employeeDetailView,employeeTmp)

2 ( (pbNew,clicked --> value employee create EmployeeDetailView,new),normalResult --> Home employeeDetailView,employeeTmp)

3 containerEmployees defaultActionRe Employee new quested DetailView

4 (containerEmployees,defaultActionRequested normalResult employee employeeTmp --> EmployeeDetailView,new) DetailView

5 (containerEmployees,defaultActionRequested normalResult employee openWidget --> EmployeeDetailView,new) DetailView

6 EmployeeDetailView instance employee self DetailView

7 employeeHome self employee self HomeT

8 pbNew clicked Employee new DetailView

9 (pbNew,clicked --> EmployeeDetailView,new) normalResult employee employeeTmp DetailView

10 (pbNew,clicked --> EmployeeDetailView,new) normalResult employee openWidget DetailView

11 pbRefresh clicked containerE items mployees

12 (pbRefresh,clicked --> value employee allInstances containerEmployees,items) HomeT

13 sharedTransaction self employee transaction HomeT

14 sharedTransaction self Employee businessTrans DetailView actionParentTr ansaction

15 Window openedWidget pbRefresh click

118 Using VisualAge Smalltalk ObjectExtender 6.5.2 Create a Detail View Figure 96 shows the layout of Rb21EmployeeDetailView in the Composition Editor. Table 13 on page 121 lists all the connections.

Figure 96. Rb21EmployeeDetailView

The key features of the Rb21EmployeeDetailView are: • The businessTransaction is used to simplify the construction. • Not crucial to this sample, but worth noting: The businessTransaction part has its parentTransaction feature promoted as businessTransactionParentTransaction. The promotion of the parentTransaction enables the view to be in a nested transaction level deeper than the topLevelTransaction-sharedTransaction nesting. However, in this sample, the transaction of the businessTransaction is in all situations a topLevelTransaction and writes to the persistent storage when committing. If no sharedTransaction is supplied as parent transaction, the businessTransaction creates a topLevelTransaction as its transaction on its own to manage the situation. You can verify this by

Employee Bottom-Up 119 removing the connection from the sharedTransaction to the EmployeeDetailView factory in the Rb21EmployeeListView. Everything runs just fine as before (Figure 95 on page 116). • The (standard) tempEmployee variable is used to decouple the "sending" from the "receiving" transaction. Transacted variables do a transaction switching—that is what they are built for. But the direct connection between one transacted variable in one transaction context and another transacted variable in another transaction context does not work properly. The combination of the plain vanilla (standard) temp variable and the delayed event for picking up the value in the variables is a perfect construction for decoupling two transacted variables of different transaction contexts. The event for picking up the variable value and storing it in the new transaction variable is the openedWidget event of the Window of the EmployeeDetailView and is totally asynchronous to the normalResult return event of the new sent to the EmployeeDetailView factory to create the employeeDetailView instance. • The only enhancement beyond what you already know from 5.8, “Create Views with Transactions” on page 83 is the deleteConfirmer—shown in action in Figure 97 on page 122. There is no mystery about the connections the deleteConfirmer uses to interface with the actual view and the transacted variable. A successful commit closes the window, and an intended and an unintended rollback and "business as usual" for the businessTransaction: The businessTransaction just creates "lazily" a correct, new transaction with which to resume.

120 Using VisualAge Smalltalk ObjectExtender Table 13. Connections of Rb21EmployeeDetailView Source (S) Feature of S Target (T) Feature of T

1 businessTransaction transaction employee transaction

2 concatenator string Window title

3 deleteConfirmer okYesRetry employee remove 4 deleteConfirmer okYesRetry businessTransaction commit

5 (deleteConfirmer,okYesRetry --> normalResult Window closeWidget businessTransaction,commit)

6 employee firstnme firstNmeField object 7 empno empNoField 8 midinit midInitField 9 edlevel edLevelField 10 lastname lastNameField

11 employee isNotPersistent trueElseFalse boolean

12 employee firstnme concatenator string1 13 midinit string3 14 lastname string5

15 employee isPersistent pbDelete enabled

16 employee phoneno phoneNoField object 17 job jobField 18 workdept workDeptField 19 hiredate hireDateField 20 sex sexField 21 birthdate birthDateField 22 bonus bonusField 23 comm commField 24 salary salaryField

25 pbCancel clicked businessTransaction rollback

26 pbDelete clicked deleteConfirmer prompt

27 pbSave clicked businessTransaction commit

28 (pbSave,clicked --> normalResult Window closeWidget businessTransaction,commit)

29 trueElseFalse trueElseFalse empNoField editable

30 Window openedWidget employee self

31 (Window,openedWidget --> value employeeTmp self employee,self)

Employee Bottom-Up 121 6.6 Run and Monitor the List and Detail View GUI The GUI developed in the previous sections "works as designed" (Figure 97).

Every click on the NewEmployee button and every double-click on an employee in the EmployeeListView opens an independent EmployeeDetailView. Each double-click on an employee in the list opens a new detail view, even if you double-click on the same employee again and again. You can close the list view; the detail views stay open.

Figure 97. List and Detail View with the Deletion Confirmer in Action

The Delete button in the EmployeeDetailView is disabled for new employees. Clicking on the Delete button for an already persistent employee shows the Confirm Deletion dialog.

Saving or deleting an employee closes the EmployeeDetailView, whereas a reset creates a rollback only, and the view stays open. When you close the window a rollback automatically occurs.

122 Using VisualAge Smalltalk ObjectExtender The application does not prevent the user from overwriting rows in the database tables, and the user can throw away changes without a confirmation. For the Reset such a function would be implemented in the same way as the DeletionConfirmer. For the change detection, more work must be done, because neither the transaction, the object, nor the transacted variable has a public interface to have access to the isChanged information.

In Figure 98 employee 000030 SALLY A KWAN was opened four times in a detailed view from the List view.

Figure 98. One Plus Four Object Versions of Employee 000030.

Use ObjectExtender’s Status Tool/Browser and (inspector enablement) to play with transactions and object versions. For every EmployeeDetailView you open, a new child transaction of the sharedTransaction—a topLevelTransaction—is created. Verify this by selecting View->Transaction Statistics between subsequent view opens without closings (Figure 99).

Employee Bottom-Up 123 Figure 99. View and Inspect Transactions and Transaction Statistics

For every employee object you touch in the EmployeeDetailView, a new version of that touched object is created. As many open views with changes you have of one object, as many versions you have of the object—plus one: the origin. Inspecting allInstances of the class shows them (Figure 100).

124 Using VisualAge Smalltalk ObjectExtender Figure 100. Fife (1+4) Instances of Employee 000030 in allInstances

Employee Bottom-Up 125 126 Using VisualAge Smalltalk ObjectExtender Chapter 7. Employee and Department Top-Down

The Employee and Department Top-Down sample looks at two classes—the Employee and Department class—and the associations between them (Figure 101) and follows the forward or top-down development path (see 2.1, “Forward or Top-Down: Start with the Object Model” on page 19 and Figure 11 on page 21). The sample uses the name prefix Rb30.

isAdministeredBy > < administers departments 1

Department Employee * departmentNumber administrative employeeNumber name Department name phoneNumber manager < worksIn adminDepartment workDepartment has > ...... * 1 work employees Department

isManagedBy > < manages 0..1 0..1 managed manager Department

Figure 101. Class Diagram for Employee and Department Sample

No attempt is made to reuse classes or parts developed earlier—except the non-sample-related helper parts—because each sample demonstrates something different, and we want each sample to stand alone, something that would be difficult to guarantee if we reused classes.

7.1 Define Model (Metadata) The starting point for defining the model is the Model Browser. The model is defined in the same way as in 5.1, “Define the Model (Metadata)” on page 47, except for a few new things, of course. New to this sample are the associations. Information that was just a simple (data) attribute in the previous samples—for example, the workDepartment of the employee—is now

© Copyright IBM Corp. 1999 127 part of an association between the Employee and the Department and is treated differently in an extra step (see 7.1.4, “Define Associations” on page 129).

7.1.1 Create New Model Create the model, name it Rb30EmpDept, and save it in Rb30EmpDeptModelApp and storage class Rb30EmpDeptModel. An alternative project name would be Rb30Company..., but that name would imply that the company object would also be modeled.

7.1.2 Define Classes Add the Rb30Employee and Rb30Department classes to the model.

7.1.3 Define Attributes Add the attributes for the two classes you have created. The attributes for Rb30Employee are the same as those listed in Table 3 on page 51. They are repeated here, in Table 14, for ease of reference. Table 14. Attributes for the Rb30Employee Class Attribute Name Attribute Type Required (Comment)

1 birthDate Date No

2 bonus ScaledDecimal No

3 comm ScaledDecimal No

4 edLevel Integer Yes

5empNo String Yes (Object ID)

6firstNme String Yes

7 hireDate Date No

8 job String No

9 lastName String Yes

10 midInit String Yes

11 phoneNo String No

12 salary ScaledDecimal No

13 sex String No

14 workDepartment Rb30Department No (Association)

128 Using VisualAge Smalltalk ObjectExtender Table 15. Attributes for the Rb30Department Class Attribute Name Attribute Type Required/Comment

1 administrativeDepartment *Rb30Department Yes (Association)

2 administrativeDepartment Rb30Department Yes (Association)

3 deptName String Yes

4 deptNo String Yes (Object ID)

5 employees *Rb30Employees No (Association)

6 location String No

7 manager Rb30Employee No (Association)

Table 14 and Table 15 also show which attributes (one in each table) are used for associations. Now add all attributes except the associations using the Attribute Editor.

7.1.4 Define Associations Once you have entered the class attributes and set the object identities for each class, use the Association Editor of the Model Browser to set up the associations between the two classes (Figure 102 on page 130).

It is important to select the names for the associations carefully, to avoid later confusion. As associations are listed separately from classes in the Model Browser, and later on generated service classes and methods will include the association name in their name, expand the name beyond that shown in the class diagram, so that it is obvious which association is meant. The Association Editor suggests using the two roles separated by an underscore character (_) or the phrase "to" to build the association name (in Figure 102 the underscore is shown as dash), example, for the employees (Role1) and work department (Role2) as shown in Figure 103 on page 130, left side: employees_workDepartment

A good alternative is to use both classes with the association name as infix, for example, as shown in Figure 103, right side: department-has_employees

Note the reversed sequence of the class names in the association name, while the same sequence of role names employees - workDepartment is maintained.

Employee and Department Top-Down 129 Class2 Class1

of Class1 in Class2 of Class2 in Class1

Figure 102. Association Editor View Opens with Name Templates

Figure 103. has Association Naming Relationships

Note: The underscore character or the "to" phrase in the association name is not necessarily required, but it helps ObjectExtender create reasonably shortened physical names for the constraints to insert into the database. ObjectExtender identifies the name components of an association name by the underscore and truncates each component proportionally until the whole name fits the length limit of the database. Remember the

130 Using VisualAge Smalltalk ObjectExtender VapDatabasePhysicalRules class to set the #maximumLength:, build database-conforming physical names, and customize name creation by subclassing. We extended the VapDatabasePhysicalRules (and some other related) classes in VapDatabasePhysicalRulesXApp to facilitate use and customizing—we even allowed dashes for separators as the Association Editor implies. You will use the basic and extended version in 7.3.2, “Generate Conforming Physical Names” on page 140. The application is part of the Appendix C, “Downloadables, ConfigMaps, Applications” on page 355.

You must also think carefully about the other parts of the Association Editor dialog. The association is between two classes, for example, Rb30Employee and the Rb30Department.

For the Roles of... you can say: • In the Rb30Department class, the role of the Rb30Employee is (has) employees. • In the Rb30Employee class, the Rb30Department is the workDepartment.

For the Navigables you can say: • Yes for the Rb30Department class, because you want to ask a department for its (has) employees. • Yes for the Rb30Employee class, because you want to ask an employee for his or her work department.

For the Manys you can say: • Yes for the Rb30Department class, because a department can have (has) many employees. • No for the Rb30Employee class, because an employee can work in only one department.

For the Requireds you can say: • No for the Rb30Department class, because a department can exist without (has no) employees. • No for the Rb30Employee class, because an employee can exist without working in a department.

The No in the Manys and the Nos for the Requireds are biased because of the DB2 sample database. The No in the Many for the department is given through the fact of the one-to-many relation—only one department can have one and the same employee at a time or an employee can work in only one

Employee and Department Top-Down 131 department (full-time in one department). The No for the Requireds is given through the fact of the null-able workdept column in the employee table.

The direction of the one-to-many associations is for technically not relevant ObjectExtender. The namings for the same sample relation with reversed direction would then be as in Figure 104.

Figure 104. WorksIn Association Naming Relationships

For the Employee and Department sample we suggest using the Role1-Role2 naming schema, and—for the actual sample association— the direction as shown in Figure 103 on page 130, even though the has association is not really a has-association (department "has" employees). For a real has-association, such as an order has orderItems, the direction as shown in Figure 103 on page 130 would be the (only) natural choice.

There are three associations to enter, supporting the three associations in the Employee and Department sample as shown in Figure 101 on page 127.

The first association—has or worksIn—was just used as an actual sample to introduce and explain the Association Editor in the paragraphs above. Now enter the association with the values shown in Figure 103 on page 130, on the left.

The second association is the manages or isManagedBy association with the roles manager and managedDepartment (Figure 105). In this association, we want to be able to navigate from department to manager and from manager to department, so both Navigable check boxes are checked. Departments only ever have one manager, and an employee can only ever manage one department (while this may not be true in the real world, it is the case for our

132 Using VisualAge Smalltalk ObjectExtender sample), so neither Many check box is checked. Finally, not all departments have a manager and not all employees are department managers, so neither Required check box is checked.

The manages and isManagedBy association is a one-to-one association. The direction of the one-to-one associations is for relevant ObjectExtender when generating the schema for a relational database and placing the foreign key. The foreign key is placed in the table that will be mapped from Class1, or in other words, in the table that corresponds with the class playing Role1. Therefore, the settings to the right in Figure 105 are correct.

Figure 105. One-to-One Association: Manages or isManagedBy

The third and last association is the administers or isAdministeredBy association with the roles departments and administrativeDepartment (Figure 106). In this association, we want to be able to navigate from one level in the department hierarchy to the next lower or upper level, so both Navigable check boxes are checked. Not every department administers inferior departments, but every department is ministered by a superior department. So, for the Role1/Class2 side (departments), the Many check box is checked and the Required check box is unchecked, and for the Role2/Class1 side (managedDepartment), the Many check box is unchecked and the Required check box is checked. This results in a special situation for the department, which is on the top of the hierarchy of this typical— tree with root, nodes, and leaves—construct: The root department administers itself, and is recognized as the root department by this very fact.

The resulting Model Browser display now looks like that shown in Figure 107.

Employee and Department Top-Down 133 Figure 106. Administers Association

Note that the Rb30Department has only three (value) attributes in the textual summary, but four additional (relation) attributes in the Class Attributes list that are marked with an r in parentheses in front of the name. These four association attributes are derived from the three associations in the Class Associations and have the names of the roles (role in).

Note further that each Navigable end of an association becomes a relational attribute.

Figure 107. Model Browser: Department Class

134 Using VisualAge Smalltalk ObjectExtender 7.2 Generate Code for the Model Classes Before generating the code for the real model classes (rather than the metadata defining the classes), in the Model Browser select Models->Model Code Generation Options to check that the generation options are correct. They should be as shown in Figure 108. If you do not want to generate all code into the same application, continue with 7.2.1, “Generate Model Code into Different Applications” on page 135.

Figure 108. Rb30 Sample Code Generation Options

Now generate the classes and code to support the model, using the Generate command in the Model Browser, and continue with 7.2.2, “Review Generated Model Class Code” on page 136.

7.2.1 Generate Model Code into Different Applications If you want to generate the model code into different applications follow these generic steps to set the model code generation options and to generate the model code: 1. Generate the code for the associations. For each class or class cluster tupel with associations, choose an application name, set the generation options accordingly, and generate the code for the associations between the tupel items. For the Employee and Department sample, choose the Rb30DeptYEmpApp application for the generation of the employees-workDepartment and manager-managedDepartment association code, and the Rb30DeptYDeptApp application for the departments-administrativeDepartment association code. (The Y

Employee and Department Top-Down 135 separates the tupel names and is related to a naming concept, where extensions are named Item1XItem2App and called Item1 eXtensions requested by Item2.) 2. Generate the code for the classes twice. For each class or class cluster, choose an application name, set the generation options accordingly, and generate the code for the class or class cluster. For the Employee and Department sample, choose the Rb30DeptApp application for the generation of the Rb30Department class and the Rb30EmployeeApp for the Rb30Employee class. Some partial generations will complain about undefined globals, and some will ask to replace an already defined global by a class. The compiler can not compile generated sources with undefined globals. The complaining generation has to be rerun. 3. Adjust the prerequisites. Some partial generation will also complain through the repository in the System Transcript about undeclared prerequisites with Warning: 49 ClassA>>#methodB should not reference CApp::ClassD. Adjust the prerequisites of the applications complaining about undeclared prerequisites as best you can. Situations with circular references among parcels are never complaint free.

7.2.2 Review Generated Model Class Code Take a look at the generated classes in Rb30EmpDeptApp in the VisualAge Organizer (Figure 109). In addition to the model, home, and key kind of class, which are already known from the Employee sample (Figure 46 on page 56), six association classes—one for each (navigable) role in the model— have been generated.

Take a look at the methods of the Rb30Department class related to the associations (Figure 110 on page 138).

ObjectExtender generated the getter—but no setters—for the Many associations departments and employees and for each an add... and remove... action for your convenience. If you are not comfortable with the names that ObjectExtender created—because the add and remove are of a single item,— you can change the names for the method and the argument, for example, to addDepartment: aDepartment. Using these add... and remove... methods unburdens you from setting the foreign key in the many-elements in the one-to-many relationships.

136 Using VisualAge Smalltalk ObjectExtender Figure 109. Model Classes for the Employee and Department Sample

ObjectExtender may generate isForward := true twice in the one-to-one relationship initialization methods, for example, in the one-to-one Rb30DepartmentToManagerRelationship class and in the one-to-one Rb30EmployeeToManagedDepartmentRelationship class. Make sure that the latter class sets its isForward to false. The class with the foreign key is the forward relationship, the other class is the backward relationship.

Important note: Whenever you change something in the model or regenerate the model code, you have to go through the entire generation chain—generate model code, schema and map, services, because the generated model code may not be complete or correct after just the model code generation. The services generation updates the generated model code. You can verify this on the one-to-one managedDepartment_manager association related relationship classes, which have isForward := true in

Employee and Department Top-Down 137 their initialize method after the model code generation but true and false after the services generation. An error message like "...does not understand ...Key" indicates an incomplete forward generation chain.

Figure 110. Rb30Department Class with Methods

7.3 Generate Schema and Mapping: Metadata and Database Now you want to have ObjectExtender generate the schema and data store mappings for a database, or—once again—the image schema for the local image data store.

138 Using VisualAge Smalltalk ObjectExtender If you want to use the local image data store, select Models->Create Image Schema from the menu bar in the Model Browser and continue with 7.6, “Run Sample Headless with Scripts” on page 146.

To generate the schema and mapping for a database, select Models-> Generate schema from model from the menu bar in the Model Browser.

Then review the generated schema and data store mappings using the Schema Browser and the Map Browser, respectively. If you already have these browsers open, you may have to refresh their lists. In the respective browser execute the Schemas->Load Available Schemas command and the Datastore Maps->Load Available Maps command from the menu bar to see the new schema and map.

7.3.1 Review the Schema and the Mapping Figure 111 shows the Rb30Employee table of the generated schema. After you change the qualifier and adjust the column types and converters, the schema seems to be OK.

Figure 111. Generated Schema for Rb30EmployeeTable

Note: The table names in the logical name of the Foreign Key Relationships will not be used to generate the physical names for the constraints.

If your foreign keys for the one-to-one association are generated in the wrong table, go back to the model editor, delete the relevant association, and define the association again—in the reverse direction—and regenerate the schema. Force the update of the lists in the Schema Browser and Model Browser by

Employee and Department Top-Down 139 executing the Schemas->Load Available Schemas... command and Datastore Maps->Load Available Maps... command, respectively.

Figure 112 shows the Rb30Department table of the generated schema. After you change the qualifier and adjust the column types, the schema seems to be OK.

Figure 112. Generated Schema for Rb30Department Table

Neither the schema nor the mapping requires structural changes.

Note: If you need to regenerate the schema because of an incorrect setup association, you must not only delete and reenter the incorrect setup association and delete the related Foreign Key Relationship; you also must delete the generated foreign key Column, because each generation itself adds its foreign key column.

7.3.2 Generate Conforming Physical Names It is possible to generate a physical database directly from the generated schema. However, you are almost certain to get some errors. These errors are in addition to the expected errors when the DROP TABLE commands are generated, as seen in Figure 55 on page 68.

When you try generating the database directly from the schema, the auto-generated ALTER TABLE ADD CONSTRAINT command fails because the foreign key names automatically generated by ObjectExtender do not conform to the database naming conventions—they either include spaces or are way too long. Another possible area of failure is the length of the automatically generated table and column names. These are usually limited by the particular database used. For DB2, table and column name lengths must not exceed 18 characters.

ObjectExtender provides basic support for generating DB2-conforming physical names. Evaluate this Smalltalk expression: VapDatabasePhysicalRules applyToSchemaNamed: ’Rb30EmpDept’

140 Using VisualAge Smalltalk ObjectExtender Verify the result and change it—if it does not match your requirements—in the Column Editor (Figure 113, left) and in the Foreign Key Relationship Editor (Figure 113, right).

Figure 113. Table Column Editor and Foreign Key Relationship Editor

You open these editors in the Schema Browser either from the appropriate menu or by double-clicking on an item in the appropriate list.

In the Employee and Department Top-Down sample we extended the physical name generation support for DB2. Import VapDatabasePhysicalRulesApp from Appendix 85, “Downloadable File RBOE.zip and Its Contents” on page 356 into your development repository and load it into your image. Start the extended generation in the System Transcript by selecting the new item, ObjectExtender Tools->Apply DB2 Physical Rules..., or by evaluating one of these Smalltalk expressions: • VapDatabasePhysicalRules applyDB2Rules • VapDatabasePhysicalRules singleton applyReasonablyToSchema: (VapSchema schemaNamed: ’Rb30EmpDept’

Employee and Department Top-Down 141 When asked for the schema name, enter Rb30EmpDept. Table 15 lists the relevant methods for the name creation in VapDatabasePhysicalRules. Table 16. VapDatabasePhysicalRules Class: Major Extending Methods VapDatabasePhysicalRules>>method ($=Class Method)

1 $applyDB2PhysicalRules - starts the generation and asks for the schema name in a message prompter

2 conformPhysicalName: actualPhysicalName from: nameOrSeedName for: anEntity - callback method for the call of the conformance and uniqueness methods (3 & 4)

3 conformPhysicalName: actualPhysicalName from: nameOrSeedName - builds the name, splits and truncates it

4 uniquePhysicalName: physicalName in: aSchema - makes the name unique in the schema

If you have already applied the basic naming support and want to try our extended naming support, either remove some or all of the physical names, or just delete the entire schema in the Schema Browser, regenerate the schema in the Model Browser, and apply our physical naming support. If the trace is on, each physical name that is either generated or changed is listed in the System Transcript Window (Figure 114).

Figure 114. Extended Physical Naming Support

142 Using VisualAge Smalltalk ObjectExtender 7.3.3 Match with the DB2 Sample Database (Optional) A few physical name changes and a flexible connection definition enable the Employee and Department sample to play not only with the self generated database but also with the DB2 sample database.

The following column name changes are mandatory: • In the Rb30Employee table change the (physical) foreign key Rb30epartment_deptNo to workDept, to be in synch with the Department table of the DB2 sample database (Figure 81 on page 105). • In the Rb30Department table change the (physical) foreign key name Rb30Employee_empNo to mgrNo and Rb30Department_deptNo to admrDept, to be in synch with the Department table of the DB2 sample database (Figure 137 on page 195).

Double-click on each of the columns in the Schema Browser to open the Column Editor and make the changes.

If you generate your table into a database other than the sample, you can change the table names to be in synch with the DB2 sample database tables EMPLOYEE and DEPARTMENT: • Change the (physical) Rb30Employee table name to Employee. • Change the (physical) Rb30Department table name to Department.

Double-click on each of the classes in the Schema Browser to open the Table Editor and make the changes.

If you have the rights to add aliases in the DB2 sample database, you do not have to change the table names. Just add aliases for the tables, such as USERID EMPLOYEE for the USERID RB30Employee table, and USERID DEPARTMENT for the USERID RB30Department table. If you use the DB2 UDB Control Center, expand the hierarchy down to the Aliases item and select Create... from the pop-up menu.

7.3.4 Change the Column Types Use the types listed in Table 17 to adjust the column types of the Rb30Employee table. Note that columns 5 and 14 are key columns that may produce an error and temporary inconsistencies (see 7.3.5, “Change the Key Column Types” on page 144). Use the VapTrimStringConverter for all

Employee and Department Top-Down 143 characterlike columns to get the usual string behavior in Smalltalk for the entry fields and the concatenation. Table 17. Columns for the Rb30Employee Table Column Column Type Not Column Column Type Not Name with Length Null Name with Length Null

1 birthDate DATE No 8 job CHAR(8) No

2 bonus DECIMAL(9,2) No 9 lastName VARCHAR(15) Yes

3 comm DECIMAL(9,2) No 10 midInit CHAR(1) Yes

4 edLevel SMALLINT Yes 11 phoneNo CHAR(4) No

5 empNo CHAR(6) Yes 12 salary DECIMAL(9,2) No

6 firstNme VARCHAR(12) Yes 13 sex CHAR(1) No

7 hireDate DATE No 14 workDept CHAR(3) No

Use the types listed in Table 18 to adjust the column types of the Rb30Department table. Note that columns 2, 4, and 5 are key columns that may produce an error and temporary inconsistencies when changed (see 7.3.5, “Change the Key Column Types” on page 144). Again, use the VapTrimStringConverter for all characterlike columns to get the usual string behavior in Smalltalk for the entry fields and the concatenation Table 18. Columns for the Rb30Department Table Column Column Type Not Column Column Type Not Name with Length Null Name with Length Null

1 deptName VARCHAR(29) Yes 4 admrDept CHAR(3) Yes

2 deptNo CHAR(3) Yes 5 mgrNo CHAR(6) No

3 location CHAR(16) No

7.3.5 Change the Key Column Types Type changes of columns that are not part of a foreign key relationship require no special consideration. The change is local to the table.

Type changes of columns that are part of foreign key relationships impact the column type definitions in the primary key tables as well as the foreign key tables. Ensure that you change all columns that are part of a foreign key relationship.

144 Using VisualAge Smalltalk ObjectExtender ObjectExtender’s consistency check support prevents you from changing column types that are part of foreign key relationships. For details and how to work around or fix the strict implementation of the consistency check see Appendix F.2, “VapSchemaColumnEditorModel>> #areForeignKeysValid” on page 389.

Change all primary key column types and make the related foreign key column types match them.

Figure 115 shows foreign key relationships with inconsistent key type definitions.

Figure 115. Inconsistent Foreign Key Relationships

Note: For some unknown reason ObjectExtender did not generate NOT NULL for the column with the (logical) name Rb30Department_deptNo (Figure 115), even though we specified Required in the model (Figure 106 on page 134). Open the Column Editor and uncheck the Allow nulls check box (Figure 113 on page 141, left).

Finally, change the qualifier in the tables. Open a Class Editor by double-clicking on each table in the Tables list in the Schema Browser and change the qualifier to USERID (see Note on page 67).

Save the schema by selecting Schemas->Save Schema... from the menu bar in the Schema Browser. Use application name Rb30EmpDeptSchemaApp and storage class name Rb30EmpDeptSchema.

Save the mapping. Select Datastore_Maps->Save Datastore Map from the menu bar in the Map Browser to do so. Use application name Rb30EmpDeptMapApp and storage class name Rb30EmpDeptMap.

7.4 Generate Physical Database You have now created a consistent model, schema, and data store map. Now you have to generate the physical database itself.

Employee and Department Top-Down 145 You can preview the generated SQL code as an appendix to the textual summary of the schema by selecting Schemas->Generate DDL Script for Schema Creation from the menu bar of the Schema Browser. Copy and paste the SQL preview into a file and save the file for review with database designers, for later table (re-)creation outside ObjectExtender, or for the database administrator if he or she is the only one with the access rights to create database tables.

To create the tables, keys, and constraints in ObjectExtender, select Schemas->Import/Export Schema->Export Entire Schema to Databa se... from the menu bar in the Schema Browser. When you do this, you can use the System Transcript to check that the generated SQL code executed without errors, except for errors on the DROP TABLE commands if the tables do not currently exist. See Appendix D.2, “Rb30DBCR.txt - DB Table Creation Report” on page 365 for the generated and executed SQL code for the Employee and Department - Top-Down sample.

7.5 Generate Service Classes The last step in generating the persistence support is to generate the service classes. In the Map Browser, select Datastore Maps->Generation Options from the menu bar to set the required generation options. In the database connection information specify only the Connection type, so you will be asked for the rest at runtime and you can select either your generated database or the DB2 sample database as the data source. For the code generation application specify the application name Rb30EmpDeptRb30empdeptServicesApp. Finally, perform the service class generation by selecting Datastore Maps->Generate Services from the menu bar.

At this point, we suggest versioning and releasing (and saving by exporting) the sample work you have done so far. See the steps in 5.3.3, “Version, Release, and Save the Sample Work” on page 60.

7.6 Run Sample Headless with Scripts The run consists of two phases: 1. Set up sample objects 2. Navigate the objects links

Figure 116 shows the objects that are created and navigated.

146 Using VisualAge Smalltalk ObjectExtender department home employee home (home of the TOP (home of the DH and S... objects) EMP... objects) EH

Top Level TOP EMPTP0 (manager) Department Dept (no further employees)

Sublevel 1 S1A (no mgr) S1B EMP1B0 Departments Dept Dept A and B EMP1A1 EMP1B1

EMP1A2

(no administered departments) S2A (no manager) Dept (no employees)

EMPXXX

(no administered (employee with departments) no department)

Figure 116. Data Sample for Navigation

Each object has a name—all UPPERCASE and BOLD in the figure, for example, TOP and EMPTP0, that corresponds to the primary key of the object—if the object has one and is used to name temporary variables for the object in the scripts, for example vTOP and vEMPTP0, respectively.

The circular reference from the bottom to the top for the top-level department TOP, represents the special situation of the root element in the hierarchy given by the constraint: "Each element in the hierarchy must have a superior

Employee and Department Top-Down 147 element." Review the definition made for this administers association on page 133 and in Figure 106 on page 134. Even if there are no sublevels, the top- level item has one subordinate: itself. This special situation is demonstrated with the 1st Transaction of the setup script.

The scripts to set up the sample objects and navigate them are downloadable as a workspace file, Rb30ObjW.wsp (see Appendix C, “Downloadables, ConfigMaps, Applications” on page 355.)

The script detects some problems and gives a short description of the solutions. For more details see appendixes: • F.3, “Required Self-Associations” on page 391 • F.4.1, “Forward: Nil-Ability” on page 393 • F.4.2, “Backward: Individual Access Solution (Overriding)” on page 394

7.6.1 Set up Sample Objects The setup part of the script below performs a global ObjectExtender reset and an activate of the data store—operations that can be also invoked from the Status Tool. After that, the sample objects are created and persisted in three transactions. If something goes wrong, execute the part of the script entitled CLEANUP and start over. You can also reexport the entire schema to the database. This reexport drops the tables and you have a clean restart.

Note that all association assignments use objects, not keys.

" Rb30ObW.wsp " " Rb30 Employee and Department Object setup and navigate Workspace " " ********************************************************************************* " "(in Tools set: Basic Trace, Enable Inspectors)"

" ======BEG SETUP SCRIPTS ======" • '01 Perform a global reset of ObjectExtender' vapTrace. [ System vapReset. '' vapTrace ] value " --->" • '02 Activate the Rb20EmployeeHome''s data store.' vapTrace. [ Rb30EmpDeptRb30empdeptDataStore singleton activate. '' vapTrace ] value " --->" "Asks 3 times for a database connection specification if the database connection is not completely defined. Choose 'Share'. ObjectExtender maintains several sessions."

148 Using VisualAge Smalltalk ObjectExtender • '03 1st Trx: Add TOP department.' vapTrace. [ | trx vTOP | trx := Transaction begin: '1st'. (vTOP := Rb30DepartmentHome singleton create) deptNo: 'TOP'; deptName: 'TOP Department'; location: 'TOP location'; administrativeDepartment: vTOP. trx commit. '' vapTrace ] value " --->" /////////////////////// DEBUGS - STACK OVERFLOW - ZAP //////////////////////////////// a Rb30Department(nil) " ---> DEBUGGER with a STACK OVERFLOW! There is no way to overcome the stack overflow than to give ObjectExtender a small kick...: Complement the 'Resource>>#updateIndex:in:startingAt:' method in the line 'aTargetBo notNil' with a recursion check to 'aTargetBo notNil & (TargetBo ~~ version businessObject)', close the debugger, and re-execute step 03." ////////////////////////////////// REPEAT STEP 03 ///////////////////////////////////////////////////// • '04 Check TOP department. NOTE: Resume in the Debugger.' vapTrace. [ | vTOP stream | System vapReset. Rb30EmpDeptRb30empdeptDataStore singleton activate. vTOP := Rb30DepartmentHome singleton findByDeptNo: 'TOP'. (stream := WriteStream on: String new) nextPutAll: '* self: '; nextPutAll: vTOP printString; cr; nextPutAll: '* administrativeDepartment: '; nextPutAll: vTOP administrativeDepartment printString; cr; nextPutAll: '* departments: '; nextPutAll: (vTOP departments collect: [:d | d deptNo]) printString ; cr; nextPutAll: '* manager: '; nextPutAll: vTOP manager printString. 'Shows TOP with TOP in departments and as administrativeDepartment, and nil as manager' vapTrace. self halt. "Double click on vTOP, employees, departments, stream, and swipe over list to see the values." stream contents vapTrace. '' vapTrace ] value " ---->" • '05 Add S1A, S2A, and S1B departments.' vapTrace. [ | trx vTOP vS1A vS1B vS2A | System vapReset. Rb30EmpDeptRb30empdeptDataStore singleton activate. trx := Transaction begin: '2nd'. vTOP := Rb30DepartmentHome singleton findByDeptNo: 'TOP'. vS1A := Rb30DepartmentHome singleton create deptNo: 'S1A'; deptName: 'S1A Department'; location: 'S1A location';

Employee and Department Top-Down 149 yourself. vS1B := Rb30DepartmentHome singleton create deptNo: 'S1B'; deptName: 'S1B Department'; location: 'S1B location'; yourself. vTOP addDepartments: vS1B. vS1A addDepartments: ( vS2A := Rb30DepartmentHome singleton create deptNo: 'S2A'; deptName: 'S2A Department'; location: 'S2A location'; yourself ). trx commit. '' vapTrace ] value " --->" • '06 Add employees & managers except one to departments.' vapTrace. [ | trx vTOP vS1A vS1B vEMPTP0 vEMP1B0 | System vapReset. Rb30EmpDeptRb30empdeptDataStore singleton activate. trx := Transaction begin: '3rd'. (vTOP := Rb30DepartmentHome singleton findByDeptNo: 'TOP') addEmployees: ( (vEMPTP0 := Rb30EmployeeHome singleton create) empNo: 'EMPTP0'; edLevel: 0; firstNme: 'EMPTP0first'; midInit: 'T'; lastName: 'EMPTP0last'; yourself ); manager: vEMPTP0. vTOP departments do: [ :dept | dept deptNo = 'S1A' ifTrue: [ dept addEmployees: ( Rb30EmployeeHome singleton create empNo: 'EMP1A1'; edLevel: 1; firstNme: 'EMP1A1first'; midInit: 'E'; lastName: 'EMP1A1last'; yourself ); addEmployees: ( Rb30EmployeeHome singleton create empNo: 'EMP1A2'; edLevel: 1; firstNme: 'EMP1A2first'; midInit: 'E'; lastName: 'EMP1A2last'; yourself ) ] ifFalse: [ dept deptNo = 'S1B' ifTrue: [ dept addEmployees: ( (vEMP1B0 := Rb30EmployeeHome singleton create) empNo: 'EMP1B0'; edLevel: 1; firstNme: 'EMP1B0first'; midInit: 'E'; lastName: 'EMP1B0last'; yourself ); addEmployees: ( Rb30EmployeeHome singleton create empNo: 'EMP1B1'; edLevel: 1; firstNme: 'EMP1B1first'; midInit: 'E'; lastName: 'EMP1B1last'; yourself ); manager: vEMP1B0 ] ] ]. Rb30EmployeeHome singleton create

150 Using VisualAge Smalltalk ObjectExtender empNo: 'EMPXXX'; edLevel: 1; firstNme: 'EMPXXXfirst'; midInit: 'E'; lastName: 'EMPXXXlast'. trx commit. '' vapTrace ] value " --->" • '07 Verify Object Setup - List Department Hierarchy with...' vapTrace. [ | stack stream dept | System vapReset. Rb30EmpDeptRb30empdeptDataStore singleton activate. stream := WriteStream on: String new. (stack := OrderedCollection new) add: (Array with: 'TOP' with: ''). [stack notEmpty] whileTrue: [ | ind | ind := stack last at: 2. dept := Rb30DepartmentHome singleton findByDeptNo: (stack removeLast at: 1). dept departments do: [ :d | d ~~ dept ifTrue: [stack add: (Array with: d deptNo with: ind , '| ')] ]. stream cr; nextPutAll: ind; nextPutAll: '|---'; nextPutAll: dept printString; cr; nextPutAll: ind; nextPutAll: '| |---admrDept: '; nextPutAll: dept administrativeDepartment printString; cr; nextPutAll: ind; nextPutAll: '| |---departments: '; nextPutAll: (dept departments collect: [:s | s deptNo]) printString; cr; nextPutAll: ind; nextPutAll: '| |---manager: '; nextPutAll: (dept manager ~~ nil ifTrue: [dept manager empNo] ifFalse: ['none']); cr; nextPutAll: ind; nextPutAll: '| |---employees: '; nextPutAll: (dept employees collect: [:e | e empNo]) printString ]. stream contents vapTrace. '' vapTrace ] value " --->"

" ======END SETUP SCRIPTS ======"

" ======BEG CLEANUP ======"

• 'X1 Remove all managers from the departments.' vapTrace. [ | trx | System vapReset. Rb30EmpDeptRb30empdeptDataStore singleton activate. trx := Transaction begin: 'X1'. Rb30DepartmentHome singleton allInstances do: [ :d | d manager: nil ]. trx commit. '' vapTrace ] value " ---> /////////////////////////////// MANAGERS STILL SET - ZAP ///////////////////////////////////// Change the generated method below (or change the generater class StRelation):

Employee and Department Top-Down 151 Rb1to1DepartmentToManagerRelationship >>#primGetForwardKeyForLink: aLink "Get the forward key value to populate the passed link" | key | key := aLink source managerKey. aLink target isInitialTarget ifFalse: [ key := key class new ]. ^key ////////////////////////////////// REPEAT STEP X1 ///////////////////////////////////////////////////// • 'X2 Remove all employees.' vapTrace. [ | trx | System vapReset. Rb30EmpDeptRb30empdeptDataStore singleton activate. trx := Transaction begin: 'X2'. Rb30EmployeeHome singleton allInstances do: [ :e | e markRemoved ]. trx commit. '' vapTrace ] value " --->" • 'X3 Remove the departments recursively from bottom in single Trx''s.' vapTrace. [ | trx dept | System vapReset. Rb1toMSelfReqRb1tomselfreqDataStore singleton activate. trx := Transaction begin: 'X3'. dept := Rb1toMSRDepartmentHome singleton findByDeptNo: 'TOP'. [dept ~~ nil] whileTrue: [ | departmentsTemp deptTemp deleteTrx | (departmentsTemp := dept departments) isEmpty ifTrue: [ (deptTemp := dept administrativeDepartment) removeDepartments: dept. dept markRemoved; vapTrace. trx commit. trx := Transaction begin: 'X3...'. dept := deptTemp ] ifFalse: [ dept := dept ~~ (deptTemp := departmentsTemp first) ifTrue: [deptTemp] "first is last on top" ifFalse: [ dept ~~ (deptTemp := departmentsTemp last) ifTrue: [ deptTemp] ifFalse: [dept markRemoved; vapTrace. nil] ] ] ]. "TOP" trx commit. '' vapTrace ] value " --->"

" ======END CLEANUP ======"

Look at the downloadable workspace file Rb30ObjW.wsp (Appendix C, “Downloadables, ConfigMaps, Applications” on page 355) to see the the

152 Using VisualAge Smalltalk ObjectExtender complete System Transcript output. The following extracts are from the following two steps: • 04 Check TOP department • 07 Verify Object Setup - List Department Hierarchy with Contents

Note that the employee entry EMPXXX does not show up, because its department attribute is nil (and the managedDepartment returns nil too).

" ======TRANSCRIPT OUTPUT ======"

• (04 Check Top Department.)

'Shows TOP with TOP in departments and as administrativeDepartment, and nil as manager' '* self: a Rb30Department(''TOP'') * administrativeDepartment: a Rb30Department(''TOP'') * departments: OrderedCollection(''TOP'' ) * manager: nil'

• (07 Verify Object Setup - List Department Hierarchy with Contents)

|---a Rb30Department(''TOP'') | |---admrDept: a Rb30Department(''TOP'') | |---departments: OrderedCollection(''TOP'' ''S1A'' ''S1B'' ) | |---manager: EMPTP0 | |---employees: OrderedCollection(''EMPTP0'' ) | |---a Rb30Department(''S1B'') | | |---admrDept: a Rb30Department(''TOP'') | | |---departments: OrderedCollection() | | |---manager: EMP1B0 | | |---employees: OrderedCollection(''EMP1B0'' ''EMP1B1'' ) | |---a Rb30Department(''S1A'') | | |---admrDept: a Rb30Department(''TOP'') | | |---departments: OrderedCollection(''S2A'' ) | | |---manager: none | | |---employees: OrderedCollection(''EMP1A2'' ''EMP1A1'' ) | | |---a Rb30Department(''S2A'') | | | |---admrDept: a Rb30Department(''S1A'') | | | |---departments: OrderedCollection() | | | |---manager: none | | | |---employees: OrderedCollection()' ''

Figure 117 shows the "self halt." in the step 04 Check Top Department.

Employee and Department Top-Down 153 Figure 117. Inspecting the TOP Department in the Debugger

7.6.2 Navigate the Objects To navigate objects, you need to know which methods are available to help you. Use the VisualAge Organizer and the Class Editor to look at the application and classes where the model code is stored. For a first example, Figure 118 shows the methods available in the Rb30DepartmentHome class of the Rb30EmpDeptApp application for creating and accessing departments.

154 Using VisualAge Smalltalk ObjectExtender Figure 118. Access Methods Implemented by Rb30DepartmentHome

You can see the simple and inherited create method (Figure 119) when you change the method visibility to the immediate superclass by selecting Methods->Visibility->To Named Class... from the menu bar and selecting PersistentHomeCollection in the selection dialog.

Figure 119. Usual Access Methods of Rb30DepartmentHome

Employee and Department Top-Down 155 As a second example, Figure 120 shows the methods available in the Rb30Department class of the Rb30EmpDeptApp application for accessing the associated objects.

Figure 120. Access Methods of Rb30Department

You can use the same approach to find out which methods are available for the other objects.

For Employee and Department sample, you have to test the following six navigation cases which are related to the three bidirectional navigable associations: 1. Find the departments for a department (if any). 2. Find a department’s administrative department (there must be one). 3. Find employees for a department (if any). 4. Find an employee’s department (if any). 5. Find the manager (if any) of a department. 6. Find the department (if any) an employee manages.

156 Using VisualAge Smalltalk ObjectExtender To carry out these tests, it is only necessary to have read access to the database. Therefore perform the tests within the context of the global shared transaction, without begining or committing a transaction. Start the sequence by executing this Smalltalk expression: • 'N0 Setup Navigate (OE general reset, data store activate.' vapTrace. [ System vapReset. Rb30EmpDeptRb30empdeptDataStore singleton activate. '' vapTrace ] value " --->"

Finding the Departments for a Department (If Any) Obviously, there are roughly two kinds of departments: those that administer others (S1A and TOP), and those that do not (S1B, and S2A)—so-called nodes and leaves. The element on the top is called root and also administers itself—a given for this Employee and Department sample. In the unusual case of a single department, the root is a special leaf administering only itself. This was the situation when we checked the TOP department after adding it (page 149).

Executing the following Smalltalk expressions: • 'N1a Find the departments of the S1A department.' vapTrace. [ ( ( Rb30DepartmentHome singleton findByDeptNo: 'S1A') departments collect: [:d | d deptNo] ) vapTrace. '' vapTrace ] value " --->" • 'N1b Find the departments of the S1B department.' vapTrace. [ ( ( Rb30DepartmentHome singleton findByDeptNo: 'S1B') departments collect: [:d | d deptNo] ) vapTrace. '' vapTrace ] value " --->" • 'N1c Find the departments of the TOP department.' vapTrace. [ ( ( Rb30DepartmentHome singleton findByDeptNo: 'TOP') departments collect: [:d | d deptNo] ) vapTrace. '' vapTrace ] value " --->" you should get the following output in the System Transcript (some lines of the trace, such as the SQL, have been removed from the output, but are visible in the downloadable script file): • for N1a—S1A departments: OrderedCollection('S2A') • for N1b—S1B departments: OrderedCollection() • for N1c—TOP departments: OrderedCollection('TOP' 'S1A' 'S1B' )

Note that the TOP department is included its departments.

Employee and Department Top-Down 157 Finding a Department’s Administrative Department Every department has an administrative department—the root or TOP department itself also has an administrative department.

Executing the following Smalltalk expressions: • 'N2a Find the administr. dept. of the S1A department.' vapTrace. [ (Rb30DepartmentHome singleton findByDeptNo: 'S1A') administrativeDepartment deptNo vapTrace. '' vapTrace ] value " --->" • 'N2b Find the administr. dept. of the TOP department.' vapTrace. [ (Rb30DepartmentHome singleton findByDeptNo: 'TOP') administrativeDepartment deptNo vapTrace. '' vapTrace ] value " --->"

you should get the following output in the System Transcript (again, some lines of the trace have been removed): • for N2a—S1A’s administrative Department: 'TOP' • for N2b—TOP’s administrative Department: 'TOP'

Finding Employees for a Department (if Any) Executing the following Smalltalk expressions: • 'N3a Find the employees of the S1B department.' vapTrace. [( (Rb30DepartmentHome singleton findByDeptNo: 'S1B') employees collect: [:e | e empNo] ) vapTrace. '' vapTrace ] value " --->" • 'N3b Find the employees of the S2A department.' vapTrace. [ ( (Rb30DepartmentHome singleton findByDeptNo: 'S2A') employees collect: [:e | e empNo] ) vapTrace. '' vapTrace ] value " --->"

you should get the following output in the System Transcript: • for N3a—S1B’s employees: OrderedCollection('EMP1B0' 'EMP1B1' ) • for N3b—S2A’s employees: OrderedCollection()

Finding an Employee’s Department (If Any) Executing the following Smalltalk expressions: • 'N4a Find the work department of empl. EMP1B0 (if any).' vapTrace. [ (Rb30EmployeeHome singleton findByEmpNo: 'EMP1B0') workDepartment vapTrace. '' vapTrace ] value " --->"

158 Using VisualAge Smalltalk ObjectExtender • 'N4b Find the work department of empl. EMPXXX (if any).' vapTrace. [ (Rb30EmployeeHome singleton findByEmpNo: 'EMPXXX') workDepartment vapTrace. '' vapTrace ] value " --->" you should get the following output in the System Transcript: • for N4a—EMP1B0’s work department: a Rb30Department('S1B') • for N4b—EMPXXX’s work department: nil

Note that a non-required association returns nil if not set—the work department of employee EMPXXX is not set.

Finding the manager (if any) of a department Executing the following Smalltalk expressions: • 'N5a Find the manager of the S1B department (if any).' vapTrace. [ (Rb30DepartmentHome singleton findByDeptNo: 'S1B') manager vapTrace. '' vapTrace ] value " --->" • 'N5b Find the manager of the S1A department (if any).' vapTrace. [ (Rb30DepartmentHome singleton findByDeptNo: 'S1A') manager vapTrace. '' vapTrace ] value " --->" you should get the following output in the System Transcript: • for N5a—S1B’s manager: a Rb30Employee('EMP1B0') • for N5b—S1A’s manager: nil

Note that a non-required association returns nil if not set—the work department of employee EMPXXX is not set.

Finding the Department (If Any) an Employee Manages Obviously, there are two kind of employees: those who manage a department (EMPTP0 and EMP1B0) and those who do not (EMP1A1 EMP1A2, and EMP1B1. This time the script not only vapTraces to the System Transcript but also opens an inspector on the result object.

Before you execute the scripts, import the V2.0 [ JRW 09/07/98 ] a version of the VapInspectorUtilitiesApp of the downloadable library, RBOED.DAT, into your library, load it into your image, and enable the inspectors (see Appendix C, “Downloadables, ConfigMaps, Applications” on page 355 and 3.4, “Inspectors” on page 38).

Employee and Department Top-Down 159 Executing the following Smalltalk expressions: • 'N6a Find the managed department of EMP1B0 (if any).' vapTrace. [ ( (Rb30EmployeeHome singleton findByEmpNo: 'EMP1B0') managedDepartment ) vapTrace; inspect. '' vapTrace ] value " --->" • 'N6b Find the managed department of EMP1B1 (if any).' vapTrace. [ ( (Rb30EmployeeHome singleton findByEmpNo: 'EMP1B1') managedDepartment vapTrace ) inspect. '' vapTrace ] value " --->"

you should get the following output in the System Transcript (after working through Appendix F.4.2, “Backward: Individual Access Solution (Overriding)” on page 394)": • for N5a—S1B’s manager: a Rb30Employee('EMP1B0') • for N5b—S1A’s manager: nil

and for N5a the Inspector as shown in Figure 121.

Note that a non-required association returns nil if the backward link is not set—the manager of department S1A is not set.

Figure 121. Version V0.2... Enabled Inspector on the S1B Department

Note that the V0.2 [ JRW 09/07/98 ] a version enabled inspector does not show the instance variable values, in contrast to the version V0.1 [ JRW 08/28/98 ] a (Figure 122).

160 Using VisualAge Smalltalk ObjectExtender Figure 122. Version V0.1... Enabled Inspector on the S1B Department

7.7 Run Sample with User Interface Now you add GUIs to support the persistent classes. The first GUI is a single-window GUI and supports the creation and maintenance of employees of a given set of departments. The second GUI follows the same ideas introduced in Figure 94 on page 115. Create the Rb30EmpDeptViewApp for all Rb30 prefixed views.

7.7.1 Single Window GUI for Employee Creation and Maintenance The single window GUI (Figure 123; connections in Table 19 on page 163) is derived from the Rb20EmployeeView in Figure 71 on page 84 (for the window layout) and Figure 75 on page 92, second from top (for the use of the transactions).

As the workDepartment of an employee must be taken from the list of available departments, a drop-down list box is used to display the current department (selectedItem) and other available departments (items). The available departments are supplied by the departmentHome’s allInstances. The displayed attribute in the drop-down list box can be the deptNo, or—more flexible as discussed in the next paragraph—referenceString.

Because the workDepartment of an employe is now a department object and no longer just a string (foreign key), the list column with the header workDept and the employee need to be changed. It is convenient and a good practice to define a printable, derived, read-only attribute for reference purposes in lists that display more than just the object ID or another single attribute. A natural name for this printable, derived, read-only, string

Employee and Department Top-Down 161 attribute is referenceString, and if it is a promoted value, such as the referenceString of the workDepartment of the employee, the origin is added as a prefix, such as the workDepartmentReferenceString. For the definitions of the convenience methods see 7.7.2, “Convenience Attributes and Methods” on page 164.

Figure 123. Single Window View: Rb30EmployeeView

162 Using VisualAge Smalltalk ObjectExtender Table 19. Connections of Rb30EmployeeView Source (S) Feature of S Target (T) Feature of T

1 businessTransaction transaction employee transaction 2 businessTransaction transaction containerEmployees items 3 (businessTransaction,transaction value employeeHome allInstances --> containerEmployees,items)

4 concatenator string Window title 5 containerEmployees selectedItem employee self 6 containerEmployees selectionIsValid pbMarkForDelete enabled 7 departmentHome allInstances dropDownDepartments items

8 employee birthDate birthDateField object 9 employee lastName lastNameField object 10 employee midInit midInitField object 11 employee phoneNo phoneNoField object 12 employee job jobField object

13 employee comm commField object 14 employee bonus bonusField object 15 employee salary salaryField object 16 employee edLevel edLevelField object 17 employee sex sexField object

18 employee empNo empNoField object 19 employee firstNme firstNmeField object 20 employee hireDate hireDateField object 21 employee isPersistent tbIsPersistent selection 22 employee isNotPersistent trueElseFalse boolean

23 employee self notNil object 24 employee workDepartment dropDownDepartments selectedItem 25 employee fullName concatenator string1 26 employeeHome allInstances containerEmployees items 27 notNil notNil scrolledForm enabled

28 pbCancel clicked businessTransaction rollback 29 pbMarkForDelete clicked employee remove

30 pbNew clicked employeeHome create 31 (pbNew,clicked --> normalResult employee self employeeHome,create)

32 pbSave clicked businessTransaction commit 33 (pbSave,clicked --> errorResult errorPrompter promptFor: businessTransaction,commit)

34 trueElseFalse trueElseFalse empNoField editable

Employee and Department Top-Down 163 7.7.2 Convenience Attributes and Methods A view of an object has to display not only stored values, such as strings and numbers, but also references to associated objects. To make the GUIs consistent and the objects easy to use, it is a good practice and convenient to define for each object: • One attribute called referenceString that is a printable string and a clueful reference to the object • Attributes called xyzReferenceString—one for each associated object xyz—that are the (promoted) reference strings of the associated objects

and implement the corresponding method.

For the Employee the reference string attributes are: • referenceString built from the empNo lastName, firstName, and midInit • managedDepartmentReferenceString promoted from the managedDepartment • workDepartmentReferenceString promoted from the workDepartment

For the Department the reference string attributes are: • referenceString built from the deptNo, and deptName • administrativeDepartmentReferenceString promoted from the administrativeDepartment • managerReferenceString promoted from the manager (employee)

You can also think of more than one reference string attribute for an object, such as fullName for the Employee. In many places, where you want to show an employee reference, the natural composition of the fullname: "firstNme midInit. lastName" is not convenient.

Define the attributes in the Public Interface Editor (Figure 124). Define them as read-only because they are all derived from other attributes or objects. An attribute is defined as read-only by a blank Set selector. Note that the Event symbol is defined. It is required to signal changes when the attribute’s sources are changed.

164 Using VisualAge Smalltalk ObjectExtender Figure 124. The Read-Only referenceString Attribute in the Public Interface Editor

Generate the default methods (scripts) but no instance variables for the attributes, and complete the generated default methods as shown in Table 20 on page 166 and Table 22 on page 168. Categorize the methods in the category OERedbook, because categorizing is also a good practice.

Add the signaling for the derived attributes in the user code {post-Set} area of the setter methods of the contributing attributes (Table 21 on page 167 and Table 22 on page 168) and categorize the changed Generated Accessors also in

Employee and Department Top-Down 165 the category OERedbook-modified. Note that methods 7 to 10 in Table 21 on page 167 are private. Table 20. Reference String and fullName Methods of Employee Rb30Employee>>#methods in category OERedbook

1 referenceString "Return the value of referenceString." | stream tempMidInit | ^(stream := WriteStream on: String new) nextPutAll: self empNo; nextPut: $ ; nextPutAll: self lastName; nextPutAll: ', '; nextPutAll: self firstNme; nextPutAll: ( (tempMidInit := self midInit asString) notEmpty ifTrue: [stream nextPut: $ ; nextPut: tempMidInit first. '.'] ifFalse: [''] ); contents

2 workDepartmentReferenceString "Return the value of workDepartmentReferenceString." ^self workDepartment referenceString

3 managedDepartmentReferenceString "Return the value of managedDepartmentReferenceString." ^self managedDepartment referenceString

4 fullName "Return the value of fullName." | stream tempMidInit | ^(stream := WriteStream on: String new) nextPutAll: self firstNme; nextPut: $ ; nextPutAll: ( (tempMidInit := self midInit asString) notEmpty ifTrue: [stream nextPut: tempMidInit first. '. '] ifFalse: [''] ); nextPutAll: self lastName; contents

166 Using VisualAge Smalltalk ObjectExtender Table 21. Employee Modifications to Signal Changes of Derived Attributes Rb30Employee>>#methods in categery OERedbook-modified

5 managedDepartment: aRb30Department "Set the value of the association managedDepartment in the current transaction."

"Begin user code {pre-Set}" "End user code"

self bom markModified. self managedDepartmentLink connectTo: aRb30Department.

"Begin user code {post-Set}" > self signalEvent: #managedDepartmentReferenceString > with: self managedDepartmentReferenceString. "End user code"

self signalEvent: #managedDepartment with: aRb30Department

6 workDepartment: aRb30Department ... > self signalEvent: #workDepartmentReferenceString > with: self workDepartmentReferenceString. ...

7 primEmpNo: aString ... self signalEvent: #referenceString with: self referenceString. ...

8 primFirstNme: aString ... > self signalEvent: #referenceString. > self signalEvent: #fullName with:self fullName. ...

9 primLastName: aString ... > self signalEvent: #referenceString. > self signalEvent: #fullName with:self fullName. ...

10 primMidInit: aString ... > self signalEvent: #referenceString. > self signalEvent: #fullName with:self fullName. ...

Employee and Department Top-Down 167 Table 22. Department referenceString Methods Rb30Department>>#methods in category OERedbook

1 referenceString "Return the value of referenceString."

^(WriteStream on: String new) nextPutAll: self deptNo; nextPut: $-; nextPutAll: self deptName; contents

2 administrativeDepartmentReferenceString "Return the value of administrativeDepartmentReferenceString."

^self administrativeDepartment referenceString

3 managerReferenceString "Return the value of managerReferenceString."

^self manager referenceString

168 Using VisualAge Smalltalk ObjectExtender Table 23. Department Modifications to Signal Changes of Derived Attributes Rb30Employee>>#methods in categery OERedbook-modified

4 administrativeDepartment: aRb30Department "Set the value of the association administrativeDepartment in the current trx."

"Begin user code {pre-Set}" "End user code"

self bom markModified. self administrativeDepartmentLink connectTo: aRb30Department.

Begin user code {post-Set}" > self signalEvent: #administrativeDepartmentReferenceString > with: self administrativeDepartmentReferenceString. "End user code"

self signalEvent: #administrativeDepartment with: aRb30Department

5 manager: aRb30Employee .... > self signalEvent: #managerReferenceString > with: self managerReferenceString. ....

6 primDeptName: aString ... > self signalEvent: #referenceString with: self referenceString. ...

7 primDeptNo: aString .... > self signalEvent: #referenceString with: self referenceString. ....

Note that methods 6 and 7 in Table 23 are private.

You may be wondering why methods 2 and 3 of the employee (Table 20 on page 166) and method 3 of the department (Table 22 on page 168) do not check nil. The UndefinedObject nil has an extension in application Rb99VasXOeApp that makes the nil-check obsolete (Table 24). Table 24. UndefinedObject Extension referenceString UndefinedObject>>#referenceString

referenceString

^'~none'

Employee and Department Top-Down 169 7.7.3 GUI with List and Detail Views Supporting Drag and Drop The GUI with list and detail views follows the concept explained in 6.5, “Adding the User Interface” on page 115 with two additions: • Drag-source support in list views • Reusable form with drag and drop support for single references in detail views • Detail views with drag and drop support

Drag-Source Support in List Views The Rb30DepartmentListView (Figure 125) and Rb30EmployeeListView (Figure 126) are built the same way as the Rb21EmployeeListView (Figure 95 on page 116) in 6.5.1, “Create a List View” on page 116. The only major difference—besides the prefix Rb30 and the additional Department scope—is the use of the reference string attribute names for the attributeName setting of the container detail columns, and the drag-source support in the actual list. The reference string attributes in the view are used to achieve a reasonable display of the references.

Figure 125. Rb30DeaprtmentListView

170 Using VisualAge Smalltalk ObjectExtender Figure 126. Rb30EmployeeListView

The manager, admrDept (administrativeDepartment), wDept (workDepartment), and mgdDept (managedDepartment) columns use the reference string names in their attributeName settings. The reference strings are described in 7.7.2, “Convenience Attributes and Methods” on page 164. Table 12 on page 118 lists the connections.

Reusable Form with Drag and Drop Support for Single References A read-only entry field is sufficient to display the reference string of a referenced object in a detail view, but not for the assignment of a reference. The form Rb99SingleReferenceForm (Figure 127) in Rb90UtilityViewsApp supports drag-source and drag-target operations and is the replacement for read-only entry field or drop-down lists for single references as used in, for example, Rb30EmployeeView in Figure 123 on page 162.

The Rb99SingleReferenceForm is used in the Rb31EmployeeDetailView (Figure 131 on page 180) for the workDept and mgdDept (managedDepartement) reference. In the Rb31DepartmentDetailView (Figure 129 on page 176) it is used for admrDept (administrativeDepartment) and manager references.

Employee and Department Top-Down 171 As opposed to the entry field’s String object, from the referenced object the reference form’s object is the referenced object itself. Therefore the connection of the referenceString feature of the business object to the object of the entry field is replaced with the connection from the object reference feature to the referenceObject feature of the reference part in the form. To supply the display functionality of the entry field, namely, the display of the reference string of the referenced object, the reference part in the form promotes the referenceObject and the referenceAttributeName. The referenceAttributeName has to be specified at edit time and has the default value referenceString.

Note: The reference part is an AbtListView part and needs special care for the drag and drop, because it features only strings in the standard drag and drop support. The scripts in Table 26 on page 174, the orderedCollection variable, and the OrderedCollection Factory provides the special care. We chose AbtListView part for the reference part because of the easy use of its already built-in select and drag-and-drop support.

1. 2. 3.

Figure 127. Rb99SingleReferenceForm with Drag and Drop Support

172 Using VisualAge Smalltalk ObjectExtender The Clear and List menu choices are to reset the referenceObject to nil and fire the referenceList event to, for example, open the list of all objects available for a reference.

For more information about the Rb99SingleReferenceForm part consult the following items: • Figure 128—shows the parts list. • Table 25 on page 174—lists the connections. • Table 26 on page 174—lists the events and scripts of the drag and drop supporting reference event-to-script connections. • Table 27 on page 175—lists all promoted features.

Figure 128. Rb99SingleReferenceForm Parts List

Employee and Department Top-Down 173 Table 25. Connections of Rb99SingleReferenceForm Source (S) Feature of S Target (T) Feature of T

1 menuChoiceClear clicked referenceObject self 2 menuChoiceClear clicked orderedCollection removeAtIndex:

3 orderedCollection self reference items

4 reference dragStartRe Rb99SingleReferenceForm dragStartReque quested sted:

5 reference dragOver Rb99SingleReferenceForm dragOver:

6 reference dropped Rb99SingleReferenceForm dropped:

7 referenceObject self OrderedCollection new

8 (referenceObject,self --> normalResult orderedCollection self OrderedCollection,new)

9 (referenceObject,self --> normalResult orderedCollection add: OrderedCollection,new)

10 ( (referenceObject,self --> anObject referenceObject self OrderedCollection,new),normal Result --> orderedCollection,add:)

11 referencePpopUpMenu self reference menu

Table 26. Drag and Drop Support Events and Scripts in Rb99SingleReferencesForm Event of reference Subpart Script

1 dragStartRequested dragStartRequested: aDragDrop "Add the dragged object." aDragDrop sourceItems add: (self subpartNamed: 'referenceObject') items first

2 dragOver dragOver: aDragDrop "Return the drop vote." ( (aDragDrop sourceItems size == 1) & ( (self subpartNamed: 'reference') abtAtAttribute: #dropAcceptableClassList ) includes: aDragDrop sourceItems first class) ifTrue: [aDragDrop vote: #(3)] ifFalse: [aDragDrop vote: #()]

3 dropped dropped: aDragDrop "Drop to the reference object." (self subpartNamed: 'referenceObject') value: aDragDrop sourceItems first

174 Using VisualAge Smalltalk ObjectExtender Table 27. Promoted Features in Rb99SingleReferenceForm Feature Name Subpart Name [Class] (Promoted Feature) Type:

Purpose

1 referenceObject referenceObject [AbtVariable] (self) attribute:

variable to hold the referenced object (for example, the workdepartment of an employee)

2 referenceAttributeName reference [AbtListView] (attributeName) attribute:

name of the attribute to display (a symbol, for example: referenceString)

3 referenceDefaultActionRequested reference [AbtListView] (defaultActionRequested) event:

event fired on the double-click to, for example, open a detail view on the referenceObject

4 referenceAcceptableDropClassList reference [AbtListView] (acceptableDropClassList) attribute:

an OrderedCollection of classes that are accepted as drop items to set up the reference to

5 referenceNillable menuChoiceClear [AbtPushButtonView] (enabled) attribute:

boolean to enable the menu choice for the reset (set to nil) of the reference object

6 referenceListEnabled menuChoiceList [AbtPushButtonView] (enable) attribute:

boolean to enable a search and list view on available reference objects to select and drag from

7 referenceListClicked menuChoiceList [AbtPushButtonView] (clicked) event:

event to open a search and list view on available reference objects to select and drag from

Employee and Department Top-Down 175 Detail Views with Drag and Drop Support Rb30DepartmentDetailView (Figure 129) and Rb30EmployeeDetailView (Figure 131) are built the same way as Rb21EmployeeDetailView (Figure 96 on page 119) in 6.5.2, “Create a Detail View” on page 119.

Figure 129. Rb30DepartmentDetailView

Figure 129 shows the Rb30DepartmentDetailView parts list.

176 Using VisualAge Smalltalk ObjectExtender Figure 130. Rb30DepartmentDetailView Parts List

Employee and Department Top-Down 177 The only major differences—besides the prefix Rb30 and the additional Department scope—are: • The use of Rb99SingleReferenceForm for the referenced objects, such as the admrDept (administrativeDepartment), manager, mgndDepartment (managedDepartment), and workDept (workDepartment) • The list support for objects that can be referenced • The drag and drop support in multiple references (lists), such as the list of administered departments and employees • The hasModifications support to indicate whether there are modified objects in the transaction • TheunsavedChanges support on the window close request with the options save, discard, and cancel • The transaction name display support • The Department specific support to set the self reference in a root department.

The drag and drop support for multiple references (lists) needs the connections from the dropped and dragCompletedRequest events to the new dragged... and dropped... methods of the Department (Table 28 on page 179).

The new methods are required, because the standard drag and drop implementation expects the removeAll: and addAll: protocol of the Collection, which is not understood by ObjectExtender’s LinkCollection.

The new methods are defined as actions in the public interface. The public interface definitions of the departments and employees read-only attributes must be completed with the event symbols so that the drag and drop action shows updates.

For the required ObjectExtender modifications and extensions to enable the hasModifications support, see Appendix F.6, “Transaction>>#hasModifications Support” on page 400.

For the transaction name display support, see Appendix F.7, “Transaction Name Form” on page 405.

The spider webs of the Rb30DepartmentDetailView in Figure 129 on page 176 and Rb30EmployeeDetailView in Figure 131 on page 180, the parts list in Figure 132 on page 181 and, and the connections listed in Table 29 on page 182 through Table 35 on page 188 make the need for simplification obvious (see comment on page 94). The Rb99SingleReferenceForm could incorporate

178 Using VisualAge Smalltalk ObjectExtender the opening of the detail and list view, and a Rb99MultipleReferenceForm could be built to support drag-drop supported lists with opening of the items detail and list view. Table 28. Rb30Department Methods for the Drag-Drop Support Rb30Department

1 draggedDepartments: aDragDrop

"Perform the draggedDepartments action."

self signalEvent: #departments with: self departments

2 droppedDepartments: aDragDrop

"Perform the droppedDepartments action."

aDragDrop sourceItems do: [ :si | self addDepartments: si ]. self signalEvent: #departments with: self departments

4 draggedEmployees: aDragDrop

"Perform the draggedEmployees action."

self signalEvent: #employees with: self employees

5 droppedEmployees: aDragDrop

"Perform the droppedEmployees action."

aDragDrop sourceItems do: [ :si | self addEmployees: si ]. self signalEvent: #employees with: self employees

Employee and Department Top-Down 179 Figure 131. Rb30EmployeeDetailView

Figure 132 shows the Rb30EmployeeDetailView parts list.

180 Using VisualAge Smalltalk ObjectExtender Figure 132. Rb30EmployeeDetailView Parts List

Employee and Department Top-Down 181 Table 29. Connections of the Rb30DepartmentDetailView (Part 1 of 4)

Source (S) Feature of S Target (T) Feature of T

1 admrDeptIsNil isNilObject department administrative Department

2 (admrDeptIsNil,isNilObject --> value department self department,administrativeDepartmen t)

3 admrDeptReferenceForm referenceDefaultActio DepartmentDetailView new nRequested

4 admrDeptReferenceForm referenceListClicked DepartmentListView new

5 (admrDeptReferenceForm,reference normalResult departmentDetailView modelTmp DefaultActionRequested --> DepartmentDetailView,new)

6 (admrDeptReferenceForm,reference normalResult departmentDetailView openWidget DefaultActionRequested --> DepartmentDetailView,new)

7 ((admrDeptReferenceForm,reference value admrDeptReferenceForm object DefaultActionRequested --> DepartmentDetailView,new),normalR esult --> departmentDetailView,modelTmp)

8 businessTransaction transaction department transaction

9 businessTransaction hasModifications pbCancel enabled

10 businessTransaction hasModifications hasModifications boolean

11 concatenator string Window title

12 containerDepartments defaultActionRequest DepartmentDetailView new ed

13 containerDepartments dropped department droppedDepart ments

14 containerDepartments dragCompleteReques department draggedDepart ted ments

15 (containerDepartments,defaultAction normalResult departmentDetailView modelTmp Requested --> DepartmentDetailView,new)

16 (containerDepartments,defaultAction normalResult departmentDetailView openWidget Requested --> DepartmentDetailView,new)

182 Using VisualAge Smalltalk ObjectExtender Table 30. Connections of the Rb30DepartmentDetailView (Part 2 of 4)

Source (S) Feature of S Target (T) Feature of T

17 ((containerDepartments,defaultActionR value containerDepartments selectedItem equested --> DepartmentDetailView,new),normalRes ult --> departmentDetailView,modelTmp)

18 containerEmployees defaultActionRequest EmployeeDetailView new ed

19 containerEmployees dropped department droppedEmplo yees

20 containerEmployees dragCompleteReques department draggedEmplo ted yees

21 containerEmployees selectionIsValid employeesMenuChoice enabled Remove

22 (containerEmployees,defaultActionReq normalResult employeeDetailView employeeTmp uested --> EmployeeDetailView,new)

23 (containerEmployees,defaultActionReq normalResult employeeDetailView openWidget uested --> EmployeeDetailView,new)

24 ((containerEmployees,defaultActionRe value containerEmployees selectedItem quested --> EmployeeDetailView,new),normalResul t --> employeeDetailView,employeeTmp)

25 deleteConfirmer okYesRetry department remove

26 deleteConfirmer okYesRetry businessTransaction commit

27 (deleteConfirmer,okYesRetry --> normalResult Window closeWidget businessTransaction,commit)

28 department isNotPersistent trueElseFalse boolean

29 department isPersistent pbDelete enabled

30 department departments containerDepartments items

31 department employees containerEmployees items

32 department deptNo deptNoField object

33 department deptName deptNameField object

34 department location loacationField object

35 department self deptNotNil object

Employee and Department Top-Down 183 Table 31. Connections of the Rb30DepartmentDetailView (Part 3 of 4)

Source (S) Feature of S Target (T) Feature of T

36 department referenceString concatenator string2

37 department administrativeDepar admrDeptReferenceFor referenceObject tment m

38 department manager mgrReferenceForm referenceObject

39 department transaction transactionForm transaction

40 DepartmentDetailView instance departmentDetailView self

41 DepartmentListView instance departmentListView self

42 DepartmentListView instance departmentListView openWidget

43 departmentsMenuChoiceList clicked DepartmentListView new

44 deptNotNil notNilObject admrDeptIsNil object

45 (deptNotNil,notNilObject --> value department administrativeDepar admrDeptIsNil,object) tment

46 EmployeeDetailView instance employeeDetailView self

47 EmployeeListView instance employeeListView self

48 EmployeeListView instance employeeListView openWidget

49 employeesMenu self containerEmployees menu

50 employeesMenuChoiceList clicked EmployeeListView new

51 employeesMenuChoiceRemove clicked extractor object

52 (employeesMenuChoiceRemove,cli value containerEmployees selectedItems cked --> extractor,object)

53 extractor eachExtractedItem department removeEmployees:

54 (extractor,eachExtractedItem --> normalResult containerEmployees refreshAllItems department,removeEmployees:)

55 hasModifications isTrue concatenator string1

56 hasModifications isFalse concatenator string1

57 hasUnsavedChanges isTrue UnsavedChanges promptFor:

58 hasUnsavedChanges isFalse Window closeWidget

59 (hasUnsavedChanges,isTrue --> associatedPart Window self UnsavedChanges,promptFor:)

60 mgrReferenceForm referenceDefaultAct EmployeeDetailView new ionRequested

184 Using VisualAge Smalltalk ObjectExtender Table 32. Connections of the Rb30DepartmentDetailView (Part 4 of 4)

Source (S) Feature of S Target (T) Feature of T

61 mgrReferenceForm referenceListClicked EmployeeListView new

62 (mgrReferenceForm,referenceDefaul normalResult employeeDetailView employeeTmp tActionRequested --> EmployeeDetailView,new)

63 (mgrReferenceForm,referenceDefaul normalResult employeeDetailView openWidget tActionRequested --> EmployeeDetailView,new)

64 ((mgrReferenceForm,referenceDefa value mgrReferenceForm object ultActionRequested --> EmployeeDetailView,new),normalRe sult --> employeeDetailView,employeeTmp)

65 pbCancel clicked businessTransaction rollback

66 pbDelete clicked deleteConfirmer prompt

67 pbSave clicked businessTransaction commit

68 pbSave enabled pbCancel enabled

69 (pbSave,clicked --> normalResult Window closeWidget businessTransaction,commit)

70 pbXInspect clicked Rb30DepartmentDetail inspect: View

71 (pbXInspect,clicked --> parameter1 department self Rb30DepartmentDetailView,inspect:)

72 trueElseFalse trueElseFalse deptNoField editable

73 UnsavedChanges noAbort Window closeWidget

74 UnsavedChanges okYesRetry businessTransaction commit

75 (UnsavedChanges,okYesRetry --> normalResult Window closeWidget businessTransaction,commit)

76 Window openedWidget department self

77 Window closeWidgetRequest Rb30DepartmentDetail closeWidgetReque View sted:

78 Window closeWidgetRequest hasUnsavedChanges boolean

79 (Window,closeWidgetRequest --> value businessTransaction hasModifications hasUnsavedChanges,boolean)

80 (Window,openedWidget --> value departmentTmp self department,self)

Employee and Department Top-Down 185 Table 33. Connections of the Rb30EmployeeDetailView (Part 1 of 3) Source (S) Feature of S Target (T) Feature of T

1 businessTransaction transaction employee transaction

2 businessTransaction hasModifications pbCancel enabled

3 businessTransaction hasModifications hasModifications boolean

4 concatenator string Window title

5 deleteConfirmer okYesRetry employee remove

6 deleteConfirmer okYesRetry businessTransaction commit

7 (deleteConfirmer,okYesRetry --> normalResult Window closeWidget businessTransaction,commit)

8 DepartmentDetailView instance departmentDetailView self

9 DepartmentListView instance departmentListView self

10 DepartmentListView instance departmentListView openWidget

11 employee isNotPersistent trueElseFalse boolean 12 employee empNo empNoField object 13 employee referenceString concatenator string2 14 employee firstNme firstNmeField object

15 employee midInit midInitField object 16 employee lastName lastNameField object 17 employee phoneNo phoneNoField object 18 employee isPersistent pbDelete enabled

19 employee job jobField object 20 employee edLevel edLevelField object 21 employee hireDate hireDateField object 22 employee sex sexField object

23 employee bonus bonusField object 24 employee comm commField object 25 employee salary salaryField object 26 employee birthDate birthDateField object

27 hasModifications isTrue concatenator string1

28 hasModifications isFalse concatenator string1

29 hasUnsavedChanges isTrue UnsavedChanges promptFor:

30 hasUnsavedChanges isFalse Window closeWidget

186 Using VisualAge Smalltalk ObjectExtender Table 34. Connections of the Rb30EmployeeDetailView (Part 2 of 3) Source (S) Feature of S Target (T) Feature of T

31 (hasUnsavedChanges,isTrue --> associatedPart Window self UnsavedChanges,promptFor:)

32 managedDeptRefForm referenceDefaultActi DepartmentDetailView new onRequested

33 managedDeptRefForm referenceListClicked DepartmentListView new

34 managedDeptRefForm referenceObject employee managedDe partment

35 (managedDeptRefForm,referenc normalResult departmentDetailView modelTmp eDefaultActionRequested --> DepartmentDetailView,new)

36 (managedDeptRefForm,referenc normalResult departmentDetailView openWidget eDefaultActionRequested --> DepartmentDetailView,new)

37 ( value managedDeptRefFor object (managedDeptRefForm,referenc m eDefaultActionRequested --> DepartmentDetailView,new),nor malResult --> departmentDetailView,modelTm p)

38 pbCancel clicked businessTransaction rollback

39 pbCancel enabled pbSave enabled

40 pbDelete clicked deleteConfirmer prompt

41 pbSave clicked businessTransaction commit

42 (pbSave,clicked --> normalResult Window closeWidget businessTransaction,commit)

43 pbXInspect clicked Rb30EmployeeDetail inspect: View

44 (pbXInspect,clicked --> parameter1 employee birthDate Rb30EmployeeDetailView,inspec t:)

45 trueElseFalse trueElseFalse empNoField editable

46 UnsavedChanges noAbort Window closeWidget

Employee and Department Top-Down 187 Table 35. Connections of the Rb30EmployeeDetailView (Part 3 of 3) Source (S) Feature of S Target (T) Feature of T

47 UnsavedChanges okYesRetry businessTransactio commit n

48 (UnsavedChanges,okYesRetry --> normalResult Window closeWidget businessTransaction,commit)

49 Window openedWidget employee self

50 Window closeWidgetRequest Rb30EmployeeDeta closeWidget ilView Requested:

51 Window closeWidgetRequest hasUnsavedChang boolean es

52 (Window,closeWidgetRequest --> value businessTransactio hasModificati hasUnsavedChanges,boolean) n ons

53 (Window,openedWidget --> value employeeTmp self employee,self)

54 workDeptRefForm referenceDefaultActi DepartmentDetailVi new onRequested ew

55 workDeptRefForm referenceListClicked DepartmentListView new

56 workDeptRefForm referenceObject employee workDepart ment

57 (workDeptRefForm,referenceDefau normalResult departmentDetailVi modelTmp ltActionRequested --> ew DepartmentDetailView,new)

58 (workDeptRefForm,referenceDefau normalResult departmentDetailVi openWidget ltActionRequested --> ew DepartmentDetailView,new)

59 ( value workDeptRefForm object (workDeptRefForm,referenceDefau ltActionRequested --> DepartmentDetailView,new),norma lResult --> departmentDetailView,modelTmp)

Figure 133 and Figure 134 show the views at runtime with the object setup as described in 7.6.1, “Set up Sample Objects” on page 148.

188 Using VisualAge Smalltalk ObjectExtender Figure 133. Rb30Department List and Detail View with Data

Figure 134. Rb30Employee List and Detail Views with Data

Employee and Department Top-Down 189 If you specified only the Connection type in the Database Connection Info dialog, you can use the GUIs to browse and maintain the Employee and Department tables of the DB2 sample database by specifying Sample as Data source at data store activation time.

Look at the Save and Reset buttons in the detail views in Figure 133 and Figure 134. In the department detail view they are disabled and indicate that no changes have yet been made to the department business object. In the employee detail view they are enabled and indicate that a change has already been made to the employee business object, namely, to the firstNme. Also, the window title shows with the leading asterisk (*) that modifications have already been made to the object. If you close the window, a prompter opens with a message about unsaved changes. The options to continue are: • Yes—Save the changes and close the window. • No—Do not save the changes. Discard them and close the window. • Cancel—Cancel the closing dialog and return to the detail view.

7.7.4 Transaction Save GUIs The GUIs for the department and employee details (Figure 129 on page 176 and Figure 131 on page 180) are "transaction save". Run the following scenario, and you will understand why and what happens: 1. Open the department list view with all departments (Figure 133 on page 189, left side). 2. Double-click on the S1A department in the list view (Figure 133 on page 189, left side) to open the detail view of the S1A department (Figure 135, left side). 3. Double-click on the S1B department in the list view (Figure 133 on page 189, left side) to open the detail view of the S1B department (Figure 135, right side). 4. Select the EMP1A2 employee in the S1A department’s list of employees and drag and drop it onto the S1B department’s list of employees (Figure 135). 5. Look at what happened (Figure 135, left and right side): • The drag-source object and transaction are still unchanged—the window title is not lead by an asterisk, and the S1A department’s list of employees still shows both employees, EMP1A1 and EMP1A2. • The drag-target or drop object and transaction have changed—the window title leads with an asterisk, the Save and Reset buttons are

190 Using VisualAge Smalltalk ObjectExtender enabled, and the S1B department’s employee list shows the dropped S1A2 employee.

Figure 135. Department Detail Views after a Drag and Drop of an Employee

Why? • The dropped event of the list is fired at first and invokes the droppedEmployees: method of the S1B department through the transacted variable. On passing the transacted variable the indirectly through the business transaction connected transaction became the current transaction in which the execution of the addEmployees: method invoked by the droppedEmployees: method (Table 28 on page 179) resulted in a modification to the S1B department. • The dragCompletedRequest event also fired a method of an object in a transacted variable, but because this draggedEmployees: method is empty, the object and transaction related to the variable remain unchanged. Even if you click on the Refresh button, the same list of S1A department employees appears as before—because the S1B transaction is not yet

Employee and Department Top-Down 191 committed. After a save of the S1B transaction the refresh of the S1A window shows one employee less in its list.

Suppose a typical concurrence situation: you opened—before you saved the S1B department—a third department, for example, the S2A department, dragged and dropped the same EMP1A1 employee from the S1A department to the S2A department, and committed the S2A department changes. The attempt to commit the S1B department changes fails—if you declared the workDept foreign key as part of the optimistic predicate. The commit has to fail, because the S2A commit came first—and served first.

To check out the concurrence, open the Map Browser (Figure 136).

Figure 136. Map Browser: Defining a Part of the Optimistic Predicate

In the Map Browser select Rb30EmpDeptRb30empdept from the Datastore Maps, select Rb30Employee from the Persistent Classes, select Rb30Employee from the Table Maps, select workDepartment from the Property Maps, and define the workDepartment as part of the optimistic predicate by selecting the Property Maps-> Be part of optimistic predicate from the menu bar. Regenerate the services, and—to see the transaction failure—connect the errorResult of the

192 Using VisualAge Smalltalk ObjectExtender businessTransaction to the prompFor: feature of an error message prompter or the object feature of a message display field that you add to the view.

Employee and Department Top-Down 193 194 Using VisualAge Smalltalk ObjectExtender Chapter 8. Employee and Department Bottom-Up

The Employee and Department Bottom-Up sample looks at two tables, the Employee and Department tables of the DB2 sample database (Figure 81 on page 105 and Figure 137 on page 195), and goes along the backward or bottom-up development path (see 2.2, “Backward or Bottom-Up: Start with the Database” on page 20 and Figure 11 on page 21). The sample uses the name prefix Rb31, except where parts from the previous sample are reused. The class diagram of the resulting object model looks as shown in Figure 101 on page 127.

Figure 137. DB2 Sample Database: Employee Table with Columns

To exercise this sample, you should have the DB2 sample database created and the DB2 database manager must be started. You can start the DB2 database manager in a system command window with db2start and create the DB2 sample database SAMPLE with db2sampl. The sample database installation uses your userid as the (high-level) qualifier (see also the Note on page 67). If the DB2 sample database is not created or not available, you can use the database and tables created, in 7.4, “Generate Physical Database” on page 145.

© Copyright IBM Corp. 1999 195 8.1 Import Schema from Relational Database (Metadata) The import of the schema from the database and the adjustment of the imported schema are similar to the import and adjustment for the single table described in 6.1, “Import Schema from Relational Database (Metadata)” on page 106.

For this sample name the schema Rb31EmpDept and use Rb1EmpDeptSchema for the storage class name and Rb1EmpDeptSchemaApp for the storage application name when saving the schema.

8.1.1 Select Tables and Import Schema In the Select Tables dialog (Figure 138), select the Employee and the Department and import the schema.

Figure 138. Select Tables Dialog: Select Department and Employee Table

Figure 139 shows the imported Rb31EmpDept schema.

196 Using VisualAge Smalltalk ObjectExtender Figure 139. Imported Database Schema Rb31EmpDept

8.1.2 Review and Adjust Imported Schema Because the captured tables have no primary key definitions in the database, the tables in the schema have no primary keys defined. In Figure 139, for example, the DEPARTMENT table has no columns marked with the two greater than signs (>>). You have to define the primary keys manually, using the Table Editor (Figure 50 on page 63). The EMPNO column has to be the primary key of the EMPLOYEE table, and the DEPTNO column has to be the primary key of the DEPARTMENT table.

Furthermore, the captured tables have no constraints in the database, so the schema has no foreign key relationships (the list of Foreign Key Relationships in the schema is empty in Figure 139). You have to add them manually in the Foreign Key Relationship Editor (Figure 140).

Employee and Department Bottom-Up 197 Figure 140. Foreign Key Relationship Editor: employee_workDepartment

Choose Foreign Keys->New Foreign Key Relationship... from the menu bar of the Schema Browser and use the information in Figure 141 and Table 36 on page 199 to create the three foreign key relationships.

Figure 141 shows the foreign key relationships that were generated in 7.3, “Generate Schema and Mapping: Metadata and Database” on page 138 to give you an idea of what they should look like, and Table 36 lists them adjusted for this sample.

Note: For this sample there is no need to define physical names or to check the Constraints exists in Database box, because we do not have to generate any DDL.

198 Using VisualAge Smalltalk ObjectExtender Figure 141. Foreign Key Relationships from Rb30EmpDept Schema

Table 36. Foreign Key Relationships for Rb31EmpDept Schema Foreign Key Relationship Name

Primary Key Table Foreign Key Table Foreign Key

1 administrativeDepartment_departments

DEPARTMENT DEPARTMENT ADMRDEPT

2 manager_managedDepartment

EMPLOYEE DEPARTMENT MGRNO

3 workDepartment_employees

DEPARTMENT EMPLOYEE WORKDEPT

The last step in preparing for the model and map generation is to prefix the logical table names with Rb31, using the Table Editor, because they will

Employee and Department Bottom-Up 199 define the model class names. Choose Tables->Edit Tables... to change the logical table names EMPLOYEE and DEPARTMENT to Rb1Employee and Rb31Department, respectively. Figure 142 shows the schema with the changed logical table names and the manually defined foreign key relationships.

Figure 142. Foreign Key Relationships of the Rb31EmpDept Schema

Note: The schema is now ready for the model and map generation, but not yet for the services generation. The last step in preparing for the services generation is to define how column values are converted to their mapped attribute and vice versa, using the Column Editor. Either select a column and click on the Edit button in the Table Editor or double-click on a column in the Schema Browser and select the appropriate converter. For CHAR(nn) columns and String attributes the VapTrimStringConverter fits best with Smalltalk’s String behavior and GUIs: Trailing blanks are trimmed on read and padded on write (see Figure 50 on page 63 and Figure 51 on page 64).

200 Using VisualAge Smalltalk ObjectExtender 8.2 Generate Model Definition and Mapping (Metadata) In 7.1, “Define Model (Metadata)” on page 127 we developed the model for the Employee and the Department by hand. It is open to us to use this model for developing our application and to generate the necessary mappings between the model and the schema by hand too, using the Map Browser. However, to illustrate the power of ObjectExtender, we actually generate the model from the schema that we have just imported and adjusted.

Choose Schemas->Generate Model from Schema from the menu bar in the Schema Browser to generate the model definitions and the mappings. Figure 143 through Figure 146 show the generated model classes and the generated mappings for the Employee and the Department.

Figure 143. Generated Rb1employee Model Class

Employee and Department Bottom-Up 201 Figure 144. Generated Rb31Employee Table Map

Figure 145. Generated Rb1department Model Class

202 Using VisualAge Smalltalk ObjectExtender Figure 146. Generated Rb31Department Table Map

8.2.1 Review and Adjust the Generated Model Definitions Now open the Model Browser and find the new model that has been created. It has been given the same name as the schema, Rb31EmpDept. Note that the relevant class attributes marked with (a) and class associations with corresponding (r) marked class attributes have been generated. (r) stands for relation (that is, a class association).

Note: By default ObjectExtender generates the associations navigable in both directions, the one-to-xxx associations in the direction, where the "one" side is in the left side of the Association Editor window, the "xxx" side (the foreign key side) as Many, and all non-(r) class attributes with Value not required.

ObjectExtender generated the names for the roles in the associations and the corresponding (r) class attributes. Because they are not particularly meaningful within the context of the application, and because they will become the method names in the future generated model code, we change them, using the Association Editor. In the Model Browser, double-click on either the (r) class attribute or the class association to open the Association Editor and modify the role names. Furthermore, in the Association Editor, adjust the Navigable, Many, and Required box, where required. Use the information in Figure 147 through Figure 149 to edit the class associations. (The easiest way for this sample is to select the Rb1department model class

Employee and Department Bottom-Up 203 in the Model Browser and double-click on each class association. In this way you catch all three class associations of the sample.)

Figure 147. administrativeDepartment_departments Association

Figure 148. manager_managedDepartment Association

204 Using VisualAge Smalltalk ObjectExtender Figure 149. workDepartment_employees Association

In the Model Editor, double-click on all non-(r) class attributes that which require a value, to open the Class Attribute Error and mark the Value Required check box. Use the information in Table 14 on page 128 and Table 15 on page 129 to determine, which non-(r) class attributes require a value.

The model is now reviewed and adjusted. When we saved the model for the first time, we named the application Rb31EmployeeModelApp and accepted the storage class name of Rb31EmployeeModel.

8.2.2 Review and Adjust the Generated Mappings Changing the names of an (r) class attribute or role name affects the mappings by creating mapping errors, indicated by: <>, <>, and <

Employee and Department Bottom-Up 205 Figure 150. Employee VapValidationErrors <>

Figure 151. Department VapValidationErrors <>

In the Map Browser select each persistent class and each table map with a broken cluster map and choose Table Maps->Edit Property Maps... from the menu bar to open the Property Map Editor. Confirm the

206 Using VisualAge Smalltalk ObjectExtender Metadata Validation Error message window, select the Associations page, and map the associations to the foreign key relationships. Use the Property Map Editor to fix the broken mappings.

Figure 152. Property Map Editor for Department (see Note below)

Note: When a self-referencing map entry loses focus on either selecting another map or click on the OK button in the Property Map Editor, a post-edit dialog opens (Selection Required dialog in Figure 152) and asks for the side of the foreign key relationship for the map that just lost the focus. Therefore the Foreign Key - ADMRDEPT selection in the Selection Required dialog in Figure 152 actually refers to the administrativeDepartment association that was selected and lost focus just before the departments was selected. The side of the foreign key relationship for the departments association is the Primary Key - DEPTNO.

Employee and Department Bottom-Up 207 Figure 153. Property Map Editor Open for Employee

Because of the new role names with the new property maps, you must delete the old and broken property maps in the Map Browser. After you delete these old and broken property maps, all mapping errors should disappear. If not, check the mappings again and carefully read the textual summary of the error prone table map and the messages in the Metadata Validation Error window when opening the Map Property Editor.

Figure 154 shows the adjusted Rb31Department table map. Look at the textual summary of the self-referencing relationship maps in the lower half of the Map Browser window: • Rb31department, 1:1, administrativeDepartment, forward map, #member • Rb31department, 0:’many’, departments, backward map, #parent

The data store map is now reviewed and adjusted. When we save the map, we give the associated application the name Rb31EmpDeptMapApp and accept the offered storage class name of Rb31EmpDeptRb31empdeptMap.

208 Using VisualAge Smalltalk ObjectExtender Figure 154. Adjusted Rb31Department Table Map

8.3 Generate Model Code Classes To generate the model code classes from the metadata, in previous examples, in the Model Browser, select Models->Model Code Generation Options from the menu bar to define model code application (Figure 155), and then Models->Generate to actually generate the model code classes from the metadata.

Employee and Department Bottom-Up 209 Figure 155. Model Code Generation Options for Rb31EmpDept Model

You can inspect the generated classes with the VisualAge Organizer. The relevant classes are defined in the Rb31EmpDeptApp.

8.4 Generate Service Classes Once the model code classes have been generated, we generate the service classes to support the physical database access. As in the previous examples, we use the Map Browser for this. The first stage is to ensure that the generation options have been set to establish a connection to the database. In the Map Browser choose Datastore Maps->Generation Options... to open the generation options dialog. A typical dialog is shown in Figure 89 on page 112 and Figure 91 on page 113. For the services application we accept ObjectExtender’s model and map dependent default name: Rb31EmpDeptRb31empdeptServicesApp.

In the Map Browser we then choose Datastore Maps->Generate Services to generate the service classes. You can inspect the generated service classes with the VisualAge Organizer. The relevant classes are defined in the Rb31EmpDeptRb31empdeptServicesApp.

Note that the data store has the name Rb31EmpDeptRb31empdeptDataStore and can be activated by executing the following Smalltalk expression: Rb31EmpDeptRb31empdeptDataStore singleton activate

210 Using VisualAge Smalltalk ObjectExtender 8.5 Run Sample Headless with Scripts We now test our code by running headless, without a user interface. We follow the general approach used in the previous samples. In this case, as we are using an existing database, we check only that we can access data and navigate the links.

8.5.1 Accessing the Data in the Database Having activated the data store, using the ObjectExtender Status Browser, we test that we can read data from the database. You can use the scripts listed below to do this and use the Inspector to look at the retrieved objects. Execute the following script to look at the name of the employee with employee number 000240: | emp | emp := Rb31employeeHome singleton findByEmpno: '000240'. emp lastname inspect "---> 'MARINO ' is inspected"

Note that the leading zeros are required for the employee number because this attribute is a string and not a number. So ’000240’ is not the same as ’240’.

Execute the following script to look at the name of the department with department number A00: | dept | dept := Rb31departmentHome singleton findByDeptno: 'A00'. dept deptname inspect. "---> 'SPIFFY COMPUTER SERVICE DIV.' is inspected"

If you use a separate DB2 command window to inspect the actual contents of the database, you can confirm that the results retrieved by our scripts agree with the values in the database.

8.5.2 Navigating the Object Links Next we confirm that we can navigate all links in all directions. The test cases are described in detail in 7.6.2, “Navigate the Objects” on page 154. The scripts and results are summarized below.

Finding the Employees of a Department Executing the following script opens an Inspector window as shown in Figure 156 and tells us that there are three employees in the department with deptno A00:

Employee and Department Bottom-Up 211 | dept emps | dept := Rb31departmentHome singleton findByDeptno: ’A00’. emps := dept employees asOrderedCollection asSortedCollection: [ :a :b | a empno < b empno ]. emps inspect

Figure 156. Employees of Department A00

Finding an Employee’s Department Executing the following script opens an Inspector window as shown in Figure 158 and tells us that the employee with empno 000170 works in department with deptno D11: | emp | emp := Rb31employeeHome singleton findByEmpno: ’000170’. emp workDepartment inspect

Figure 157. Department D11 of Employee 000170

212 Using VisualAge Smalltalk ObjectExtender Finding the Manager of a Department Executing the following script opens the Inspector window as shown in Figure 158 and tells us, that the employee with empno 00010 is the manager of the department with deptno A00: | dept | dept := Rb31departmentHome singleton findByDeptno: ’A00’. dept manager inspect

Figure 158. Manager (Employee) 000010 of Department A00

Executing the same script for the department with deptno D01, which has no manager, delivers the expected nil result: | dept | dept := Rb31departmentHome singleton findByDeptno: ’D01’. dept manager inspect "---> nil"

Determining Whether an Employee Manages a Department First we test the employee with empno 000010 who is known to manage the department with deptno A00 (last in text pane in Figure 158 and Figure 159, ...’A00’...’000010’... and ...’A00’ ’000010’...) with the following script: | emp | emp := Rb31employeeHome singleton findByEmpno: ’000010’. emp managedDepartment inspect

Figure 159. Managed Department of Employee (Manager) 000010

Employee and Department Bottom-Up 213 Now we test for an employee who is known not to manage a department, employee with empno 000170. Not surprisingly, the debugger fires up with the error message “No object found,” as seen when we tried the same test in 7.6.2, “Navigate the Objects” on page 154. The debugger display is to all intents and purposes the same as that seen in Figure 224 on page 394.

The reasons for the problem and one solution are the same as detailed in Appendix F.4.2, “Backward: Individual Access Solution (Overriding)” on page 394. We could indeed use the approach we used there, but by now warning bells should be ringing. This is the second time that this problem has occurred. We should be looking for a generic solution rather than patching the relevant relationship class each time we find this problem. The implementation of a more generic solution is discussed in more detail in the Appendix F.4.3, “Backward: Generic Access Solution (New Inheritance)” on page 396.

8.6 Run Sample with User Interface In Chapter 5, “Employee Top-Down” on page 47, we generated a user interface to manage the employee. Here, we present a single window user interface to manage the departments. Again, the user interface we present here is simple. It contains no business rules and no error handling.

The developed user interface to manage departments—CRUD (create, read, update, and delete) departments and assign departments, employees, and managers—is shown in Figure 160. Figure 162 on page 223 shows the parts list. The connection details are shown in Table 45 on page 224 through Table 47 on page 226. Figure 161 on page 217 shows the user interface at runtime.

To simplify the user interface for this sample, we add new public interface attributes and new methods to the Rb31department and Rb31employee classes: • Single reference string attributes and methods to access and reasonably display the required fields of the associated classes (see pages 218 trough 219) • Signalling multiple reference attributes and methods to update the view lists of departments and employees (see page 220) • Customized add... and remove... methods for adding and removing departments and signalling the changes (see pages 221 through 222)

214 Using VisualAge Smalltalk ObjectExtender Figure 160. Rb31DeptView to Manage Departments in the Composition Editor

Employee and Department Bottom-Up 215 The implementation key features of the user interface in Figure 160 are: • A container to hold a list of all departments. • A container labeled employees to hold a list of departments in the new or selected department. The items attribute is connected to the departments attribute of the department. • A container labeled other employees to hold list of the other employees, that are the employees which are not in the new or selected department. • A button labeled "<<" to move selected departments from the list of the other departments into the list of employees of the new or selected department. • A button labeled ">>" to remove selected departments from the list of the new or selected department. • A container labeled employees to hold a list of employees in the new or selected department. The items attribute is connected to the employee attribute of the department. • A container labeled other employees to hold list of the other employees, that are the employees which are not in the new or selected department. • A button labeled "<<" to move selected employees from the list of available employees into the list of employees of the new or selected department. • A button labeled ">>" to remove selected employees from the list employees of the new or selected department. • A button labeled Mgr to make the selected employee the manager of the new or selected department. The button is enabled only when an employee of the new or selected department is selected. If more than one employee is selected, the first is taken.

216 Using VisualAge Smalltalk ObjectExtender Figure 161. Rb31DeptView at Runtime to Manage Departments at Runtime

Employee and Department Bottom-Up 217 For single references in the department, we add the new public interface attributes and methods deptNoAndName, managerEmpNoAndName, and admrDeptNoAndName to the Rb31department class (Table 37 and Table 38). Table 37. Reference Attributes in the Rb31department Public Interface Attribute name Get selector Attribute data type

1 admrDeptNoAndName admrDeptNoAndName String

2 deptNoAndName deptNoAndName String

3 managerEmpNoAndName managerEmpNoAndName String

Table 38. Reference Methods of Rb31department Rb31department>>#methods in category OERedbook

1 admrDeptNoAndName

"Return the value of admrDeptNoAndName."

^self administrativeDepartment deptNoAndName

2 deptNoAndName

"Return the value of deptNoAndName."

| tDeptno tDeptname |

(tDeptno := self deptno) == nil ifTrue: [tDeptno := '']. (tDeptname := self deptname) == nil ifTrue: [tDeptname := '']. ^ tDeptno , ' ' , tDeptname

3 managerEmpNoAndName

"Return the value of managerEmpNoAndName."

| mgr | ^(mgr := self manager) ~~ nil ifTrue: [mgr empNoAndName] ifFalse: ['-']

218 Using VisualAge Smalltalk ObjectExtender For single references in the employee, we add the new public interface attributes and methods empNoAndName, managerEmpnoAndName, and workDeptNoAndName to the Rb31employee class (Table 39 and Table 40). Table 39. Reference Attributes in the Rb31employee Public Interface Attribute name Get selector Attribute data type

1 deptNoAndName deptNoAndName String

2 managerEmpNoAndName managerEmpNoAndName String

3 workDeptNoAndName workDeptNoAndName String

Table 40. Reference Methods of Rb31employee Rb31employee>>#methods (for reference) in category OERedbook

1 empNoAndName

"Return the value of empNoAndName."

| stream | (stream := WriteStream on: String new) nextPutAll: self empno; nextPut: $ ; nextPutAll: self lastname trimBlanks; nextPutAll: ', '; nextPutAll: self firstnme trimBlanks. self midinit asString trimBlanks notEmpty ifTrue: [ stream nextPutAll: ' ('; nextPut: self midinit; nextPutAll: ’.)’ ]. ^stream contents

2 managerEmpNoAndName

"Return the value of managerEmpNoAndName."

| dept | ^(dept := self workDepartment) ~~ nil ifTrue: [dept managerEmpNoAndName] ifFalse: ['-']

3 workDeptNoAndName

"Return the value of workDeptNoAndName."

| dept | ^(dept := self workDepartment) ~~ nil ifTrue: [dept deptNoAndName] ifFalse: ['-']

Employee and Department Bottom-Up 219 For adding and removing departments and employees, the Rb31Department class needs additional public interface attributes and methods (Table 41 and Table 42), and user code in the add... and remove... methods (Table 43 on page 221 and Table 44 on page 222). Table 41. Signalled Attributes in the Rb31department Public Interface Attribute name Get selector / Attribute data type / Change event symbol Contents type

1 sDepartments sDepartments / LinkCollectionShell / sDepartments Rb31departments

2 sEmployees sEmployees / LinkCollectionShell / sEmployees Rb31employee

Note: s-prefix stands for signalled (for example: signaledDepartements)

Table 42. Signalled Methods in the Rb31department Class Rb31employee>>#methods (for singals) in category OERedbook

1 sDepartments

"Signalled departments."

^self departments

2 sEmployees

"Signalled employees."

^self employees

220 Using VisualAge Smalltalk ObjectExtender Table 43. Add.../Remove... Departments Methods in Rb31department Class Rb31department>>#methods (add.../remove... departments) in category OERedbook-customized

1 addDepartments: aDepartments

"Add method forwarded to the link collection"

"Begin user code {pre-Connect}" > (aDepartments isKindOf: Collection) ifTrue: [ > aDepartments do: [ :e | self addDepartments: e ]. > self signalEvent: #sDepartments > with: self departments. > ^aDepartments ]. "End user code"

self departmentsLink connectTo: aDepartments.

"Begin user code {post-Connect}" "End user code"

2 removeDepartments: aDepartments

"Remove method forwarded to the link collection"

"Begin user code {pre-Disconnect}" > (aDepartments isKindOf: Collection) ifTrue: [ > aDepartments do: [ :e | > self removeDepartments: e. > e administrativeDepartment: e ]. > self signalEvent: #sDepartments > with: self departments. > ^aDepartments ]. "End user code"

self departmentsLink disconnectFrom: aDepartments.

"Begin user code {post-Connect}" "End user code"

Employee and Department Bottom-Up 221 Table 44. Add.../Remove... Employees Methods in Rb31department Class Rb31department>>#methods (add.../remove... departments) in category OERedbook-customized

1 addEmployees: anEmployees

"Add method forwarded to the link collection"

"Begin user code {pre-Connect}" > (anEmployees isKindOf: Collection) ifTrue: [ > anEmployees do: [ :e | self addEmployees: e ]. > self signalEvent: #sEmployees > with: self employees. > ^anEmployees ]. "End user code"

self employeesLink connectTo: anEmployees.

"Begin user code {post-Connect}" "End user code"

2 removeEmployees: anEmployees

"Remove method forwarded to the link collection"

"Begin user code {pre-Disconnect}" > (anEmployees isKindOf: Collection) ifTrue: [ > anEmployees do: [ :e | > self removeEmployees: e. > e workDepartment: nil ]. > self signalEvent: #sEmployees > with: self employees. > ^anEmployees ]. "End user code"

self employeesLink disconnectFrom: anEmployees.

"Begin user code {post-Connect}" "End user code"

222 Using VisualAge Smalltalk ObjectExtender Figure 162. Rb31DeptView Parts List

Employee and Department Bottom-Up 223 Table 45. Connections of Rb31DeptView (Part 1 of 3) Source (S) Feature of S Target (T) Feature of T

1 contDepartmentsList selectedItem department self

2 copier1 copy otherDepartments self 3 (copier1,copy --> availableDs,self) normalResult otherDepartments removeAll:

4 ( (copier1,copy --> aCollection department departments availableDs,self),normalResult --> availableDs,removeAll:)

5 copier2 copy otherEmployees self 6 (copier2,copy --> availableEs,self) normalResult otherEmployees removeAll:

7 ( (copier2,copy --> aCollection department employees availableEs,self),normalResult --> availableEs,removeAll:)

8 department sDepartments departments items 9 department sEmployees employees items 10 department deptname deptnameField object 11 department deptno deptnoField object

12 department location locationField object 13 department isNotPersistent deptnoField enabled 14 department manager managerField object 15 department sEmployees copier2 origin 16 department sDepartments copier1 origin

17 (department,manager --> value department managerEmp managerField,object) NoAndName

18 (department,sDepartments --> value allDepartments self copier1,origin)

19 (department,sEmployees --> value allEmployees self copier2,origin)

20 departments selectionIsValid pbRemoveDepts enabled 21 employees selectionIsValid pbRemoveEmps enabled 22 employees selectionIsValid pbSetManager enabled

23 otherDepartments self otherDepts items 24 otherDepts selectionIsValid pbAddDepts enabled 25 otherEmployees self otherEmps items 26 otherEmps selectionIsValid pbAddEmps enabled

224 Using VisualAge Smalltalk ObjectExtender Table 46. Connections of Rb31DeptView (Part 2 of 3) Source (S) Feature of S Target (T) Feature of T

27 pbAddDepts clicked department addDepartme nts:

28 (pbAddDepts,clicked --> aDepartments otherDepts selectedItems department,addDepartments:)

29 pbAddEmps clicked department addEmployee s:

30 (pbAddEmps,clicked --> anEmployees otherEmps selectedItems department,addEmployees:)

31 pbCancel clicked topLevelTransaction rollback 32 pbMarkDelete clicked department remove 33 pbNew clicked departmentHome create

34 (pbNew,clicked --> normalResult department self departmentHome,create)

35 ( (pbNew,clicked --> normalResult department administrative departmentHome,create),normalResul Department t --> department,self)

36 ( ( (pbNew,clicked --> value department self departmentHome,create),normalResul t --> department,self),normalResult --> department,administrativeDepartment)

37 pbRemoveDepts clicked department removeDepar tments:

38 (pbRemoveDepts,clicked --> aDepartments departments selectedItems department,removeDepartments:)

39 pbRemoveEmps clicked department removeEmplo yees:

40 (pbRemoveEmps,clicked --> anEmployees employees selectedItems department,removeEmployees:)

41 pbSave clicked topLevelTransaction commit 42 pbSetManager clicked department manager

43 (pbSetManager,clicked --> value employees selectedItem department,manager)

Employee and Department Bottom-Up 225 Table 47. Connections of Rb31DeptView (Part 3 of 3) Source (S) Feature of S Target (T) Feature of T

44 topLevelTransaction self department transaction 45 topLevelTransaction self allEmployees self 46 topLevelTransaction self contDepartmentsList items 47 topLevelTransaction self allDepartments self

48 topLevelTransaction commitedOrRol sharedTransaction beginChild ledback

49 (topLevelTransaction,committedOrRoll normalResult topLevelTransaction self edback --> sharedTransaction,beginChild)

50 (topLevelTransaction,self --> value departmentHome allInstances allDepartments,self)

51 (topLevelTransaction,self --> value employeeHome allInstances allEmployees,self)

52 (topLevelTransaction,self --> value departmentHome allInstances contDepartmentsList,items)

53 Window aboutToOpenW sharedTransaction beginChild idget

54 (Window,aboutToOpenWidget --> normalResult topLevelTransaction self sharedTransaction,beginChild)

226 Using VisualAge Smalltalk ObjectExtender Chapter 9. Employee and Department - Outside-In

For the sample going along the Outside-In development path we look at the Employee and Department classes. Figure 101 on page 127 shows the class diagram. The crucial task along the path is mapping the model to the schema. This sample uses the Rb32 prefix.

9.1 Preparation Work Before we can start with the mapping task, we need a model (actually a metamodel) with the Employee and Department classes and their associations, and a schema of the Employee and Department database tables and their primary key and foreign key associations.

You define the model by the metamodel in the same way as described in 7.1, “Define Model (Metadata)” on page 127, or you reuse the model created there. For the schema you either define it manually, import it from the database tables as described in 8.1, “Import Schema from Relational Database (Metadata)” on page 196, or reuse the schema created there.

There is nothing wrong and nothing will go wrong with reusing the model, the schema, or both. The data store, which is generated together with the services from the map, and the data store’s activation at runtime ensure the proper working of the sample. If you reuse the model, you do not even have to generate or regenerate the model code classes, and, furthermore, you can run the same tests scripts and GUI applications used in Chapter 7, “Employee and Department Top-Down” on page 127 (with the proper data store activation).

9.2 Create the Map In the Map Browser choose Datastore Maps->New Datastore Map... from the menu bar. In the New Datastore Map dialog window (Figure 163) enter Name, Model, and Schema from the information listed in Table 48 on page 228.

Note: In this sample we reuse the Rb30Model from Chapter 7, “Employee and Department Top-Down” on page 127 and Rb31Schema from Chapter 8, “Employee and Department Bottom-Up” on page 195.

© Copyright IBM Corp. 1999 227 Figure 163. New Datastore Map Dialog

Table 48. Map Creation Information Item Entry Remark

Name Rb32EmpDeptRb32EmpDept Use this data store map if you created a dedicated model and schema.

Rb30EmpDeptRb31EmpDept Use this data store map if you decided to reuse the existing model and schema.

Model Rb32EmpDept Use this model if you created it for this sample.

Rb30EmpDept Use this model if you decided to reuse the existing model.

Schema Rb32EmpDept Use this schema if you created it for this sample.

Rb31EmpDept Use this schema if you decided to reuse the existing model.

Note: It is useful to compose the data store Name from the model name and the schema name, for example, Rb30EmpDeptRb31EmpDept.

9.3 Create Table Maps In the Map Browser (Figure 164) select the newly created, empty data store map and a persistent class. Then choose New Table Map-> Add Cluster Map with no inheritance... from either Table Maps on the

228 Using VisualAge Smalltalk ObjectExtender menu bar or the pop-up menu of the Table Maps list. In the Table Map With... dialog select the table, for which you want to define mappings.

Create table maps for the Rb30Department and Rb30Employee persistent classes with the Rb31Department and Rb31Employee tables.

Figure 164. Table Map Creation Dialog

9.4 Map Attributes to Columns In the Map Browser select a table map and choose Table Maps-> Edit Property Maps to open the Property Map Editor (Figure 165). The Property Map Editor opens on the Attributes page. Map each model attribute to a table column with a Simple mapping. Open the Property Map Editor on the each table map and perform the Class Attribute to Table Column mappings.

Note: An incorrect mapping, such as mapping more than one class attribute to one table column or mapping incompatible column definitions, results in a <> in the Table Maps list of the Map Browser. Open the Property Map Editor on that broken map and correct the mapping. Opening a broken map displays a detailed Metadata Validation Error message, which you can use as an aid to correct the error (Figure 166).

Employee and Department - Outside-In 229 Figure 165. Property Map Editor with Map Types and Table Columns

Figure 166. Metadata Validation Error: Column Overlap

9.5 Map Associations to Foreign Key Relationships On the Associations page in the Property Map Editor (Figure 167), perform the Class Association to Foreign Key Relationship mappings. For self-referencing associations, see Figure 152 on page 207.

Save the data store map. For the storage application name use the map name with the MapApp suffix, and for the storage class name, use the map name with the Map suffix.

230 Using VisualAge Smalltalk ObjectExtender Figure 167. Associations Property Map Editor

9.6 Generate the Services Before you generate the services check the generation options. The data source of the database connection specification is the DB2 sample database, because we reuse the schema that was imported from there in 8.1, “Import Schema from Relational Database (Metadata)” on page 196. For the services application, accept the default name.

After generating the services apply the fixes for the nil-able, non-required one-to-one departmentToManager association as described in Appendix F.4, “Non-Required, One-to-Xxx Association” on page 392.

Note: The services generation may use the map to correct the generated model code for backward relationships, because the model code generation assumes all one-to-one relationships as forwards. If the model code application is not an open edition, you are asked whether ObjectExtender should create a scratch edition. But in this sample model code changes are required by the fact, that the sample, from where the model and model code is reused, has constraints defined in the database, whereas the sample, from where the schema and database is reused, has no constraints in the database.

Employee and Department - Outside-In 231 9.7 Run the Sample Because we reuse the Rb30EmpDept model from 7.1, “Define Model (Metadata)” on page 127, we can also reuse the scripts and the drag-drop supporting GUIs from the same chapter, namely 7.6, “Run Sample Headless with Scripts” on page 146, and 7.7, “Run Sample with User Interface” on page 161 by activating the correct data store, namely Rb30EmpDeptRb31EmpDeptDataStore. Figure 168 shows an a tree view as a GUI alternative. The view is a read-only view. Table 49 on page 233 and Table 50 on page 234 list the scripts to supply the items (roots), the children, and the visual information. Table 51 on page 235 lists the connections.

Note: The workDepartment of the employee entry with empNo 000130 was set to nil by setting the workDept column value in the database to NULL.

Figure 168. Rb30EmpDeptRb31EmpDeptTreeView

232 Using VisualAge Smalltalk ObjectExtender Table 49. Rb30EmpDeptRb31EmpDeptTreeView Scripts (Part 1 of 2) Rb30EmpDeptRb31EmpDeptTreeView>>#methods (root(s).../children...)

1 rootsFrom: departments and: employees

"Return the root/ top department(s) and employees."

^( departments select: [ :e | e administrativeDepartment = e ] ) , ( employees select: [ :e | e workDepartment == nil ] )

2 childrenRequested: requestData

"Set the requestData's children."

| item children child |

(item := requestData item) class name == #Rb30Department ifTrue: [ (children := OrderedCollection new) addAll: ( item departments asOrderedCollection remove: item ifAbsent: []; yourself ). (tbManager selection and: [(child := item manager) notNil ]) ifTrue: [ children add: child ]. tbEmployees selection ifTrue: [ children addAll: (item employees asOrderedCollection remove: item manager ifAbsent: []; yourself ) ] ] ifFalse: [ ^nil].

requestData children: children

Employee and Department - Outside-In 233 Table 50. Rb30EmpDeptRb31EmpDeptTreeView Scripts (Part 2 of 2) Rb30EmpDeptRb31EmpDeptTreeView>>#visualInfoRequested: method

3 visualInfoRequested: requestData

"Set the requestData's visual information, such as icon, label, hasChildren."

| item icon label hasChildren |

icon := requestData icon. (item := requestData item)

item class name == #Rb30Department ifTrue: [ icon :=AbtIconDescriptor new moduleName: 'abticons'; id: 106; icon. label := (WriteStream on: String new) nextPutAll: item referenceString; nextPutAll: ' - ' ; nextPutAll: item managerReferenceString; contents. hasChildren := (item departments notEmpty or: [ item manager notNil or: [item employees notEmpty] ]) ] ifFalse: [

item class name == #Rb30Employee ifTrue: [ item managedDepartment == nil ifTrue: [ icon := AbtIconDescriptor new moduleName: 'abticons'; id: 543; icon ]. label := (WriteStream on: String new) nextPutAll: item referenceString; nextPutAll: ' - ' ; nextPutAll: item phoneNo; nextPutAll: ' - '; nextPutAll: item job; contents. hasChildren := false ] ifFalse: [

^nil ] ].

requestData icon: icon; label: label; hasChildren: hasChildren

234 Using VisualAge Smalltalk ObjectExtender Table 51. Connections of Rb30EmpDeptRb31EmpDeptTreeView Source (S) Feature of S Target (T) Feature of T

1 contIconTree visualInfoReq Rb30EmpDeptRb31 visualInfoReq uested EmpDeptTreeView uested:

2 contIconTree itemChildren Rb30EmpDeptRb31 childrenRequ Requested EmpDeptTreeView ested:

3 departmentHome self departmenHomeT self

4 employeeHome self employeeHomeT self

5 topLevelTransaction committedOr sharedTransaction beginChild Rolledback

6 topLevelTransaction self departmenHomeT transaction

7 topLevelTransaction self employeeHomeT transaction

8 topLevelTransaction self Rb30EmpDeptRb31 rootsFrom:an EmpDeptTreeView d:

9 (topLevelTransaction,committedOrRolled normalResult topLevelTransaction self back --> sharedTransaction,beginChild)

10 (topLevelTransaction,self --> normalResult contIconTree items Rb30EmpDeptRb31EmpDeptTreeView,r ootsFrom:and:)

11 (topLevelTransaction,self --> parameter1 departmenHomeT allInstances Rb30EmpDeptRb31EmpDeptTreeView,r ootsFrom:and:)

12 (topLevelTransaction,self --> parameter2 employeeHomeT allInstances Rb30EmpDeptRb31EmpDeptTreeView,r ootsFrom:and:)

13 Window aboutToOpen sharedTransaction beginChild Widget

14 (Window,aboutToOpenWidget --> normalResult topLevelTransaction self sharedTransaction,beginChild)

Employee and Department - Outside-In 235 236 Using VisualAge Smalltalk ObjectExtender Chapter 10. Summary and Conclusion

In Part 2, “Your First Samples” on page 45, we looked at a number of simple examples, working them forward from the model and backward from a legacy database. Each of the samples demonstrates some interesting features of ObjectExtender. It is also possible to have a middle solution, where we produce a new model and map it to an existing database.

The important features demonstrated in Chapter 5, “Employee Top-Down” on page 47, through Chapter 9, “Employee and Department - Outside-In” on page 227, are these: • You must decide on the approach to be used for building the system. Do you want to develop the database from scratch or do you want to use an existing database? ObjectExtender supports a true mix-and- match approach where it is possible to start at either end or to take the middle way. • ObjectExtender’s code generation services are very powerful but they cannot envisage all possible user cases and all possible circumstances. There is a need to be careful where there are potential circular paths in the class diagram. Some user coding will be required in all but the simplest cases. • It is important to check the results of generation and testing. We found some problems only because we checked both sets of results. • Testing headless, without a user interface, provides a simple method of testing and allows the testing of cases that the user interface cannot explore. • ObjectExtender supports multiple data store and services implementations for one and the same model. Activating the data store determines which data store and services implementations are connected to the model. The home of the model is the logical connection to the activated data store and services. • It is necessary to consider adding validation, error handling, and business rules. • Object identifiers are almost sacrosanct. Once created, they should not be changed. • When working back from an existing database design, consider carefully whether you want to use the names of the existing tables, columns, and foreign keys as logical names. Your implementation is likely to be easier to

© Copyright IBM Corp. 1999 237 maintain if you choose logical names that are more meaningful than those compressed to fit into some arbitrary database limit. • Do not be afraid to implement additional methods for the generated classes if it makes your user interface design easier. It is much easier to write a small selector for a class to deliver a composite attribute (for example, employee number and last name) than it is to try and code it visually or into a script in the user interface.

238 Using VisualAge Smalltalk ObjectExtender Part 3. Advanced Details

© Copyright IBM Corp. 1999 239 240 Using VisualAge Smalltalk ObjectExtender Chapter 11. Many-to-Many Associations

In the Library Management System, there is an association that has a many-to-many cardinality: namely, that between Title and Category (Figure 169). A title can belong to one or more categories, and a category can consist of any number of titles.

Figure 169. Library Management System: Title and Category Analysis View

11.1 Define the Model With the current ObjectExtender implementation, we construct a many-to-many relationship by joining together two one-to-many relationships. Consequently, we need an additional association class that has a one-to-many relationship to each of the classes. Figure 170 shows the resulting design model.

Figure 170. Library Management System: Title and Category Design View

© Copyright IBM Corp. 1999 241 Each instance of the newly introduced TitleToCategory association class represents a connection from exactly one Title object to exactly one Category object. This lays the foundation of the model to specify in ObjectExtender’s Model Browser.

The first step is to enter the Title and Category classes with the attribute specifications in Table 52. Table 52. Rb50Title and Rb50Category Attributes Class Name Attribute Name Attribute Type Required

Rb50Title isbnNumber String Yes (Object Id)

author String No

publisher String No

description String No

Rb50Category id String Yes (Object Id)

name String No

The next step is to create the association class TitleToCategory, without entering any attributes. As soon as we have created the class, we can specify the one-to-many relationships. The convention used here for naming these relationships is that the association class name is abbreviated to the first letters of the classes it connects. Thus, the association between TitleToCategory and Title is called tcToTitle (Figure 171).

At first glance, the multiplicity of settings may be a bit confusing. The cardinality on the side of the business class (here, Title) is always exactly one (the Required checkbox is selected but the Many checkbox is not). The cardinality on the side of the association class always conforms to that of the cardinality on the business-class end of the many-to-many relationship in the analysis model. In this case, it is one or more (both Many and Required are selected). Note that we chose to call the role of TitleToCategory in this association categoryAssociations, to reflect the fact that a Title has a collection of associations to Category, not a set of direct references to Category objects.

In the same manner, the second one-to-many association between TitleToCategory and Category is defined. It is called tcToCategory (Figure 172).

242 Using VisualAge Smalltalk ObjectExtender Figure 171. Association between Title and TitleToCategory

Figure 172. Association between TitleToCategory and Category

Many-to-Many Associations 243 Because no object ID attributes have been added to TitleToCategory yet, the last step in the definition of the model is to use the association roles for that purpose in the Class Editor (Figure 173).

Figure 173. Rb50TitleToCategory Class

11.2 Define Schema and Mapping When the model is completed, one option is to Generate schema from model in the Model Browser. Because of DB2 naming limitations (maximum length of 18 characters, no spaces), however, the generated schema would need to be worked over manually. (For details about DB2 naming limitations, see 12.1.2, “Define the Schema” on page 260.) Therefore, we elected to start from scratch with a new schema. Figure 52 shows the relevant table definitions.

The next step is to define the foreign key relationships from Rb50Title2Category to Rb50Title and Rb50Category. Figure 174 shows the result is shown for one of the relationships (Rb50TitleToCategory to Rb50Category).

Once the schema is completed, the mapping is straightforward, as is the generation of the model and service code.

244 Using VisualAge Smalltalk ObjectExtender Table 53. Rb50Title, Rb50Category, and Rb50TitleToCategory Tables Table Column Type

Rb50Title isbnNumber VARCHAR(12) NOT NULL

author VARCHAR(50)

publisher VARCHAR(50)

description VARCHAR(256)

Rb50Category id VARCHAR(12) NOT NULL

name VARCHAR(50)

Rb50Title2Category title VARCHAR(12) NOT NULL

category VARCHAR(12) NOT NULL

Figure 174. Rb50 Title2Category to Rb50Category Foreign Key Relationship

Many-to-Many Associations 245 11.3 Test Sample Once all necessary code is generated, we can test the model with a sample application. Again, we do this in two steps. First, we test headless with simple scripts, and then we test with a GUI. The complete scripts are in the Rb50TstW.wsp workspace file (see Appendix C, “Downloadables, ConfigMaps, Applications” on page 355).

11.3.1 Run Sample Headless with Scripts Suppose that we have to implement the part of the Library Management System that enables the user to enter new titles, edit and delete existing titles, and assign categories to them. We cover all these steps in the next sections, but categories must already be present.

We use a simple script here to populate the Category table with categories that are not interrelated: "Setup categories." Transaction begin. (Rb50CategoryHome singleton createForId: '1') name: 'Philosophy'. (Rb50CategoryHome singleton createForId: '2') name: 'Computer Science'. (Rb50CategoryHome singleton createForId: '3') name: 'History'. (Rb50CategoryHome singleton createForId: '4') name: 'Art'. Transaction current commit.

To populate the Title table with titles we use this script: "Setup titles." Transaction begin. (Rb50TitleHome singleton createForIsbnNumber: '123456789') description: 'My IBM'; author: 'Watson'; publisher: 'IBM'. (Rb50TitleHome singleton createForIsbnNumber: '98765431') description: 'IBM as Supplier'; author: 'Customer'; publisher: 'Unknown'. Transaction current commit.

There is nothing special about these scripts; you can extend them as you like.

11.3.2 Hiding Implementation Details One of the key ideas of object orientation is to separate interface from implementation. Without having to know what is going on behind the scenes, a user should be able to use a class by just adhering to its protocol. Similarly,

246 Using VisualAge Smalltalk ObjectExtender we do not want the users of Title or Category to worry about the TitleToCategory class.That is our job as implementers of these classes.

ObjectExtender automatically generates instance methods in Title and Category for accessing and managing the links to TitleToCategory objects. For Title, ObjectExtender generated the categoryAssociations, addCategoryAssociations, and removeCategoryAssociations methods, according to the name we gave the role of the class in the association specification in the Model Browser. But ObjectExtender cannot generate methods to directly access Categories from Title because, unfortunately, it cannot guess our intentions. So we have to add the categories, addCategory, and removeCategory methods to Title, making the TitleToCategory objects in between transparent to the users of Title. We could do the same for Category, but for the purpose of this example the extension of Title is sufficient.

Returning the title’s categories is straightforward: We simply iterate through the collection of TitleToCategory objects in Title and return the Categories from there. To make this method accessible in the VisualAge Composition Editor, too, we have to define the attribute categories, with categories as get selector and change event symbol, in the Public Interface Editor for Title. (For a more efficient implementation we would use a CategoryHome>>#findForTitle: method with a selective join SQL in the shared transaction context. Note that the current transaction must be saved for the find and resumed afterwards. For selective SQLs see Chapter 13., “Selective Queries or Custom Queries” on page 277, and for join SQLs see 12.2, “Multiple Table Inheritance Mapping” on page 269).

This is the straightforward Title>>#categories method: categories

"Answers the receiver's categories, NOT its categoryAssociations"

| categories |

^categories := self categoryAssociations collect: [ :each | each category ]

A bit more sophisticated is the method for adding a category to a title. We need to create a new TitleToCategory link object with references to both aCategory and the title. The creation of the link object and the establishing of the necessary links are done by the createForTitle:category: in TitleToCategoryHome, which is called by the addCategory: in Title. Because Title is intended to be not only the class but also a VisualAge part, we have to implant the signaling mechanism as well.

Many-to-Many Associations 247 This is the TitleToCategoryHome>>#createForTitle:category: method: createForTitle: title category: category

"Method to hide key implementation class inside home collection."

| titleToCategory |

^titleToCategory := ( self createFromKey: ( Rb50TitleToCategoryKey new title: title key; category: category key; yourself ) ) title: title; category: category; yourself.

This is the Title>>#addCategory: method: addCategory: aCategory

"Add a category to the receiver"

| categoryAssociation |

categoryAssociation := Rb50TitleToCategoryHome singleton createForTitle: self category: aCategory. self signalEvent: #categories with: self categories. ^aCategory

The method for removing a category from the title’s list is a bit more complicated. First, we must look up the TitleToCategory object that holds a reference to the category to be removed from the list. There are two ways to do that: either you ask the TitleToCategoryHome to do the lookup with findByTitle:category:, or you iterate through the categoryAssociations list yourself. The problem with using the home is that after you create an object it cannot be retrieved through the home in the same transaction, because the home goes looking for it in the database. There needs to be a commit in between. So we chose the other possibility, iterating through the categoryAssociations and looking for one that references the category to be removed. Once we find the correct TitleToCategory association object, we need to disconnect the links to it from the Title object as well as from the Category object. To that end, we use the methods generated by ObjectExtender, removeCategoryAssociations in Title and removeTitleAssociations in Category. After that, we can dispose of the

248 Using VisualAge Smalltalk ObjectExtender association object, using markRemoved. Again we have to notify observers of the change. This is the method: removeCategory: aCategory

"Removes aCategory from the receiver"

| categoryAssociation |

"This does not work work within one transaction: categoryAssociation := Rb50TitleToCategoryHome singleton findByTitle: self category: aCategory."

"But this does:" categoryAssociation := self categoryAssociations detect: [ :each | each category == aCategory]. self removeCategoryAssociations: categoryAssociation. aCategory removeTitleAssociations: categoryAssociation. categoryAssociation markRemoved. self signalEvent: #categories with: self categories.

^aCategory

The following script categorizes the Title with isbnNumber = ’123456789’. In the same transaction Category with id = 1 is added and removed. The Category with id = 2 is the final categorization.

"Categorize a title." | title category | Transaction begin. '---> get title with isbnNumber = 123456789 & category with id = 1' vapTrace. title := Rb50TitleHome singleton findByIsbnNumber: '123456789'. category := Rb50CategoryHome singleton findById: '1'. '---> add category withy id = 1' vapTrace. title addCategory: category. '---> remove category withy id = 1' vapTrace. title removeCategory: category. '---> get and add category with id = 2' vapTrace. category := Rb50CategoryHome singleton findById: '2'. title addCategory: category. '---> commit' vapTrace. Transaction current commit.

Many-to-Many Associations 249 The extract from the System Transcript shows the executed SQLs. '---> get title with isbnNumber = 123456789 & category with id = 1'

'>>>find by key' SELECT T1.author, T1.description, T1.isbnNumber, T1.publisher FROM USERID.Rb50Title T1 WHERE T1.isbnNumber = '123456789'

’>>>find by key' SELECT T1.id, T1.name FROM USERID.Rb50Category T1 WHERE T1.id = '1'

'---> add category withy id = 1'

'>>>retrieveCategoryAssociationsForRb50Title: aRb50TitleKey ' SELECT T1.category, T1.title FROM USERID.Rb50Title2Category T1 WHERE T1.title = '123456789'

'>>>retrieveTitleAssociationsForRb50Category: aRb50CategoryKey ' SELECT T1.category, T1.title FROM USERID.Rb50Title2Category T1 WHERE T1.category = '1'

'---> remove category withy id = 1'

'---> get and add category with id = 2'

'>>>find by key' SELECT T1.id, T1.name FROM USERID.Rb50Category T1 WHERE T1.id = '2'

'>>>retrieveTitleAssociationsForRb50Category: aRb50CategoryKey ' SELECT T1.category, T1.title FROM USERID.Rb50Title2Category T1 WHERE T1.category = '2'

'---> commit' '>>>insert' INSERT INTO USERID.Rb50Title2Category ( title, category ) VALUES ( '123456789', '2' )

Noteworthy is the retrieval of the Title2Category associations for both Category and Title in order to perform the add the new Title2Category link object for the Category with id = 1.

250 Using VisualAge Smalltalk ObjectExtender 11.3.3 Run Sample with User Interface In our original scenario, we wanted an application with which the user can enter new titles, edit and delete existing titles, as well as assign categories to titles. We have now chosen to split the application into two parts: • The first, Rb50TitleView, is for managing the titles in general, such as displaying a list, or adding and deleting titles. • When a new title is added, or an existing title is selected for editing, the second part, Rb50TitleDetailsView, is opened. The assignment of categories can be performed there, and other title details can be entered.

Title List View Figure 175 shows the Rb50TitleView. Table 54 on page 253 lists the connections between the parts in Rb50TitleView.

Figure 175. Rb50TitleView

The key features of the Rb50TitleView part are: • The transaction for the whole application is held in Rb50TitleView. You can see the familiar interplay of the shared transaction with the top-level transaction being reused here.

Many-to-Many Associations 251 • The transacted title variable holds the currently selected title. The contents is filled as the result of a selection in the list box or by a new title created in the titleHome (Connection 9 or Connections 6 and 8). • When a title is to be edited, a new Rb50TitleDetailsView part is created with the help of an Rb50TitleDetailsView factory (Connection 4) and opened (Connection 5). The transacted title is passed as a creation parameter to the factory to set the title variable promoted from the Rb50TitleDetailsView part. The Edit button functions as a funnel for all the events that can trigger the creation of an Rb50TitleDetailsView part, whether the defaultactionRequested of the container (Connection 10) or the clicked events of the New button (Connection 7), or the Edit button itself (Connection 6). • Any changes made to the title in the Rb50TitleDetailsView are committed or rolled back on the promoted clicked events of the OK and Cancel buttons there.

252 Using VisualAge Smalltalk ObjectExtender Table 54. Connections of Rb50TitleView

Source (S) Feature of S Target (T) Feature of T

1 deleteButton clicked transacted title remove 2 deleteButton clicked topLevelTransaction commit

3 (deleteButton,clicked --> errorResult Message Prompter promptFor: topLevelTransaction,commit)

4 editButton clicked TitleDetailsView new 5 editButton clicked titleDetailsView openWidget 6 newButton clicked titleHome create 7 newButton clicked editButton click

8 (newButton,clicked --> normalResult transacted title self titleHome,create)

9 titleContainer selectedItem transacted title self 10 titleContainer defaultActionRequested editButton click 11 titleContainer selectionIsValid editButton enabled 12 titleContainer selectionIsValid deleteButton enabled

13 TitleDetailsView instance titleDetailsView self 14 titleDetailsView cancelClicked topLevelTransaction rollback 15 titleDetailsView okClicked topLevelTransaction commit

16 (titleDetailsView,cancelClicked errorResult Message Prompter promptFor: --> topLevelTransaction,rollback)

17 (titleDetailsView,okClicked --> errorResult Message Prompter promptFor: topLevelTransaction,commit)

18 titleHome allInstances titleContainer items

19 topLevelTransaction committedOrRolledback sharedTransaction beginChild 20 topLevelTransaction committedOrRolledback titleContainer items

21 (topLevelTransaction,committed normalResult topLevelTransaction self OrRolledback --> sharedTransaction,beginChild)

22 (topLevelTransaction,committed value titleHome allInstances OrRolledback --> titleContainer,items)

23 transacted title self TitleDetailsView titleVariable 24 transacted title transaction topLevelTransaction self

25 Window openedWidget sharedTransaction beginChild

26 (Window,openedWidget --> normalResult topLevelTransaction self sharedTransaction,beginChild)

Many-to-Many Associations 253 Title Details View Figure 176 shows the RB50TitleDetailsView. Table 55 on page 255 shows the connections between the parts in RB50TitleDetailsView.

Figure 176. Rb50TitleDetailsView in Composition Editor

The key features of the Rb50TitleDetailsView part shown in Figure 176 are: • The clicked events of the OK and Cancel buttons and the title variable are promoted. • Besides the entry fields for the title details, two list boxes are provided on this part. One is used to display the title’s categories, using the method we added before (Connection 14). The other shows all remaining categories. Its corresponding OrderedCollection (Connection 15) is initialized with allInstances of the categoryHome (Connections 18 and 19) reduced by the title’s categories (Connections 20 and 21). • Two buttons, addCategoryButton (<<) and removeCategoryButton (>>), perform the respective actions on the title (Connections 8 and 10) and make sure the remainingCategories are adjusted (Connections 16 and 17).

The attentive reader may ask, "What happens when things go wrong?" On the one hand we deliberately left out error handling to concentrate on the essentials here. On the other hand, Chapter 15, “Concurrent Transactions”

254 Using VisualAge Smalltalk ObjectExtender on page 325, addresses such questions as: "What happens if we open two detail views on the same title in different transactions?" Table 55. Connections of Rb50TitleDetailsView Source (S) Feature of S Target (T) Feature of T

1 title author authorField object 2 title isbnNumber isbnNumberField object 3 title publisher publisherField object 4 title description descriptionField object 5 title isNotPersistent isbnNumberField enabled

6 okButton clicked Window closeWidget

7 cancelButton clicked Window closeWidget

8 addCategoryButton clicked title addCategory:

9 (addCategoryButton,clicked --> aCategory remainingCategories selectedItem title,addCategory:) List

10 removeCategoryButton clicked title removeCategory:

11 (removeCategoryButton,clicked aCategory titleCategoriesList selectedItem --> title,removeCategory:)

12 titleCategoriesList selectionIsValid removeCategoryButt enabled on

13 remainingCategoriesList selectionIsValid addCategoryButton enabled

14 title categories titleCategoriesList items

15 remainingCategoriesList items remainingCategories self

16 (removeCategoryButton,clicked normalResult remainingCategories add: --> title,removeCategory:)

17 (addCategoryButton,clicked --> normalResult remainingCategories remove: title,addCategory:)

18 Window openedWidget remainingCategories self

19 (Window,openedWidget --> value categoryHome allInstances remainingCategories,self)

20 (Window,openedWidget --> normalResult remainingCategories removeAll: remainingCategories,self)

21 ( (Window,openedWidget --> aCollection title categories remainingCategories,self),normal Result --> remainingCategories,removeAll:)

Many-to-Many Associations 255 256 Using VisualAge Smalltalk ObjectExtender Chapter 12. Inheritance

In the examples so far, we have looked only at more or less straightforward associations between classes—that is, relationships that would also have appeared in traditional entity-relationship modeling and are therefore quite natural to map from the object-oriented world to relational databases. One of the object-oriented concepts that cause the so-called impedance gap between the two worlds is inheritance. In this chapter, we cover the ways in which ObjectExtender helps us bridge the gap.

To do so, we concentrate on that part of the Library Management System model that consists only of the book and (especially) its status classes, as shown in Figure 177. The status of a book is always represented by one of the different subclasses of Status, so what we have here is a classic example of the . The inheritance hierarchy may seem a bit artificial in this context, but that is because we ignore the behavioral aspects that led to this design in the first place.

Figure 177. Library Management System: Book and Its Status

There are basically two ways to match an inheritance tree to a relational database: Either you combine all attributes of the class hierarchy in one table

© Copyright IBM Corp. 1999 257 or you have a table for each class and establish the necessary foreign key relationships between the tables.

12.1 Single Table Inheritance Mapping Mapping the Status class and its subclasses into one table means that we put all attributes—whether from the Status class or from any of its subclasses— into one table. In addition, a discriminator column is needed (type in table STATUS in Figure 178) that holds an indicator for the class the row belongs to, in order to tell ObjectExtender what kind of object to build when a row is read from the database.

Table BOOK

serialNumber 1 2 3

Table STATUS

id book customer type startDate returnDate reservationDate endOfReservation 11 - Available 2 2 - Available 3 3 999 Borrowed 07-07-98 08-07-98

unused space in table

Figure 178. Example of Three Books and Their Status Mapped into a Single Table

As Figure 178 shows, there is a drawback to this approach: Depending on the structure of the data and the number of the rows, table space may be wasted. Support of the null value and the view of today’s relational database systems

258 Using VisualAge Smalltalk ObjectExtender ease the problem somewhat, but mapping a class hierarchy to a single table is still appropriate primarily when: • The subclasses differ more in their behavior than in their attributes. If the persistent objects have basically the same data, little space is wasted in the database. • Database access speed is critical. Having everything in one table results in fewer SQL queries and therefore in better performance.

12.1.1 Define the Model The definition of the Book and Status model follows the same steps you have seen in previous sections. There are a few things to note, though: • We can use serialNumber as a natural object ID for Book, but we must introduce an artificial ID for Status. • Although an artificial ID is introduced in the Status class, it is not necessary to do so again in its subclasses. Because subclasses inherit all their parents’ attributes, the required object ID exists in all Status classes. And, there is no point in having multiple primary keys, because all attributes are mapped to one table in the database. • Do not forget to add the one-to-one association between Book and Status with the settings shown in Figure 179.

Figure 179. Settings for the One-to-One Association between Book and Status

Inheritance 259 • The important point at which this sample deviates from what you have done before is the way in which you define the subclasses of Status. This is not done by adding associations between the subclass and the superclass but by specifying the correct superclass in the Class Editor. As shown in Figure 180, you have to change the Superclass in the drop-down list box from to the correct superclass—in this case Status.

Figure 180. Definition of Borrowed As Subclass of Status

12.1.2 Define the Schema After you have defined the model, use the Generate schema from model feature in the Model Browser. This automatically creates a schema where the inheritance tree is mapped into one table. The resulting schema looks like the schema in Figure 181.

260 Using VisualAge Smalltalk ObjectExtender Figure 181. Schema for the Book-Status Example after Automatic Generation

The Columns list of the Status table shows that ObjectExtender has combined all attributes from the Status class and its subclasses into one table. Observe that ObjectExtender has created an additional column to be able to distinguish the different types (Rb61Library). You can rename this column or change its type to what you think fits best; we chose type for the name and kept the default type VARCHAR(12). Note also that the column names automatically generated for the attributes of the subclasses. If you do not like the generated names, change them.

When you change the name of a foreign key relationship, a copy of the relationship with the new name is created. To resolve the problems with the foreign key relationship, follow these steps: 1. Add a new column called serialNumber of type INTEGER (see below for the justification) with no nulls allowed. 2. Change the type of the serialNumber column in the Book table to INTEGER. This is necessary because ObjectExtender currently prevents the types of the two columns in the foreign key relationship from matching if they are SMALLINT, even though that match is required.

Inheritance 261 3. Change the name of the foreign key relationship from Status to Book to something like bookHasStatus. Also change the foreign key to bookSerialNumber. When you close the dialog with OK, you see that you have a new copy of the foreign key relationship. 4. Delete the old Status to Book relationship. 5. Delete the old Rb61Book_serialNumber column.

Before you finally export your schema to the database, be sure you have adjusted the table qualifiers to your needs (the default is VAP).

12.1.3 Define the Mapping The first step of mapping the Book class to the Book table is the same as we have seen in previous sections. Select Book from the list of Persistent Classes in the Map Browser, add a Cluster Map with no inheritance, and edit the property map (map serialNumber and the association to Status).

A new procedure is necessary for defining the mappings for the Status class and its subclasses. Select Status from the Persistent Classes list, add a Single Table Inheritance Map, and fill in the fields in the dialog (Figure 182).

Figure 182. Table Map for Class Status

As Status is the root class of the inheritance tree, be sure you have selected the Root of the model hierarchy check box. This also allows you to choose which column is to be taken as the discriminator.

262 Using VisualAge Smalltalk ObjectExtender Note on discriminators A discriminator is used to distinguish between the types of database entries that might come from different classes. To that end, we already defined a discriminator column in our schema and now enter a discriminator value for every class of an inheritance tree that is mapped to the single table. This value is then automatically written when an object is stored in the database. The type of the discriminator value could be anything from the class name to some numerical class code. The default type is String (VARCHAR(12) in the database), and we did not change it in the schema. So when you add a single inheritance map, be sure that the discriminator value is a string constant in Smalltalk style—in other words, a string in single quotes.

After you have mapped the attributes and associations of Status, you also need to add a single table inheritance map for all subclasses of Status. Choose Status as the table to map to, then make sure that the Root of the model hierarchy check box is deselected, and decide on an appropriate value for the discriminator. As can be seen in Figure 183, we stick to the class name (as string).

Figure 183. Table Map for Class Available

When you edit the property maps for the subclasses of Status, do not map the attributes and associations that have already been mapped in Status again. Add only the mappings for additional attributes, such as startDate in Borrowed. Remember to map returnDate of both Borrowed and Overdue to the same column (returnDate).

Inheritance 263 12.1.4 Test Single Table Inheritance Mapping Sample Once the model, schema, and mappings are complete and saved, and the model and services code have been generated, it is time to test our sample. We test headless here, with little scripts. The scripts are in the Rb6XTstW.wsp workspace file (see Appendix C, “Downloadables, ConfigMaps, Applications” on page 355).

Set up Sample Objects The first test script creates the Books and Status’ as shown in Figure 178 on page 258: "Setup books with status."

| newBook newStatus today | today := Date newDay: 20 month: #December year: 1999.

"Begin a new TopLevelTransaction." Transaction begin.

"Create Available Book with id = 1." newBook := Rb61BookHome singleton createForSerialNumber: 1. (newStatus := Rb61AvailableHome singleton createForId: (newBook serialNumber) ) book: newBook.

"Create Available Book with id = 2." newBook := Rb61BookHome singleton createForSerialNumber: 2. (newStatus := Rb61AvailableHome singleton createForId: (newBook serialNumber) ) book: newBook.

"Create Borrowed Book with id = 3." newBook := Rb61BookHome singleton createForSerialNumber: 3. (newStatus := Rb61BorrowedHome singleton createForId: (newBook serialNumber) ) startDate: today; returnDate: (today addDays: 30); book: newBook; customer: 999.

"Commit the TopLevelTransaction." Transaction current commit.

If you look at the output on the System Transcript you see the SQL statements that ObjectExtender has built and executed. As a sample we

264 Using VisualAge Smalltalk ObjectExtender recall the SQL statements executed for the Borrowed Book, that is the Book with the id = 3: INSERT INTO USERID.Rb61Book ( serialNumber, condition ) VALUES ( 3, NULL )

INSERT INTO USERID.Rb61Status ( id, type, bookSerialNumber, customer, returnDate, startDate ) VALUES ( 3, 'Borrowed', 3, 999, {d '2000-01-19' }, {d '1999-12-20' } )

The noteworthy points here are: • The discriminator value ’Borrowed’ is added automatically because a Borrowed object was created. • The dates operate Y2K compliant.

Verify Sample Object Setup The next test script shows that both kind of homes, this are the (leaf) subclass and (root) superclass homes, deliver instances on an allInstances message: • The subclass homes delivers its unique instances. • The superclass home delivers the instances of its class, if it is the home of a concrete class, and all instances of all its subclass home classes.

The Debugger in Figure 184 shows the scripts in the list pane and the results in the inspector area. Close the Debugger by clicking on the Resume button. The executed SQL statements are: SELECT T1.type, T1.bookSerialNumber, T1.customer, T1.id FROM USERID.Rb61Status T1 WHERE T1.type IN ( 'Available')

SELECT T1.type, T1.bookSerialNumber, T1.customer, T1.id, T1.returnDate, T1.startDate FROM USERID.Rb61Status T1 WHERE T1.type IN ( 'Borrowed')

SELECT T1.type, T1.bookSerialNumber, T1.customer, T1.id, T1.returnDate, T1.startDate, T1.returnDate, T1.endOfReservation, T1.reservationDate FROM USERID.Rb61Status T1 WHERE T1.type IN ( 'Overdue', 'Available', 'Status', 'Borrowed', 'Reserved')

Note the WHERE clause setup of the SQL statements. The discriminator values are used in the IN part to select the matching rows. For the

Inheritance 265 alInstances of the superclass home the IN clause includes all available discriminator values. This inspires for combined selective queries, such as allOverdueAndBorrowed instances (and books, see 13.2, “Solution” on page 278.)

Figure 184. allInstances of the Superclass and Subclass Homes

Perform End-of-Day Processing The last test script is a bit more sophisticated. Imagine that the Library Management System must check at the end of every day to see which of the borrowed books have become overdue. One possibility is to go through all Borrowed objects and see whether the supposed returnDate (an attribute of Borrowed) has passed. If it has, a new Overdue object is created, and the old Borrowed object is sent the message markRemoved. This action does not actually delete the object but rather marks it to be deleted in the current transaction. It is then deleted when the transaction is committed. The procedure is this: "End-of-Day Processing"

"For this example we assume that the end-of-day processing is run at the day after the return date of the book that was created before, so that we can demonstrate the status change. Be aware that the dates span the Y2K border... and it works! VisualAge Smalltalk is Y2K compliant... and was it since ever!"

266 Using VisualAge Smalltalk ObjectExtender | today | today := Date newDay: 20 month: #January year: 2000. Transaction begin.

"Iterate through all Borrowed objects and look for passed return dates." Rb61BorrowedHome singleton allInstances do: [:each | each returnDate < today ifTrue: [

"Create a new Overdue object with the attribute values of the old Borrowed object." (Rb61OverdueHome singleton createForId: each id) returnDate: each returnDate; customer: each customer; book: each book.

"Delete the old Borrowed object." each markRemoved ] ].

Transaction current commit.

The System Transcript output again shows the executed SQL statements, and it is instructive to take a closer look at them. The call of allInstances in the BorrowedHome singleton causes an SQL query that selects all rows with the type ’Borrowed’ from the Status table. (In an actual library, this might get very expensive, so we look at how to do it more intelligently in Chapter 13, “Selective Queries or Custom Queries” on page 277.) The next query, selected from the Book table, is caused by the navigation from Borrowed to Book in the statement each book.

There is then a delete for the old Borrowed object and an insert into the Status table for the new Overdue object. At this point, we sometimes experienced an inverse order of the SQL statements. ObjectExtender tried to insert before the delete, causing a duplicate key error. This occurred because the Borrowed Object (with id = 1) was still around when the new Overdue object (also with id = 1) was inserted. To work around this, we could set a different ID in the new Overdue object, for example, a time stamp from the database (To make the test script work we used the Time millisecondClockValue). Another workaround and probably a general recommendation for single table inheritance mapping, is to have unique primary keys for every subclass. This is an easy solution, being a combination of id and type. (Reversing the createFor: and markRemoved order and temporarily storing the further required values from Borrowed does not help, because ObjectExtender is reordering back again the SQL

Inheritance 267 statements in the resources sort on the transaction commit.) But considering the database performance by comparing the insert/delete and the update on the given single table (with the already paid space waste) questions anyway the state pattern design of the Status in Library System Design. With a single Status object, which is only changing its state, and with no Status history, the Book id foreign key as the primary key becomes the most simple solution (and shows the very tight coupling between the Book and its Status).

Tip: If your script crashes within a transaction in cases such as above, evaluate the Smalltalk expression AbtDbmSystem startUp to reset a locked database connection.

Note: The Book table was not updated with the script above. However, the situation changes considerably if we make a small change to our end-of-day script: As the one-to-one association between Book and Status is navigable from both sides, we can build the link from the other side, from Book, not from Status as we did in the previous script. Use the following script: | today newStatus | today := Date newDay: 20 month: #January year: 2000.

Transaction begin. Rb61BorrowedHome singleton allInstances do: [:each | each returnDate < today ifTrue: [ newStatus := ( Rb61OverdueHome singleton createForId: Time millisecondClockValue ) returnDate: each returnDate; customer: each customer; "We don’t set book in status anymore..." yourself. "...but set the new status in book." each book status: newStatus. each markRemoved ] ]. Transaction current commit.

If we do it this way, then ObjectExtender marks the book as dirty, because we changed one of its instance variables. When the transaction is committed, the change automatically causes an update to that object’s representation in the database, even though from the database point of view nothing has changed for the book table. To get the best performance, always consider the way you navigate from object to object and from which side you set up links.

268 Using VisualAge Smalltalk ObjectExtender 12.2 Multiple Table Inheritance Mapping Mapping an inheritance hierarchy to multiple tables means putting all the common attributes of the superclass into one table but putting all of the additional attributes of subclasses into separate tables.

The tables for the subclasses then need to be linked to the superclass table with foreign key relationships. Figure 185 shows the result for the example with the three books from Figure 178 on page 258.

Table BOOK Table STATUS

serialNumber id book customer type 1 11 - Available 2 2 2 - Available 3 3 3 999 Borrowed

Table AVAILABLE Table BORROWED

id id startDate returnDate 1 3 07-07-98 08-07-98 2

Figure 185. Example of Three Books and Their Status Mapped to Multiple Tables

On the one hand, this results in no unused space in the tables, unlike the case with single table mapping. On the other hand, there is an overhead for combining the primary and foreign keys in the subclass tables. Furthermore, there is a penalty in terms of additional SQL statements: Reading an object from, or writing it to, the database now results in two statements. As a result, this approach may be appropriate when subclasses differ considerably

Inheritance 269 in their data. Mapping very different subclasses to a single table would result in a lot of unused space.

Database access speed considerations are not paramount. It is a general law in computing that you pay for less memory consumption (in this case, for the database) with reduced performance. There is no silver bullet; you must find the trade-off that best meets your needs.

12.2.1 Define the Model To demonstrate multiple table mapping and to contrast it with the single table mapping, we stick to the Book and Status example from the Library Management System.

Fortunately, we can use exactly the same model as before. This shows one benefit of the ObjectExtender practice of separating modeling considerations from implementation issues.

12.2.2 Define the Schema We need to make some changes to the previous schema. Only the Book table remains exactly the same. For the definition of the Status table and the four subclass tables, refer to Table 56 and Table 57, respectively.

Note: In the subclass tables, ID is used as both the primary and the foreign key. Table 56. Tables, Columns, and Attributes for Status Superclass Table Column Name Column Type Allows Nulls

Status id INTEGER No (Primary Key)

bookSerialNumber INTEGER No (Foreign Key)

customer INTEGER Yes

type VARCHAR(12) No

270 Using VisualAge Smalltalk ObjectExtender Table 57. Tables, Columns, and Attributes for the Subclasses of Status Table Column Name Column Type Allows Nulls

Available id INTEGER No (Pr. + F. Key)

Borrowed id INTEGER No (Pr. + F. Key)

startDate DATE Yes

returnDate DATE Yes

Overdue id INTEGER No (Pr. + F. Key)

returnDate DATE Yes

Reserved id INTEGER No (Pr. + F. Key)

reservationDate DATE Yes

endOfReservation DATE Yes

Define all of the foreign key relationships, from Status to Book and from all the subclass tables to Status. The Available to Status relationship in Figure 186 on page 183 illustrates one example of these foreign key relationships.

Figure 186. Definition of the Foreign Key Relationship between Available and Status

Inheritance 271 12.2.3 Define the Mapping The really different (and therefore more interesting) part is the definition of the mapping. For Book it is the same as before: a simple cluster map with noinheritance. For Status, however, it is quite different. Add Root/Leaf Inheritance Table Map brings up the dialog in Figure 187.

Figure 187. Definition of the Status Table Map (Root Inheritance Table Map)

Note: It is still necessary to have a discriminator column, but only in the root of the model hierarchy. (Be sure you have the respective check box selected for Status.)

The definition of the maps for the subclasses is also done with Add Root/Leaf Inheritance Table Map. But here the check box Root of the model hierarchy must be deselected. This in turn enables the Foreign key relationship drop-down list box, from which the correct relationship must be chosen. It is only here that the superclass and subclass association is established; it cannot be done on the Associations page in the Property Map notebook (see Figure 188).

Note: As with single inheritance table mapping, do not map attributes or associations of superclasses in the subclasses again.

272 Using VisualAge Smalltalk ObjectExtender Figure 188. Definition of the Available Table Map (Leaf Inheritance Table Map)

12.2.4 Test Multiple Table Inheritance Mapping Sample Once the model, schema, and mappings are complete and saved, and the model and services code have been generated, we are almost ready to test our sample.

Beside the insert-before-delete problem, which we faced already with single table inheritance mapping in 12.1.4, “Test Single Table Inheritance Mapping Sample” on page 264, we may exploit other pitfalls and implement some fixes as described in Appendix F.5, “SQL Execution Order in Multiple Table Inheritance Mapping” on page 398.

We test headless here, with little scripts.The scripts looks the same as in 12.1.4, “Test Single Table Inheritance Mapping Sample” on page 264—but have the class prefix RB62— and are available in Rb6XTstW.wsp workspace file (see Appendix C, “Downloadables, ConfigMaps, Applications” on page 355).

Set up Sample Objects The first test script creates the Books and Status’ as shown in Figure 178 on page 258.

If you look at the output on the System Transcript you see the SQL statements that ObjectExtender has built and executed. As a sample we

Inheritance 273 recall the SQL statements executed for the Borrowed Book, that is the Book with the id = 3:

INSERT INTO USERID.Rb62Book ( serialNumber, condition ) VALUES ( 1, NULL )

INSERT INTO USERID.Rb62Status ( id, type, bookSerialNumber, customer ) VALUES ( 1, 'Borrowed', 1, 999 )

INSERT INTO USERID.Rb62Borrowed ( id, returnDate, startDate ) VALUES ( 1, {d '2000-01-19' }, {d '1998-12-20' } )

The noteworthy point here is that you can see that two SQL statements are executed to store the Borrowed object: • Insert into the Rb62Status root table (discriminator set automatically) • Insert into the Rb62Borrowed leaf table

Verify the Sample Object Setup The next test script verifies the sample object setup.

Execute an allInstances on all (leaf) subclass homes and on the (root) superclass home, inspect the results, and take a look at the executed SQL statements. Setting ObjectExtender’s trace level to Basic Trace in the ObjectExtender Tools menu of the displays enough for that. Setting the the trace level to Extended Trace displays also the casted and set to null columns in the union SQL statement (see SQL statement on page 275).

Executing an allInstances on a (leaf) subclass home, for exmaple, Rb62OverdueHome singleton allInstances, shows that ObjectExtender executes a join SQL to read multiple table inheritance mapped leaf objects:

SELECT T1.type, T1.bookSerialNumber, T1.customer, T1.id, T2.returnDate FROM USERID.Rb62Status T1, USERID.Rb62Overdue T2 WHERE (T1.id = T2.id) AND (T1.type IN ( 'Overdue'))

274 Using VisualAge Smalltalk ObjectExtender Executing an allInstances on the (root) superclass home, for example, Rb62StatusHome singleton allInstances, shows that ObjectExtender executes a union and join SQL statement:

SELECT T1.type, T1.bookSerialNumber, T1.customer, T1.id, T2.returnDate, T2.startDate, CAST(NULL AS DATE), CAST(NULL AS DATE), CAST(NULL AS DATE) FROM USERID.Rb62Status T1, USERID.Rb62Borrowed T2 WHERE (T1.id = T2.id) AND (T1.type IN ( 'Borrowed')) UNION ALL (SELECT T2.type, T2.bookSerialNumber, T2.customer, T2.id, CAST(NULL AS DATE), CAST(NULL AS DATE), CAST(NULL AS DATE), T1.endOfReservation, T1.reservationDate FROM USERID.Rb62Reserved T1, USERID.Rb62Status T2 WHERE (T2.id = T1.id) AND (T2.type IN ( 'Reserved'))) UNION ALL (SELECT CAST(NULL AS VARCHAR (12)), CAST(NULL AS INTEGER), CAST(NULL AS INTEGER), CAST(NULL AS INTEGER), CAST(NULL AS DATE), CAST(NULL AS DATE), CAST(NULL AS DATE), CAST(NULL AS DATE), CAST(NULL AS DATE) FROM USERID.Rb62Status T1 WHERE T1.type IN ( 'Status')) UNION ALL (SELECT T1.type, T1.bookSerialNumber, T1.customer, T1.id, CAST(NULL AS DATE), CAST(NULL AS DATE), CAST(NULL AS DATE), CAST(NULL AS DATE), CAST(NULL AS DATE) FROM USERID.Rb62Status T1 WHERE T1.type IN ( 'Available')) UNION ALL (SELECT T1.type, T1.bookSerialNumber, T1.customer, T1.id, CAST(NULL AS DATE), CAST(NULL AS DATE), T2.returnDate, CAST(NULL AS DATE), CAST(NULL AS DATE) FROM USERID.Rb62Status T1, USERID.Rb62Overdue T2 WHERE (T1.id = T2.id) AND (T1.type IN ( 'Overdue')))

Noteworthy points here are the third and second last SELECTs. • The third last SELECT would deliver (root) superclass instances, if they would be concrete classes • The second last SELECT is not a join, because the (leaf) subclass object have no unique information. They share the primary key with the primary key of its corresponding (root) superclass objects.

Inheritance 275 Perform End-of-Day Processing In the last test script—the end-of-day processing—four SQL statements are executed for the Status change and one for the Book per status change: • Insert into the Rb62Status root table (discriminator set automatically) • Insert into the Rb62Overdue leaf table • Delete from the Rb62Borrowed leaf table • Delete from the Rb62Status root table • Update of the Rb62Book table

The statement for the Book is executed because the Overdue Status is assigned to the Book and not vice versa (see Note on page 268). The details of the executed SQL statements are: INSERT INTO USERID.Rb62Status ( id, type, bookSerialNumber, customer ) VALUES ( 39027748, 'Overdue', 1, 999 )

INSERT INTO USERID.Rb62Overdue ( id, returnDate ) VALUES ( 39027748, {d '2000-01-19' } )

DELETE FROM USERID.Rb62Borrowed WHERE id = 1

DELETE FROM USERID.Rb62Status WHERE id = 1

UPDATE USERID.Rb62Book SET condition = NULL WHERE serialNumber = 1

Note the reverse order of the insert and delete SQL statements concerning the inheritance, the class hierarchy, and the database foreign key constraints: • For the inserts: 1st - Status; 2nd and last - Overdue • For the deletes: 1st - Borrowed; 2nd and last - Status

276 Using VisualAge Smalltalk ObjectExtender Chapter 13. Selective Queries or Custom Queries

As we have already seen in 12.1, “Single Table Inheritance Mapping” on page 258, there are often cases where the queries that ObjectExtender generates in the Home Collection classes are not sufficient. The queries for all instances or for a single object by key may be too broad and therefore potentially inefficient, or their scope may be too narrow. Sometimes, there is a need for queries that return a subset of all instances, determined by the values of their attributes.

Note: The terms selective queries and custom queries are synonymous.

13.1 Problem Remember the Book and Status example where we wanted to find out all Borrowed objects that are already overdue in 12.1.4, “Test Single Table Inheritance Mapping Sample” on page 264. We used a script like:

"End-of-Day Processing"

| today | today := Date newDay: 20 month: #January year: 2000. Transaction begin.

"Iterate through all Borrowed objects and look for passed return dates." Rb70BorrowedHome singleton allInstances do: [:each | each returnDate < today ifTrue: [

"Create a new Overdue object with the attribute values of the old Borrowed object." (Rb70OverdueHome singleton createForId: each id) returnDate: each returnDate; customer: each customer; book: each book.

"Delete the old Borrowed object." each markRemoved ] ].

Transaction current commit.

© Copyright IBM Corp. 1999 277 When this script is executed, the following SQL command is sent to the database, the result set is parsed, and for each object read a lot of action goes on behind the scenes:

SELECT T1.type, T1.bookSerialNumber, T1.customer, T1.id, T1.returnDate, T1.startDate FROM USERID.Rb70Status T1 WHERE T1.type IN ('Borrowed')

With large tables or large result sets, the approach above easily gets costly.

Note that there is already a WHERE clause because we reuse the schema where all Status subclasses were mapped to a single table. The clause is needed to select only objects of the class Borrowed.

13.2 Solution Much better than querying all entries and then parsing them would be a query that returns the essential result set in the first place. Note that there is already a WHERE clause because we reuse the schema where all Status subclasses were mapped to a single table. The WHERE clause is required to select only objects of the class Borrowed. Therefore, we used the following SELECT statement augmented by an extended WHERE clause:

SELECT T1.type, T1.bookSerialNumber, T1.customer, T1.id, T1.returnDate, T1.startDate FROM USERID.Rb70Status T1 WHERE (T1.type IN ( 'Borrowed')) AND (T1.returnDate < {d '2000-01-20' })

To achieve this with ObjectExtender, you must: 1. Add a new SQL query string to the appropriate QueryPool class. 2. Add a new method to the Home Collection class. 3. Add a new service to the Service Object class.

In Smalltalk, it is good procedure to add the new methods as extensions to the classes, packaged together in one separate application. We put all extensions into an application named Rb70LibraryXSelSQLApp.

278 Using VisualAge Smalltalk ObjectExtender 13.3 Add a New SQL Query String to the QueryPool When you have defined your mappings for every class in your model and generated the services, a QueryPool service class (among others) is generated. To this query pool you must add a method that returns an SQL query string, just as we added the allInstancesOverdueAtDateSqlString method to Rb70LibraryRb70BorrowedQueryPool: Enter this: allInstancesOverdueAtDateSqlString

"Return the SQL string for the query called allInstancesOverdueAtDate."

^'SELECT T1.type, T1.bookSerialNumber, T1.customer, T1.id, T1.returnDate, T1.startDate FROM USERID.Rb70Status T1 WHERE (T1.type IN ( ''Borrowed'')) AND (T1.returnDate < :V1)'

Note how we used the host-variable syntax (a colon and a variable name) to put a placeholder in the string. This variable is replaced at run time with the date by which we want to select.

13.4 Add a New Method to the Home Collection Class The home is the entry point for queries for objects of a certain class, as we have seen with the allInstances and findByPrimaryKey: queries so far. Thus, it is also the natural place to put any additional query methods. The method allInstancesOverdueAtDue: to be added in Rb70BorrowedHome does not do much, except delegating the real work to the service object and collecting the results. The script is: allInstancesOverdueAtDate: aDate

"Answers all instances of the reciever which are/will be overdue at a certain date."

| serviceObject |

serviceObject := self serviceObject. serviceObject session: self session; allInstancesOverdueAtDate: aDate.

^self collectServiceResultsFrom: aServiceObject

Selective Queries or Custom Queries 279 We factored out the collecting of the results of the service object into a separate method (collectServiceResultsFrom:), because we can then reuse it in other query methods. Do not worry about how it works; what it does is to make the objects the service object retrieved accessible in the home: collectServiceResultsFrom: aServiceObject

"Collects the results from aServiceObject."

| bo do results |

(aServiceObject isFinished & aServiceObject isError not) ifTrue: [ results := aServiceObject oids collect: [ :anOid | do := self lookupOidFromCache: anOid. (bo := self lookupOidFromView: anOid) notNil ifTrue: [do home refresh: bo from: do] ifFalse: [self createRetrieved: do] ] ].

^results

13.5 Add a Service to the Service Object The real work is done by the service object. Therefore, we need to add an allInstancesOverdueAtDate method: to the service object—in our case, Rb70LibraryRb70BorrowedServiceObject, which is called by the home’s allInstancesOverdueAtDate: method. Use this script: allInstancesOverdueAtDate: aDate

"Answers all Borrowed objects that are/will be overdue at aDate."

'>>>executing allInstancesOverdueAtDate' vapTrace. self manyQueryUsing: self queryPool allInstancesOverdueAtDateSqlString with: (Key with: (aDate vapAsSQLStringFor: self dataStore)). self setToCompleted.

^self result.

As the generated code shows, in the first line we send a text to the ObjectExtender trace. When the tracing option is set to Basic Trace or Detailed Trace, this text is displayed on the transcript for debugging purposes.

280 Using VisualAge Smalltalk ObjectExtender The second statement is where all the pieces of the puzzle come together. We use one of the user query helpers in RelationalServiceObject, the superclass of all service objects. We use manyQueryUsing:with:. It expects an SQL string and a key as parameters. For the SQL string, we pass to the query pool the result of the method we added before. The variable already in the SQL string is replaced by what we pass as key. In our context, the key is the date by which we want to select.

The vapAsSQLStringFor: method on the date assures that the string representation of the date is one the SQL processor understands, and not the Smalltalk standard string representation. If you want to query for a single object and not for multiple objects, you could use singleQueryUsing:with: instead of manyQueryUsing:with:. It looks this: manyQueryUsing: queryString with: aKey

"Execute the extended service to retrieve multiple instances, cache them, and return their OIDs."

| aQuerySpec aQuery |

aQuerySpec := AbtQuerySpec new statement: ((SqlQueryString string: queryString) evaluate: (self genericInput: aKey)); yourself. aQuery := SqlQuery querySpec: aQuerySpec connection: (self session connection). self executeQuery: aQuery. self allInstancesLoadCache: aQuery

The setToCompleted method sets a flag in the service object indicating that its retrieval has finished. In future versions of ObjectExtender, it may no longer be necessary to do this explicitly.

Selective Queries or Custom Queries 281 13.6 Run Sample Headless with a Script We suggest that you try out the new "Day-End-Processing" script given here for changing the status of borrowed books. When you compare the new version using the new query with the version presented in the introduction to this chapter, you see somewhat simplified code and, most important, vastly improved performance. The new script is: "Day-End-Processing"

| today newStatus |

today := Date newDay: 20 month: #January year: 2000.

Transaction begin.

(Rb70BorrowedHome singleton allInstancesOverdueAtDate: today) do: [:each | newStatus := (Rb70OverdueHome singleton createForId: each id) returnDate: each returnDate; customer: each customer; book: each book; yourself. each markRemoved ].

Transaction current commit.

282 Using VisualAge Smalltalk ObjectExtender Chapter 14. Nested Transactions

In previous chapters, we have looked briefly at the way ObjectExtender handles transactions. For example, in 5.8, “Create Views with Transactions” on page 83 we show the need to have the systemwide shared transaction and an associated working transaction that we named TopLevelTransaction.

In this chapter, we develop the use of transactions further, inspired by the Library example. We use the prefix Rb80 to identify sample objects developed for this chapter.

14.1 Scenario for the Transaction The scenario for this section is that a new book arrives at the library. We envisage a GUI design that provides separate windows for Category, Title, and Book management. To make it easier to work, the user needs to be able to have the Category, Title, and Book management windows open simultaneously so that he or she can: • Locate the book title, and if not yet available, enter the book title details (title description, author, publisher). • Locate the primary and secondary categories for the book title, and—if necessary—add new categories to the category tree. To add new categories, the user may want to open several category windows at the same time in order to browse and maintain the entire category tree. • Add the located primary and secondary categories to the book title. • Enter information about the individual books (there may be more than one copy of each title).

In any one window, the user needs to be able to make and discard changes without affecting what is displayed in other windows. When the user commits a change in one window, it is then propagated to the other windows. And only when the user is happy with all the changes and commits them is the total set of changes committed to the database. We suppose that the maintenance of a book title and its copies and the maintenance of the category tree have their own nested transaction hierarchy.

Before we start to implement the scenario, we take a closer look at the nested transaction problem.

© Copyright IBM Corp. 1999 283 14.2 The Nested Transaction Problem In previous samples, we used the construct illustrated in Figure 189, with connections summarized in Table 58, to create our transaction.

Figure 189. Typical Transaction Construct

Table 58. Connections of a Typical Transaction Construct Source (S) Feature of S Target (T) Feature of T

1 pbCancel clicked transaction rollback

2 pbOKSave clicked transaction commit

3 transaction committedOrRolledback parentTransaction beginChild

4 (transaction,committedOrRolled normalResult transaction self back --> parentTransaction,beginChild)

5 Window aboutToOpenWidget parentTransaction beginChild

6 (Window,aboutToOpenWidget normalResult transaction beginChild --> parentTransaction,beginChild)

284 Using VisualAge Smalltalk ObjectExtender The key feature of the construct Figure 189 is that the Cancel and OK/Save buttons rollback and commit the transaction. The transaction’s committedOrRolledback event in turn invokes the parent transaction’s beginChild action to instantiate a new transaction. (A transaction effectively dies when it is committed.)

14.3 How Not to Create the Transaction Tree When you want to use nested transactions, you cannot use this construct. Consider the following example, which shows a nested transaction sequence, implemented using the construction in Figure 189 on page 284. We implement this sample as a thought exercise, without real code.

Parent Transaction Parent Transaction T2 T1

T2 T3

Child Transaction Child Transaction V1 - Parent View V2 - Child View

Figure 190. Nested Transaction Views

In Figure 190, we have two views, V1 and V2, where V1 is the parent of V2. We also have three transactions, T1, T2 and T3. We consider T2 as the working transaction in V1 because it is the transaction that will handle the commit in VI. However, T2 is the parent transaction in V2, so the working transaction in V2 is T3.

As these transactions are living objects, we need to keep track, during our discussion, of the instances of each of these transactions. To do so, when a transaction is first instantiated, we refer to it as, for example, T1.1. When the transaction is committed and re-instantiated, we refer to it as T1.2, and so on.

Nested Transactions 285 Our starting state can therefore be represented by the tuple {T1.1, T2.1, T3.1}.

If we click on the Save button in V2, the Figure 189 construct works. Because there has been no change to T2, it is still a valid parent for T3. After the commit in V2, the construct in Figure 189 creates a new instance of T3, so the current state is now represented by the tuple {T1.1, T2.1, T3.2}.

We now click on the Save button in V1. This fires the commit action in T2, which in turn forces a commit on all child transactions of T2. The overall sequence of events is: 1. T2.1 commit action is invoked. 2. T2.1 forces a commit on all its children. 3. T3.2 commit action is invoked by CommitChildren from T2.1. 4. T3.2 commit fires the committedOrRolledBack event as part of its completion activities. 5. The current parent, T2.1, carries out the beginChild action and creates T3.3. Remember that T2.1 is the parent of T3.3. 6. Once all T2.1’s children have committed, T2.1 carries out its own commit actions. As part of its completion activities, it fires the committedOrRolledBack event. T1.1 receives this and creates a new child, T2.2, so that T2.1 is now effectively dead.

At the end of the commit sequence, the state can be represented by the tuple {T1.1, T2.2, T3.3}. On the surface, this looks okay. We have committed the changes and have built new transactions. However, we need to look much more carefully at T3.3. When this was created in Step 5, it was created with a parent of T2.1. However, T2.1 is now dead, having been replaced by T2.2 in Step 6.

T3.3 therefore has an invalid parent, which causes all sorts of unpleasant problems. You can, if you want, try to create a nested transaction example based on this construct. If you do so, you should ensure that you have saved your code and image before starting. You should also be prepared to kill the VisualAge process forcibly.

14.4 A Better Way to Create the Transaction Tree The problem with the first nested transaction attempt was with Connections 1, 2, 3, and 4 in Table 58 on page 284. These connections caused a new child

286 Using VisualAge Smalltalk ObjectExtender transaction to be built too early. Instead, it is necessary to delay any attempt to create child transactions until the commit has completed.

Furthermore, the new child transactions must be created top-down so that there is always a parent transaction for the new child. The essentials of one construct for doing this is shown in Figure 191 with the main connections listed in Table 59. We have introduced the View as child view and the transacted business object as aBOWithDataObject to illustrate the interaction with the child view and the transacted business object.

Figure 195 on page 292 shows the complete construct with all connections—the connections listed in Table 60 on page 293 and Table 61 on page 294. Table 63 on page 296 lists the View scripts to create the transaction ID (derived from the set time), the set times, and the title for the childView.

Figure 191. Possible Construct for Recreating the Transaction Tree

The main features of the construct in Figure 191 are: • The nilChecker and sharedTransaction parts are used to initialize the parentTransaction with the sharedTransaction in the case where the View is the top-level view of a view and transaction tree.

Nested Transactions 287 • The parentTransaction of childView is actually not required. It is set through the transaction valueHolder o------o View parentTransaction connection (Connection 23) at childView creation time. • Watch the source feature of Connection 21: Not the self—which is the value of the transaction variable as it is usually passed to the View factory parentTransaction—but the valueHolder. If the value only is passed, all childViews but the most recent loose connection to the transaction variable and do not anymore receive the new transaction through the parentTransaction self ------> transaction self event-to-action connection (Connection 9) on a commit of the transaction in order to build their new transaction. • If the transaction is a topLevelTransaction and writes to the persistent data store, the Save/OK button has Save as label. If the transaction is a nested transaction, the label is OK. In this sample, the nilChecker supplies the required triggers to set the different lables. • The openOwnedWidget of the childView ensures that a closeWidget on a view closes also its childViews (Connection 15). • Watch Connection 1: The promoted transactionCommitted in any childView forces the unlisted action feature updateValueFeatures of the transactedVariable to be executed to ensure updates in the parent View. For convenience, the transactionCommittedOrRolledback feature is also promoted. • Since LinkCollections are not evented, Connection 1 is not enough to refresh the View. Collection refresh, such as Connections 2 and 3 are required for every collection of the aBOWithDataObject business object which displayed in the View. If many collections of the business object are displayed in a view, the view part becomes more cluttered with connections than desired. Therefore we implemented the Rb80Signaller part to handle this (see signaller in Rb80CategoryDetailView part in left bottom corner of Figure 201 on page 317 and Connections 9 and 11 in Table 74 on page 320). • The rollback of the transaction triggered by the closedWidget event of the View ensures that no active transaction is left over when the window is closed (Connection 29).

288 Using VisualAge Smalltalk ObjectExtender Table 59. Main Connections of Rb80TransactionExample2View Source (S) Feature of S Target (T) Feature of T

1 childView transactionCommit aBOWithDataObject updateValue ted Features

2 childView transactionCommit list items ted

3 (childView,transactio value aBOWithDataObject xyz nCommitted --> (1-to-many) list,items)

9 parentTransaction self transaction self

10 (parentTransaction,self value parentTransaction beginChild --> transaction,self)

14 pbNew clicked View new

15 pbNew clicked childView openOwned Widget

16 pbOKSave clicked transaction commit

17 pbOKSave clicked parentTransaction beginChild

18 (pbOKSave,clicked --> normalResult transaction self parentTransaction,begi nChild)

23 transaction valueHolder View parentTrans action

25 transaction self aBOWithDataObject transaction

27 View instance childView self

29 view closedWidget transaction rollback

Nested Transactions 289 The arrows in Figure 192 show the creation sequence of the transaction tree in the view tree.

View new openWidget

openOwnedWidget

Figure 192. View Tree with Nested Transactions at Runtime

290 Using VisualAge Smalltalk ObjectExtender The view hierarchy is visible through the view titles. The view with the View title was created first and hosts a topLevelTransaction as transaction. Therefore the Save/OK button has Save as label. From View the views with the titles 01:17

Clicking on the Save button of the top-level view (View window) commits the transaction. Because of the Transaction’s implementation the commit is propagated to their children and so to the child views. No new transactions are generated during the commit propagation. After the control returns to the Save button, the second clicked event-to-action connection is fired and creates and sets the new transaction. Because the transaction valueHolder was passed to the (feature promoted) parentTransaction of the childViews, the self event of the parentTransaction in each childView is fired and creates and sets the new transaction—now in the childViews, and therefore in their childViews..., and so on and so forth. The sequence is easy to recognize by the set times of the transactions. Each getting the Set msClkV (millisecondClockValue) time has a built-in 100 millisecond delay which explains the sequence (1)20009 - (2)20149 - (3)20250 - (4)20350 - (4)20450 - (5)20550 - (6)20650 - (7)20750 - (8)20850.

Figure 193 and Figure 194 show an inspection dialog for transactions and nested transactions. In the Status Tool, select Inspect-> Inspect Transaction from the menu bar. In the Which transation dialog, select the shared transaction from the list to inspect a full tree.

Figure 193. Select Transaction to Inspect

Nested Transactions 291 Figure 194. Inspection of the Top-Level Transaction and Transaction Tree

Figure 195. Rb80TransactionExample2View

292 Using VisualAge Smalltalk ObjectExtender Table 60. Connections of Rb80TransactionExample2View (Part 1 of 2) Source (S) Feature of S Target (T) Feature of T

1 childView transactionCommi aBOWithDataObject updateValue tted Features

2 childView transactionCommi list items tted

3 (childView,transactio value aBOWithDataObject xyz nCommitted --> (1-to-many) list,items)

4 nilChecker isNil parentTransaction self

5 nilChecker isNil pbOKSave object

6 nilChecker notNil pbOKSave object

7 (nilChecker,isNil --> value sharedTransaction self parentTransaction,self)

8 parentTransaction self Rb80TransactionExample2View parentTrx:

9 parentTransaction self transaction self

10 (parentTransaction,self value parentTransaction beginChild --> transaction,self)

11 pbCancel clicked transaction rollback

12 pbCancel clicked parentTransaction beginChild

13 (pbCancel,clicked --> normalResult transaction self parentTransaction,begi nChild)

14 pbNew clicked View new

15 pbNew clicked childView openOwned Widget

16 pbOKSave clicked transaction commit

17 pbOKSave clicked parentTransaction beginChild

18 (pbOKSave,clicked --> normalResult transaction self parentTransaction,begi nChild)

Nested Transactions 293 Table 61. Connection of Rb80TransactionExample2View (Part 2 of 2) Source (S) Feature of S Target (T) Feature of T

19 Rb80TransactionExam TID trxID object ple2View

20 Rb80TransactionExam parentTID parentTrxID object ple2View

21 Rb80TransactionExam TSet trxSet object ple2View

22 Rb80TransactionExam parentTSet parentTrxSet object ple2View

23 transaction valueHolder View parentTransa ction

24 transaction self Rb80TransactionExample2Vie trx: w

25 transaction self aBOWithDataObject transaction

26 view aboutToOpenWidge nilChecker check t

27 View instance childView self

28 View instance childView title

29 view closedWidget transaction rollback

30 (view,aboutToOpenWid anObject parentTransaction self get --> nilChecker,check)

31 (View,instance --> value Rb80TransactionExample2Vie childTitle childView,title) w

294 Using VisualAge Smalltalk ObjectExtender Table 62. Transaction ID Scripts of Rb80TransactionExample2View Rb80TransactionExampleView2>>#methods (transaction ID scripts)

parentTrx: aTrx

"Determine and signal parentTransaction set time and ID."

| time trxName | aTrx == nil ifTrue: [ self signalEvent: #parentTSet with: '???'. ^self signalEvent: #parentTID with: '???' ]. time := ( ( '00000' , (Time millisecondClockValue \\ 100000) printString) reverse copyFrom: 1 to: 5) reverse. (Delay forMilliseconds: 100) wait. aTrx name = 'Shared' ifTrue: [trxName := 'Shared'] ifFalse: [trxName := aTrx abtAtAttribute: #transactionName]. (trxName = 'Unknown' or: [trxName = 'transaction']) ifTrue: [ aTrx abtAtAttribute: #transactionName put: (trxName := time) ]. self signalEvent: #parentTSet with: time. self signalEvent: #parentTID with: trxName.

trx: aTrx

"Determine and signal transaction set time and ID."

| time trxName | aTrx == nil ifTrue: [ self signalEvent: #TSet with: '???'. ^self signalEvent: #TID with: '???' ]. time := ( ( '00000' , (Time millisecondClockValue \\ 100000) printString) reverse copyFrom: 1 to: 5) reverse. (Delay forMilliseconds: 100) wait. aTrx name = 'Shared' ifTrue: [trxName := 'Shared'] ifFalse: [trxName := aTrx abtAtAttribute: #transactionName]. (trxName = 'Unknown' or: [trxName = 'transaction']) ifTrue: [ aTrx abtAtAttribute: #transactionName put: (trxName := time) ]. self signalEvent: #TSet with: time. self signalEvent: #TID with: trxName.

Note: The transaction ID script uses the #parentTSet, #parentTID, #TSet, and #TID events, each with a String as argument. The events must be defined in the public interface of the Rb80TransactionSample2View in order supply the connections 16 through 19 to update the transaction information fields.

Nested Transactions 295 Table 63. Title Scripts of Rb80TransactionExample2View Rb80TransactionExampleView2>>#methods (title scripts)

shortTimeString

"Return mm:ss string form Time now."

| time |

time := Time now. ^(time minutes printStringRadix: 10 padTo: 2) , ':' , (time seconds printStringRadix: 10 padTo: 2)

childTitle

"Return childTitle of the structure: childCreationTime

| parentTitle end | (end := (parentTitle := self title) indexOf: $<) = 0 ifTrue: [end := parentTitle size + 1]. ^self shortTimeString , '<' , (parentTitle copyFrom: 1 to: end - 1)

14.5 The Easy Way to Create a Transaction Tree Use the businessTransaction to create transaction trees and make life easy.

The businessTransaction has built-in support to create a top-level transaction, when necessary. Also, it forces the top-down propagation of the new transaction, if not told otherwise (see explanation of rollbackTransactionWhenParentChanges setting in the last paragraph of 5.8.2, “Use the BusinessTransaction Part” on page 90—just in front of 5.9, “Run Sample with User Interface” on page 96).

Figure 196 shows a typical construct using the businessTransaction. Because the businessTransation is lazy in initializing the transaction and only partially signals events, it is hard to build an illustrative sample such as the Rb80TransactionExample2View.

296 Using VisualAge Smalltalk ObjectExtender Figure 196. Typical businessTransaction Construct

Note: The self attribute feature of the parentBusinessTransaction is promoted as parentBusinessTransaction. To trigger updates in the parent view, you may promote the committed (and rolled back) features also.

Table 64 lists the connections of Rb80TransactionExample3View. Table 64. Connections of Rb80TransactionExample3View Source (S) Feature of S Target (T) Feature of T

1 businessTransaction self View parentBusinessTransaction

2 parentBusinessTransaction transaction businessTransaction parentTransaction

3 pbNew clicked View new

4 pbNew clicked childView openOwnedWidget

5 pbOKSave clicked businessTransaction commit

6 View instance childView self

Nested Transactions 297 14.6 Define Model (Metadata) We implement just the Title, Category, and Book classes of the overall library sample class diagram. The class diagram in Figure 197 shows the scope of the implemented classes and associations.

Title-Book-Category Class Diagram categoryHas Parentcategory Link

parentcategory {parentcategory} Link 1 * Title primaryCategory primaryTitles Category >>isbnNumber * titleHasPrimaryCategory 1 >>categoryId __description __categoryTitle __author __publisher {secondaryCategories} {secondaryTitles} {titleHasSecondaryCategories} **

categoryAssociations titleAssociations 1 tcToTitle tcToCategory 1 1 books 1 1 title category {subcategories} subcategory * * Links titleHas books Title2Category {categoryHas categoryHas >>(r)title Subcategory Subcategies} >>(r)category Links * title

Book parentcategory >>serialNo * __condition CategoryLink >>(r)parent category >>(r)sub Key: Class Name >>object ID (attribute) category Role Name >>(r)object ID (association, 1 subcategory Associaton Name relation) {conceptual Names} __attribute (value)

Figure 197. Class Diagram of Title-Book-Category Management System Model

298 Using VisualAge Smalltalk ObjectExtender The class Rb80CategoryLink is not necessarily required, as the self referencing Department in Chapter 7, “Employee and Department Top-Down” on page 127 proves; but with a link class, the sample shows how to implement a solution for the bill of material problem. The link objects in the bill of material solution carry additional information about the composition; for example, the number of each subpart. In the category sample as exercised now, the link objects could carry the number for the decimal classification system or sort information.

Strictly speaking, the >>(r)subcategory association attribute is sufficient to identify the CategoryLink objects, but for the foreign key mapping ObjectExtender, it also requires the (r)parentcategory association attribute to be part of the primary key (see Note on page 301).

The details for the model definition are set out in Table 65 on page 299 and Table 66 on page 300. Table 65. Attribute Details for the Title, Category, and Book Classes Class Attribute Attribute Details

Rb80Category categoryId String, required, object Id

categoryTitle String, required

Rb80CategoryLink - -

Rb80Title author String, required

description String

isbnNumber String, required, object Id

publisher String, required

Rb80Book condition String

serialNo Integer, required, object Id

Note: In the implemented sample, the Rb80CategoryLink class has only association attributes, and the issue of many-to-many relationships is addressed separately in Chapter 11, “Many-to-Many Associations” on page 241.

Nested Transactions 299 Table 66. Association Details for the Title, Category, and Book Classes Association Class 2 (left Role of Class 1 Class 1 (right Role of Class 2 side in Editor) side in Editor)

categoryHasParentcate Rb80Category parentcategoryLink Rb80Category subcategory goryLink navigable Link navigable required

categoryHasSubcategor Rb80Category subcategoryLinks Rb80Category parentcategory yLinks navigable Link navigable many required

titleHasPrimaryCategory Rb80Title primaryCategory Rb80Category primaryTitle navigable navigable required many

tcToTitle Rb80TitleToCa title Rb80Title parentCategory tegory navigable navigable required required

titleHasBooks Rb80Title books Rb80Book title navigable navigable many required

When we saved the model, we used the application names shown in Table 67 and accepted the default storage class names when offered. Table 67. Application Names for Rb80 Model, Schema, and Mapping Item Application Name

Model Rb80LibraryModelApp

Schema Rb80LibrarySchemaApp

Mappings Rb80LibraryMapApp

Before proceeding to the next step, you should also generate the model code classes, using the Generate... command in the Model Browser. Although it is not strictly necessary to do this before proceeding to the next stand, it is good practice. Just prior to generating the service classes in 14.9, “Generate Service Classes” on page 304, you must have generated the model code classes.

14.7 Generate Schema (Metadata) The Entity-Relationship (ER) in Figure 198 gives an overview of the schema.

300 Using VisualAge Smalltalk ObjectExtender Title-Book-Category ER Diagram / Schema categoryHas Parentcategory Link

1

Title Category >>isbnNumber >>categoryId * titleHasPrimaryCategory 1 __description __categoryTitle __author __publisher (r)categoryId

1 tcToTitle tcToCategory 1 1 1 * * titleHas Books Title2Category categoryHas >>(r)isbnNumber Subcategory >>(r)categoryId Links * Book * >>serialNo __condition CategoryLink (r)isbnNumber >>(r)parent categoryId >>(r)sub Key: Entity & Table Name >>primary key categoryId >>(r)primary & foreign key 1 Relation Name (r)foreign key __attribute

Figure 198. ER Diagram and Schema of the Title-Book-Category Management System

Note: The (r)parentcategoryId foreign key in the CategoryLink entity and table must be part of the primary, because ObjectExtender fails in the map consistency check on the one-to-many categoryHasSubcategoryLinks association. ObjectExtender makes the check without considering that the categoryHasParentCategoryLink association is a one-to-one association

Nested Transactions 301 between the same tables as the one-to-many categoryHasSubcategoryLinks association, and therefore, the (r)subcategoryId foreign key would be unique enough to be the sole primary key component. The (r)subcategoryId foreign key is unique enough, because only one CategoryLink object can exist per Category. Defining a foreign key independent primary key for the CategoryLink table, for example, categoryId with the additional and corresponding attribute as object ID in the CategoryLink class, solves the problem, but creates difficulties in setting the primary key value or keeping it in synch with the subcategoryId foreign key.

You may use the Generate schema from model command in the Model Browser to generate the schema and data store mappings. If you define the schema manually in the Schema Browser, use the information in Figure 198, Table 68, and Table 69.

The table and column details are shown in Table 68. Table 68. Schema Definitions for Rb80 Sample Tables Table Column Definition Comment

Rb80Category categoryTitle VARCHAR(50) NOT NULL

categoryId VARCHAR(12) NOT NULL Primary Key

Rb80CategoryLink subcategoryId VARCHAR(12) NOT NULL Primary Key

parentcategoryId VARCHAR(12) NOT NULL Primary Key

Rb80Title author VARCHAR (64) NOT NULL

description VARCHAR (64)

isbnNumber VARCHAR(13) NOT NULL Primary Key

primaryCategoryId VARCHAR(12) NOT NULL

publisher VARCHAR (64)

Rb80Book condition VARCHAR (128)

isbnNumber VARCHAR(13) NOT NULL

serialNo INTEGER NOT NULL Primary Key

The foreign key details are shown in Table 69. If you define the schema manually, you have the opportunity to use logical and physical names of your choice for the columns, foreign keys, and qualifier. If you generated the schema, you may change the column and foreign key names for better

302 Using VisualAge Smalltalk ObjectExtender readability. You also need to change the generated qualifier for each table to one that is relevant to your database. Table 69. Foreign Key Definitions for the Rb80 Sample Foreign Key Name Primary Key Table and Foreign Key Table and (physical name, if different from logical) Column Column

categoryHasParentcategoryLink Rb80Category Rb80CategoryLink (parentcategoryLink) (categoryId) (subcategoryId)

categoryHasSubcategoryLink Rb80Categor Rb80CategorLink (subcategoryLink) (categoryId) (parencategoryId)

tcToCategory Rb80Category Rb80Title2Category (categoryId) (categoryId)

tcToTitle Rb80Title Rb80Title2Category (isbnNumber) (isbnNumber)

titleHasBook Rb80Title Rb80Book (isbnNumber) (isbnNumber)

titleHasPrimaryCategory Rb80Category Rb80Title (primaryCategory) (categoryId) (primaryCategoryId)

When you have completed and saved the schema details (see Table 67 on page 300 for details of the application names we used), it is a good idea to generate the physical database, using the Export Entire Schema to database command in the Schema Browser. Remember to check the System Transcript for errors.

14.8 Generate Mappings (Metadata) When you have successfully completed and saved the schema details, it is time to turn to the data store mappings. If you use the Map Browser to look at the generated table maps, you see broken elements if you made changes to the schema other than the qualifier and physical names. When we developed the sample, we elected to delete the automatically generated data store map and generate it from scratch, primarily because we did not like the supplied map name, Rb80LibraryRb80library. You may, however, choose to amend the generated map by deleting the bad relationships and supplying replacements. We named our new map Rb80Library.

When we saved it, we used the application name from Table 67 on page 300.

The relevant mapping details are set out in Table 70.

Nested Transactions 303 Table 70. Mapping Details for the Rb80 Sample Class and Table Attribute and Association Role Column and Foreign Key

Rb80Category categoryId categoryId

categoryTitle categoryTitle

parentcategoryLink categoryHasParentcategoryLink

primaryTitles titleHasPrimaryCategory

subcategoryLinks categoryHasSubcategoryLinks

titleAssociatons tcToCategory

Rb80CategoryLink parentcategory categoryHasSubcategoryLinks

subcategory categoryHasParentcategoryLink

Rb80Title author author

description description

isbnNumber isbnNumber

publisher publisher

books titleHasBook

primaryCategory titleHasPrimaryCategory

secondaryCategoryAssociations tcToTitle

Rb80Book condition condition

serialNo serialNo

title titleHasBooks

Rb80TitleToCategory category tcToCategory (Rb80Title2Category) title tcToTitle

14.9 Generate Service Classes After you have updated and saved the data store mappings, it is time to generate the real service classes. Before doing this, however, check that you have used the Model Browser to generate the real model classes. Use the Generate Services command on the Map Browser to generate the real service classes. Again, remember to check the System Transcript (trace level set to Basic Trace) for errors during the generation process. ObjectExtender makes consistency checks and reports errors to you.

304 Using VisualAge Smalltalk ObjectExtender 14.10 Hiding the Auxiliary CategoryLink Class In this section we show how to add a number of features to the sample to hide the CategoryLink class from the Category class user. The relevant changes of the classes are summarized below.

14.10.1 Deletion of a Subcategory and Its Associated Link Object There are two strategies for handling the deletion of items in a tree structure such as the categories tree: • Node deletion: Delete the category and all its subcategories all the way down through the bottom subcategories • Leaf deletion only: Delete a category only, when it has no subcategories

For this sample, we choose the second strategy, namely, that it should not be possible to delete an Rb80Category that has subcategories.

We therefore add an event to the Rb80Category with the name validationError. This event takes an AbtError as a parameter. We add code for the abtRemove method:

abtRemove

"Perform the remove action on self and the connected CategoryLink. NOTE: this method overrides that in the framework."

"First, delete the dependent subcategories if we want cascade delete" "self subcategories do: [:each | each abtRemove]."

"But if no cascade delete, do this instead" | parentcategoryLink | self subcategories isEmpty ifFalse: [| err | self signalEvent: #validationError with: ( err := AbtError new errorText: 'Cannot delete a category that has subcategories'; yourself ). ^err ].

(parentcategoryLink := self parentcategoryLink) ~~ nil ifTrue: [ parentcategoryLink abtRemove ].

super abtRemove.

Nested Transactions 305 14.10.2 Finding the Subcategories Directly We add a new public interface attribute and associated get selector, both named subcategories. We specify that the attribute data type is an OrderedCollection.

We add a method subcategories with the following script:

subcategories

"Return the value of mySubcategories as an OrderedCollection. Do not include the case where a category is its own parent (root category)"

^( ( (self subcategoryLinks collect: [:each | each subcategory]) asSortedCollection: [:a :b | a referenceString < b referenceString] ) asOrderedCollection ) remove: self ifAbsent: []; yourself.

14.10.3 Setting and Getting the Parent Category Directly First, we add a new attribute, parentcategory, to the Rb80Category class, using the Public Interface Editor. We then add the new setter and getter methods using the Script Editor. These are the setter and getter methods:

parentcategory: aCategory

"Save the value of parent. Create a new Rb80CategoryLink if one does not exist."

| categoryLink |

aCategory == nil ifTrue: [^self]. (categoryLink := self parentcategoryLink) == nil ifTrue: [ aCategory addSubcategory: self ] ifFalse: [ categoryLink parentcategory removeSubcategories: categoryLink. aCategory addSubcategoryLinks: categoryLink ]. self signalEvent: #parent with: aCategory. self signalEvent: #parentcategoryReferenceString with:self parentcategoryReferenceString

306 Using VisualAge Smalltalk ObjectExtender parentcategory

"Return the the parent category, navigating through the associated categoryLink."

| categoryLink | (categoryLink := self parentcategoryLink) == nil ifTrue: [^nil]. ^categoryLink parentcategory

14.10.4 Adding and Removing a Subcategory Directly First, we add new actions, addSubcategory and removeSubcategory parentcategory, to the Rb80Category class, using the Public Interface Editor. We then add the addSubcategory: and removeSubcategory: methods using the Script Editor. The adding and removing of a category also adds and removes the associated CategoryLink object. The signalEvent:with: methods, such as referenceString, parentcategoryReferenceString, parentcategory, and subcategories, ensure the notification of dependents.

These are the methods:

addSubcategory: aCategory

"Add aCategory as a subcategory."

| categoryLink |

self addSubcategoryLinks: ( (categoryLink := Rb80CategoryLinkHome singleton create) parentcategory: self; subcategory: aCategory; yourself ).

aCategory signalEvent: #parentcategory with: self. aCategory signalEvent: #parentcategoryReferenceString with: self referenceString.

self signalEvent: #subcategories with: self subcategories

Nested Transactions 307 removeSubcategory: aCategory

"Remove aCategory from subcategories."

| categoryLink |

self removeSubcategoyLinks: (categoryLink := aCategory parentcategoryLink). aCategory abtRemove. categoryLink abtRemove.

self signalEvent: #subcategories with: self subcategories

14.11 Add Some Convenience to the Category For the programmer’s convenience, add the following to the Category and CategoryHome class: • Reference string methods for reasonable display of references • An allInstances method for returning all instances sorted

14.11.1 Reference String Methods To display references reasonably, add the following methods to the Category class:

referenceString

"Delivers a meaningful string representing the category."

| tempString |

^(WriteStream on: String new) nextPutAll: ( (tempString := self categoryId) ~~ nil ifTrue: [tempString] ifFalse: ['nil'] ); nextPutAll: ', '; nextPutAll: ( (tempString := self categoryTitle) ~~ nil ifTrue: [tempString] ifFalse: ['???'] ); contents

308 Using VisualAge Smalltalk ObjectExtender parentcategoryReferenceString

"Delivers a meaningful string representing the parentcategory."

| parentcategory |

^(parentcategory := self parentcategory) ~~ nil ifTrue: [parentcategory referenceString] ifFalse: ['???']

Also, add the attribute feature to the public interface (getter and event only).

14.11.2 All Instances Sorted There are two ways to return the all instances sorted: • Add, for example, an ORDERD BY categoryId ASC clause to the allInstances SQL string method:

#Rb80LibraryRb80CategoryQueryPool>>#allInstancesSqlString

"Return the SQL string for the query called allInstances"

^'SELECT T1.categoryId, T1.categoryTitle FROM USERID.Rb80Category T1 ORDER BY T1.categoryId ASC’

• Override the inherited allInstances method in the CategoryHome class by adding the following method:

allInstances

"Return all instances sorted by categoryId, ascending."

^( super allInstances asOrderedCollection asSortedCollection: [ :a :b | a categoryId < b categoryId ] ) asOrderedCollection

14.12 Run Samples Headless with Scripts Once you have created all the real class code and amended it as detailed in 14.10, “Hiding the Auxiliary CategoryLink Class” on page 305, you can carry out headless testing of the code.

Nested Transactions 309 The next stage is to build a visually implemented application using nested transactions. We concentrate on management of the category object, as this illustrates the nested transaction problem best.

14.13 The Category Management System Application This application consists of two views: • The first view—Rb80CategoryList1View (Figure 199)—presents a list of categories and subcategories and provides access to the second view. It also provides functions to generate new root categories and refresh the list from the database. • The second view—Rb80CategoryDetailView (Figure 201 on page 317)— allows the title of a category to be amended and subcategories to be added. It also provides the function to delete a category.

The view implementations follow the concepts used in 6.5, “Adding the User Interface” on page 115 and 14.4, “A Better Way to Create the Transaction Tree” on page 286.

14.13.1 Category List View Actually, there are three list views. Each of them behaves differently towards the transactions of the detail views when refreshing the list. The behavior depends on how the home is attached and in which transaction context the allInstances is executed.

1st List View Figure 199 shows the Rb80CategoryList1View. The composition is very straightforward. The connection details for the Rb80CategoryList1View part are listed in Table 71 on page 314.

The key features of the Rb80CategoryList1View in Figure 199 are: • All actions run in the read-only sharedTransaction. • Opening the window, clicking on the Refresh button, and any toplevelTransaction committing categoryDetailView fill the list with the actual categories from the database, using categoryHome>>#allInstances method. • Clicking on the New Root button performs the following actions to enter the root category details: 1. Creates a new categoryDetailView instance, using the CategoryDetailView factory and #new message.

310 Using VisualAge Smalltalk ObjectExtender 2. Creates a new Category instance, using the categoryHome>>#create message, and store it in the promoted tempCategory variable of the newly created categoryDetailView. 3. Opens the newly created categoryDetailView instance using the #openWidget message.

Figure 199. Rb80CategoryList1View

• Double-clicking on a Category in the list performs the following actions to amend a Category, create and add subcategories, remove subcategories, and delete the Category: 1. Selects the double-clicked Category. 2. Creates a new categoryDetailView instance, using the CategoryDetailView factory and #new message. 3. Stores the selected Category in the promoted tempCategory variable of the newly created categoryDetailView. 4. Opens the newly created categoryDetailView instance using the #openWidget message.

Nested Transactions 311 • The read-only sharedTransaction automatically becomes the promoted parentTransaction of any newly created categoryDetailView.

The Rb80CategoryList1View as shown in Figure 199 is not transaction-safe in terms of isolation (atomic/consistent/isolated/durable: ACID). The refresh changes objects in the most recent transaction and changes in that transaction show up simultaneously in the list (before any commit). The selected item in the list has sometimes another transaction as context than the sharedTransaction, and therefore the wrong object version is displayed in the detail view on a double-click.

2nd List View Rb80CategoryList2View (Figure 200, left side) works with a transacted selectedItem and transactedcategoryHome, but its behavior is still not 100% transaction-safe in terms of isolation (ACID). The connection details are listed in Table 72 on page 315.

3rd List View Rb80CategoryList3View (Figure 200, right side) seems to respect transactions.

Figure 200. Rb80CategoryList2View and Rb80CategoryList3View Extracts

312 Using VisualAge Smalltalk ObjectExtender A few script lines solve the problem:

allInstancesTrxSave: aHome

"Get allIInstances transaction save. Resume the current Transaction deferred."

| trxSave allInstances |

trxSave := Transaction current.

Transaction shared resume. allInstances := aHome allInstances.

[trxSave resume] abtDefer.

^allInstances

First, the current transaction is saved. Then, the read-only sharedTransaction context is resumed for performing the allInstances against the home. The previous transaction context is resumed with an abtDefer and the retrieved allInstances are returned. The connection details are listed in Table 73 on page 316.

An alternate implementation of the script would be the overriding of the allInstances of the home, or—less intrusive—an additional action in the public interface and an implementing method. A view part using the extended home would then again look like the Rb80CategoryList1View.

Nested Transactions 313 Table 71. Connections of the Rb80CategoryList1View Source (S) Feature of S Target (T) Feature of T

1 CategoryDetailView instance categoryDetailView self 2 categoryDetailView transactionCommi pbRefresh click ttedOrRolledback

3 cnrCategories defaultActionRequ CategoryDetailView new ested

4 (cnrCategories,defaultActionRequ normalResult categoryDetailView tempCategory ested --> CategoryDetailView,new)

5 (cnrCategories,defaultActionRequ normalResult categoryDetailView openWidget ested --> CategoryDetailView,new)

6 ( (cnrCategories,defaultActionReq value cnrCategories selectedItem uested --> CategoryDetailView,new),normalR esult --> categoryDetailView,tempCategory)

7 pbNewRoot clicked CategoryDetailView new

8 (pbNewRoot,clicked --> normalResult categoryDetailView tempCategory CategoryDetailView,new)

9 (pbNewRoot,clicked --> normalResult categoryDetailView openWidget CategoryDetailView,new)

10 ( (pbNewRoot,clicked --> Category value categoryHome create DetailView,new),normalResult --> categoryDetailView,tempCategory)

11 pbRefresh clicked cnrCategories items

12 (pbRefresh,clicked --> value categoryHome allInstances cnrCategories,items)

13 sharedTransaction self CategoryDetailView parentTransact ion

14 Window aboutToOpenWid pbRefresh click get

314 Using VisualAge Smalltalk ObjectExtender Table 72. Connections of the Rb80CategoryList2View Source (S) Feature of S Target (T) Feature of T

1 CategoryDetailView instance categoryDetailView self 2 categoryDetailView transactionComm pbRefresh click ittedOrRolledback

3 categoryHome self transactedCategory self Home

4 cnrCategories selectedItem selectedItem self

5 cnrCategories defaultActionReq CategoryDetailView new uested

6 (cnrCategories,defaultActionRequ normalResult categoryDetailView tempCategory ested --> CategoryDetailView,new)

7 (cnrCategories,defaultActionRequ normalResult categoryDetailView openWidget ested --> CategoryDetailView,new)

8 ( (cnrCategories,defaultActionReq value selectedItem self uested --> CategoryDetailView,ne w),normalResult --> categoryDetailView,tempCategory)

9 pbNewRoot clicked CategoryDetailView new

10 (pbNewRoot,clicked --> normalResult categoryDetailView tempCategory CategoryDetailView,new)

11 (pbNewRoot,clicked --> normalResult categoryDetailView openWidget CategoryDetailView,new)

12 ( (pbNewRoot,clicked --> Category value transactedCategory create DetailView,new),normalResult --> Home categoryDetailView,tempCategory)

13 pbRefresh clicked cnrCategories items

14 (pbRefresh,clicked --> value transactedCategory allInstances cnrCategories,items) Home

15 sharedTransaction self selectedItem transaction

16 sharedTransaction self CategoryDetailView parentTransact ion

17 sharedTransaction self transactedCategory transaction Home

18 Window aboutToOpenWid pbRefresh click get

Nested Transactions 315 Table 73. Connections of the Rb80CategoryList3View Source (S) Feature of S Target (T) Feature of T

1 CategoryDetailView instance categoryDetailView self 2 categoryDetailView transactionCommi pbRefresh click ttedOrRolledback

3 cnrCategories selectedItem selectedItem self

4 cnrCategories defaultActionRequ CategoryDetailView new ested

5 (cnrCategories,defaultActionReque normalResult categoryDetailView tempCategory sted --> CategoryDetailView,new)

6 (cnrCategories,defaultActionReque normalResult categoryDetailView openWidget sted --> CategoryDetailView,new)

7 ( (cnrCategories,defaultActionRequ value selectedItem self ested --> CategoryDetailView,new) ,normalResult --> categoryDetailView,tempCategory)

8 pbNewRoot clicked CategoryDetailView new

9 (pbNewRoot,clicked --> normalResult categoryDetailView tempCategory CategoryDetailView,new)

10 (pbNewRoot,clicked --> normalResult categoryDetailView openWidget CategoryDetailView,new)

11 ( (pbNewRoot,clicked --> Category value categoryHome create DetailView,new),normalResult --> categoryDetailView,tempCategory)

12 pbRefresh clicked Rb80CategoryList3V allInstancesTr iew xSave:

13 (pbRefresh,clicked --> Rb80Categ normalResult cnrCategories items oryList3View,allInstancesTrxSave:)

14 (pbRefresh,clicked --> Rb80Categ parameter1 categoryHome self oryList3View,allInstancesTrxSave:)

15 sharedTransaction self CategoryDetailView parentTransa ction

16 sharedTransaction self selectedItem transaction

17 Window aboutToOpenWid pbRefresh click get

316 Using VisualAge Smalltalk ObjectExtender 14.13.2 Category Detail View Figure 201 shows the Rb80CategoryDetailView in the Composition Editor with the connections listed in Table 74 on page 320 through Table 77 on page 323. Table 202 on page 318 shows the list and detail views at runtime. The trace in the System Transcript shows the database committed insert of Category C1.6 and the Category List 3 (All) refresh. The Category C1.7 is already committed to Category C1, but not yet to the database. Category C1 shows already Category C1.7 and hasModific(ation)s:, but not yet the List).

Figure 201. Rb80CategoryDetailView in Composition Editor

Nested Transactions 317 Figure 202. Rb80DetailView at Runtime

318 Using VisualAge Smalltalk ObjectExtender For the subcategories list update on a commit of any next lower level of nested transactions (Connection 11), the new Rb99Signaller part is introduced. It knows the category transacted variable (valueHolder) with the business object (Connection 9) and a list of featureNames, which are set at edit time (Figure 203) to signalSelectedDependents with this method: signalSelectedDependents

"Perform the signalSelectedDependents on transactedVariable (valueHolder) value in its context using the list of featureNames. First save the current transaction, afterwards resume it. featureNames is anOrderedCollection of Symbols to signal changes on the transactedVariable value, using the pattern: self signalEvent: #symbol with: self symbol where self is the value of the transactedVariable and symbol the getter for (mostly) (link) collections (because ObjectExtender does not update/event (link) collections automatically... with good performance reason."

| object trx saveTrx | saveTrx := Transaction current. (trx := self transactedVariable transaction) == nil ifTrue: [^self]. trx resume. (object := transactedVariable value) == nil ifFalse: [ self featureNames do: [ :symbol | object signalEvent: symbol with: (object perform: symbol) ] ]. saveTrx resume

Figure 203. featureNames of signaller in Rb80DetailView

Nested Transactions 319 Table 74. Connections of the Rb80CategoryDetailView (Part 1 of 4) Source (S) Feature of S Target (T) Feature of T

1 category transaction transaction self

2 category isNotPersistent categoryIdField enabled

3 category categoryId categoryIdField object

4 category categoryTitle categoryTitleField object

5 category isPersistent pbDeleteCategory enabled

6 category parentcategoryR parentCategoryIdField object eferenceString

7 category self CategoryDetailView tempParentcat egory

8 category subcategories cnrSubcategories items

9 category valueHolder signaller transactedVar iable

10 CategoryDetailView instance categoryDetailView self

11 categoryDetailView transactionCom signaller signalSelecte mitted dDependents

12 categoryIdField object concat string1

13 categoryIsNew isTrue transaction rollback

14 categoryIsNew isFalse transaction rollback

15 (categoryIsNew,isFalse --> normalResult junction junct transaction,rollback)

16 (categoryIsNew,isTrue --> normalResult Window closeWidget transaction,rollback)

17 categoryTitleField object concat string3

18 cnrSubcategories selectionIsValid pbDeleteSubcat enabled

19 cnrSubcategories defaultActionReq CategoryDetailView new uested

20 cnrSubcategories selectedItem selectedIItem self

21 cnrSubcategories items subcateogories self

320 Using VisualAge Smalltalk ObjectExtender Table 75. Connections of the Rb80CategoryDetailView (Part 2 of 4) Source (S) Feature of S Target (T) Feature of T

22 (cnrSubcategories,defaultActionRe normalResult categoryDetailView tempCategor quested --> y CategoryDetailView,new)

23 (cnrSubcategories,defaultActionRe normalResult categoryDetailView openOwned quested --> Widget CategoryDetailView,new)

24 ( (cnrSubcategories,defaultAction value selectedIItem self Requested --> CategoryDetailView,new),normalR esult --> categoryDetailView,tempCategory)

25 concat string Window title

26 isTopLevelTransaction isTrue pbOKSave object

27 isTopLevelTransaction isFalse pbOKSave object

28 junction juncted parentTransaction self

29 (junction,juncted --> value parentTransaction self parentTransaction,self)

30 parentcategoryIsNil isNil category parentcatego ry

31 (parentcategoryIsNil,isNil --> value tempParentcategory self category,parentcategory)

32 parentTransaction self Rb80CategoryDetailVi parentTrx: ew

33 parentTransaction self transaction self

34 (parentTransaction,self --> value parentTransaction beginChild transaction,self)

35 pbCancel clicked categoryIsNew check

36 (pbCancel,clicked --> anObject category isNotPersiste categoryIsNew,check) nt

37 pbDeleteCategory clicked category remove

38 (pbDeleteCategory,clicked --> normalResult transaction commit category,remove)

Nested Transactions 321 Table 76. Connections of the Rb80CategoryDetailView (Part 3 of 4) Source (S) Feature of S Target (T) Feature of T

39 (pbDeleteCategory,clicked --> normalResult Window closeWidget category,remove)

40 pbDeleteSubcat clicked selectedIItem remove

41 (pbDeleteSubcat,clicked --> aCategory cnrSubcategories selectedItem selectedIItem,remove)

42 (pbDeleteSubcat,clicked --> errorResult errorPrompter promptFor: selectedIItem,remove)

43 (pbDeleteSubcat,clicked --> normalResult subcateogories remove: selectedIItem,remove)

44 ( (pbDeleteSubcat,clicked --> anObject selectedIItem self selectedIItem,remove),normalRes ult --> subcateogories,remove:)

45 pbNewSubcat clicked CategoryDetailView new

46 (pbNewSubcat,clicked --> normalResult categoryDetailView tempCategor CategoryDetailView,new) y

47 (pbNewSubcat,clicked --> normalResult categoryDetailView openOwned CategoryDetailView,new) Widget

48 ( (pbNewSubcat,clicked --> value categoryHome create CategoryDetailView,new),normalR esult --> categoryDetailView,tempCategory)

49 pbOKSave clicked transaction commit

50 (pbOKSave,clicked --> normalResult junction junct transaction,commit)

51 (pbOKSave,clicked --> errorResult junction junct transaction,commit)

52 Rb80CategoryDetailView TID trxIDField object

53 Rb80CategoryDetailView parentTID parentTrxIDField object

54 subcateogories isEmpty pbDeleteCategory enabled

55 transaction self Rb80CategoryDetailVi trx: ew

56 transaction self isTopLevelTransaction check

322 Using VisualAge Smalltalk ObjectExtender Table 77. Connections of the Rb80CategoryDetailView (Part 4 of 4) Source (S) Feature of S Target (T) Feature of T

57 transaction hasModifications tbHasModifications selection

58 transaction valueHolder CategoryDetailView parentTransa ction

59 transaction self selectedIItem transaction

60 (transaction,self --> anObject transaction isTopLevel isTopLevelTransaction,check)

61 Window aboutToOpenWid category self get

62 Window aboutToOpenWid parentcategoryIsNil check get

63 Window closedWidget transaction rollback

64 (Window,aboutToOpenWidget --> value tempCategory self category,self)

65 (Window,aboutToOpenWidget --> anObject category parentcatego parentcategoryIsNil,check) ry

66 zzInspectHotSpot clicked Rb80CategoryDetailVi inspect: ew

67 (zzInspectHotSpot,clicked --> parameter1 category self Rb80CategoryDetailView,inspect:)

Note: The zzInspectHotSpot on the Category Id label triggers the inspector through the inspect: script (Connection 66)) on the category business object (Connection 67) and is for test purposes only.

Nested Transactions 323 324 Using VisualAge Smalltalk ObjectExtender Chapter 15. Concurrent Transactions

In Chapter 14, “Nested Transactions” on page 283, we cover transactions in detail, focusing especially on nested transactions. However, there is another dimension to transactions, namely concurrence. A transaction can have many parallel child transactions, and there can be transactions in different processes or even on different machines working on the same objects at the same time. In this chapter we explain how you can handle some of the conflicts that come up in concurrent scenarios with ObjectExtender.

15.1 Isolation Policies A transaction is essentially the management of resources over a period of time. Within a transaction, ObjectExtender keeps track of which objects are created, deleted, or modified. It also manages isolation of changes between transactions, so that changes within a transaction are not seen outside the scope of the transaction until it is committed.

ObjectExtender also supports optimistic (nonlocking) and pessimistic (locking) strategies on the back-end data store: • Optimistic means that usually no collisions are anticipated, but any that occur will be dealt with. Locking and updating are deferred until the ObjectExtender transaction is committed. This is probably the appropriate strategy most of the time, because it allows you longer-lasting transactions without having locks or tying up other significant back-end resources. • Pessimistic means that because you do not want to deal with collisions, resources are locked for exclusive use. If you have compelling reasons to use pessimistic locking, you can indicate this in the Map Browser for selected classes with Enable pessimistic locking. This causes different service code to be built; as a result, it is not possible to change the locking strategy dynamically at run time.

What you can set at run time is the isolation policy for each transaction. The repeatable read isolation policy guarantees that if the same object is fetched multiple times within a transaction, the attribute values will always be the same. This is not the case with the unrepeatable read policy, where the attribute values of a business object are not affected by uncommitted changes in sibling transactions. However, if the sibling transaction commits before the current transaction, changes made in the sibling may become visible in the current transaction.

© Copyright IBM Corp. 1999 325 15.2 Optimistic Locking If you do not specify any particular isolation policy, what you get is a nonlocking implementation of repeatable read. This can also be called copy-on-read, because a copy of the data of a business object is made the first time it is read in a transaction. Each subsequent operation on the object in that transaction, whether read or write, is then performed on the copy.

As soon as the transaction is committed, any changes to the object are written to the version of the object in the parent transaction, or to the data store, if a top-level transaction is being committed. At this time, however, the attribute values of the object in the parent transaction, or in the data store, might be different from those that were copied-on-read. A sibling transaction or another top-level transaction might have committed its changes in the meantime. This kind of situation is called collision.

15.2.1 Collision Detection in the Image In Chapter 11, “Many-to-Many Associations” on page 241, we have two windows for working with titles: one contains a list of all titles, the second is opened for editing a selected title. Nothing prevents you from having more than one detail view on a title. Because we have only one top-level transaction, however, all detail windows show and work on the same version of the object. As a result, a change in one window is reflected everywhere.

That may not always be the desired behavior in your applications. You may not want to see any changes you make in one window anywhere else, unless you click an OK or Apply button. Furthermore, you may want to allow the user to discard attempted changes altogether. As Chapter 14, “Nested Transactions” on page 283 shows, the way to do that is with nested transactions.

For the present example, we again strip down the application to the minimum. We ignore the association of titles to categories and work only with titles and their attributes. But we do add a nested transaction for each details window.

To follow us through this example, once you have defined the necessary model, schema, and mapping for Title, with the same attributes and columns as in Chapter 11, “Many-to-Many Associations” on page 241, you can advance to constructing the Rb90TitleView part with the help of Figure 204 and Table 78 on page 329.

326 Using VisualAge Smalltalk ObjectExtender Figure 204. Rb90TitleView

Rb90TitleView is not much different from Rb50TitleView, except for: • A new OK button, which explicitly triggers a commit on the top-level transaction. Triggering is no longer done as a reaction to events from the secondary window. • A transacted variable for the title home, whose transaction attribute is connected to the top-level transaction. • A message prompter for displaying errors that occur when committing the top-level transaction. The prompter informs us about collisions. • The parameter that is passed to the secondary window is no longer self of transacted title, but valueHolder. So what we are really passing is the variable with contents, not the contents alone.

Concurrent Transactions 327 This last change needs more explanation. When you construct the Rb90TitleDetailsView part (Figure 205 on page 330), a transacted variable for the selected title is also contained. As a result, we could connect the transacted title in Rb90TitleView and the transacted title in Rb90TitleDetailsView. Because we want a nested transaction for each secondary window, the transacted variable in Rb90TitleDetailsView needs to be connected to the new child transaction created when the window is opened.

This works fine when we edit an existing title, but when we create a new title and open the detail window for editing it, the Rb90TitleDetailsView part’s initialization order makes a significant difference. Parameters passed to the factory of a part are set at creation time, before the start of any event handling within the part. The transacted title is set before the new child transaction is created in the new part. The result of the beginChild action, which is triggered by the openedWidget event, is then assigned to the transaction attribute of the transacted title. This change of transactions for a transacted variable works if the variable holds an existing object. However, it does not work if it holds a new object. Therefore we need to create the child transaction, assign it to the transaction attribute of the transacted variable, and then set the contents of the variable.

However, the right order of initialization is only the first part of the story. We could have managed to achieve that by passing both the top-level transaction and the transacted variable to Rb90TitleDetailsView. Instead, we chose to import the transacted variable as a whole, thus having a variable of a transacted variable in the secondary part. The advantage of this peculiar solution is that we do not need to pass the top-level transaction separately, because it is an attribute of the AbtTransactedVariable.

328 Using VisualAge Smalltalk ObjectExtender Table 78. Connections of Rb90TitleView

Source (S) Feature of S Target (T) Feature of T

1 titleContainer defaultActionRequested editButton click

2 titleContainer selectionIsValids editButton enabled

3 titleContainer selectionIsValids deleteButton enabled

4 newButton clicked transacted titleHome create

5 (newButton,clicked --> transacted normalResult transacted title self titleHome,create)

6 newButton clicked editButton click

7 editButton clicked Rb90TitleDetailsView new

8 Rb90TitleDetailsView instance titleDetailsView self

9 editButton clicked titleDetailsView openWidget

10 Window openedWidget sharedTransaction beginChild

11 (Window,openedWidget --> normalResult topLevelTransaction self sharedTransaction,beginChild)

12 deleteButton clicked transacted title remove

13 deleteButton clicked topLevelTransaction commit

14 okButton clicked topLevelTransaction commit

15 (okButton,clicked --> errorResult commitErrorPrompter promptFor: topLevelTransaction,commit)

16 (deleteButton,clicked --> errorResult commitErrorPrompter promptFor: topLevelTransaction,commit)

17 topLevelTransaction committed sharedTransaction beginChild

18 (topLevelTransaction,committed normalResult topLevelTransaction self --> sharedTransaction,beginChild)

19 transacted title transaction topLevelTransaction self

20 Window closedWidget topLevelTransaction rollback

21 titleHome self transacted titleHome self

22 topLevelTransaction self transacted titleHome transaction

23 titleContainer selectedItem transacted title self

24 transacted title valueHolder Rb90TitleDetailsView valueHolderVariab le

25 transacted titleHome allInstances titleContainer items

Concurrent Transactions 329 The other details of the Rb90TitleDetailsView part are shown in Figure 205 and Table 79.

Figure 205. Rb90TitleDetailsView

The major differences from the Rb50TitleDetailsView are: • There are no more lists and buttons to work with the title’s categories. • All activity related to the nested transaction has been added.

A message prompter displays any errors that occur when the nested transaction is committed.

330 Using VisualAge Smalltalk ObjectExtender Table 79. Connections of Rb90TitleDetailsView Source (S) Feature of S Target (T) Feature of T

1 okButton clicked childTransaction commit

2 cancelButton clicked childTransaction rollback

3 transacted titleVariable isbnNumber isbnNumberField object

4 transacted titleVariable isNotPersistent isbnNumberField enabled

5 transacted titleVariable author authorField object

6 transacted titleVariable publisher publisherField object

7 transacted titleVariable description descriptionField object

8 Window openedWidget isbnNumberField setFocus

9 Window openedWidget parentTransaction beginChild

10 (Window,openedWidget --> normalResult childTransaction self parentTransaction,beginChild)

11 childTransaction committedOrR Window closeWidget olledback

12 (okButton,clicked --> errorResult commitFailedPro promptFor: childTransaction,commit) mpter

13 (Window,openedWidget --> normalResult transacted transaction parentTransaction,beginChild) titleVariable

14 (Window,openedWidget --> normalResult transacted self parentTransaction,beginChild) titleVariable

16 ( ( (Window,openedWidget --> parameter1 valueHolderVaria self parentTransaction,beginChild),normalRes ble ult --> transacted titleVariable,self),value --> Rb90TitleDetailsView,fromValueHolder:)

17 valueHolderVariable transaction parentTransaction self

Now we are ready to address the central issue of this chapter: What happens when we make changes to one title in two different windows concurrently? Try it out. What you should see when you commit is a message box like that in Figure 206 as soon as you commit the second nested transaction (that is, click the OK button).

Concurrent Transactions 331 Figure 206. Collision Detection in Sibling Nested Transactions

This tells you that, since the first nested transaction has already committed its changes, it is no longer possible to commit the changes in the second nested transaction to the parent top-level transaction. This is what is called collision detection. In a real world application, you would either display an instructional message to the user or implement your own collision resolution.

To implement your own strategy, you could use commitWhenFailureDo: and do whatever you want to do in the exception block, or you could override the canMerge:with: and merge:with: methods in the business object. In your implementation of canMerge:with:, you could check the conditions when you want to merge the parent and child versions of the business object, which are passed as parameters to that method. The default return value is false. In merge:with: you can actually perform the merging (the default implementation is copying the child to the parent). If, for example, you want to have a "last wins" behavior, you could always return true for canMerge:with:.

15.2.2 Collision Detection on the Back-End Data Store Now what happens if you have two Rb90TitleView windows and change the same title in their respective subwindows? Well, nothing special. As soon as you commit one top-level transaction (by clicking the OK button on the Rb90TitleView), the changes cause an update to the database. The same happens when you commit the second top-level transaction. So normally the last committing transaction "wins."

If you want the same behavior as with concurrent nested transactions for concurrent top-level transactions, let the data store do the collision detection. Normally, the SQL update statements generated by ObjectExtender use the primary key in the WHERE clause to identify the row to be updated. In the Map Browser, however, you can specify attributes to Be part of optimistic predicate. That means that the WHERE clause is enhanced by additional expressions to identify the row. When a top-level transaction is committed,

332 Using VisualAge Smalltalk ObjectExtender ObjectExtender fills these expressions with the attribute values that were read from the database before the object was changed, as is symbolically shown in the following SQL statement: UPDATE TableName SET key = keyValue, attribute = newAttributeValue, ... WHERE (key = keyValue) AND (attribute = oldAttributeValue)

When the row is not changed during the transaction, that means no other top-level transaction has changed the corresponding object, and the database finds the right row to be updated. If, however, the row (to be precise, the optimistic predicate attribute) was changed in the meantime, the database can no longer find a row to update and reports an SQL error. This error is then transferred by ObjectExtender to your application so you can deal with it.

Thus, if you want collision detection between top-level transactions, whether they run in the same process or not, you must specify an optimistic predicate. It can be either any set of business object attributes, if your business logic requires that, or a special nonbusiness attribute such as a time stamp or a version number.

We have no sensible business requirement for a time stamp or version number, but there is an easy way to try out this method of collision detection. Mark an attribute—for example, author—as part of the optimistic predicate (do not forget to regenerate the service code) and perform a Global ObjectExtender Reset. When you then open two Rb90TitleViews and change the same title in their subviews, you should get an error message when the second top-level transaction is committed (if you use DB2 the error message looks like this: "[SQLSTATE=02000 - [IBM][CLI Driver][DB2/2] SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a query is an empty table. [Native Error=100])."

Again, you can use the exception block of commitWhenFailureDo: to perform the collision resolution or inform the user in intelligible words.

You have to do a bit more to introduce nonbusiness attributes, such as a version number or a time stamp. Besides adding an attribute to the model and a column to the schema, you need to add code that increments the version number or sets the time stamp just before you commit the transaction. Setting the time stamp from Smalltalk bears the risk (admittedly slight) that you could have the same exact time stamp on different machines.

Concurrent Transactions 333 Another way to introduce nonbusiness attributes is to use database time stamps. Although this approach involves changing generated code, you can change the SQL strings in the query pool so that they use CURRENT TIMESTAMP, which returns the current database time stamp. As a result, when you add a timestamp attribute to our Title model and a timestamp column to the schema (the standard time stamp size is 26 characters), mark the timestamp attribute as being part of the optimistic predicate, generate all the necessary code, and change the insertSqlString and updateSqlString methods from:

insertSqlString

"Return the SQL string for the query called insert"

^'INSERT INTO USERID.Rb90Title ( isbnNumber, author, description, publisher, timestamp ) VALUES ( :isbnNumber, :author, :description, :publisher, :timestamp ) '

updateSqlString

"Return the SQL string for the query called update"

^'UPDATE USERID.Rb90Title SET author = :author, description = :description, publisher = :publisher, timestamp = :timestamp WHERE (isbnNumber = :isbnNumber) AND (timestamp = :V1)'

to: insertSqlString

"Return the SQL string for the query called insert"

^'INSERT INTO USERID.Rb90Title ( isbnNumber, author, description, publisher, timestamp ) VALUES ( :isbnNumber, :author, :description, :publisher, CURRENT TIMESTAMP ) '

updateSqlString

"Return the SQL string for the query called update"

334 Using VisualAge Smalltalk ObjectExtender ^'UPDATE USERID.Rb90Title SET author = :author, description = :description, publisher = :publisher, timestamp = CURRENT TIMESTAMP WHERE (isbnNumber = :isbnNumber) AND (timestamp = :V1)'

Because you took away host variables in the SQL query string ObjectExtender intended to fill, you must remove the timestamp attribute from the list of attributes to be written. If you do not, you get an error message ("Input argument size does not match query string") on commit. The list of data attributes is assembled in the ...Data and ...UpdateData methods of the data object (where the ellipses stand for the class name), so you have to remove the line for timestamp and reduce the array size by 1.

15.3 Pessimistic Locking With the optimistic scenarios we have discussed so far, collisions are detected when the transactions are committed and changes written to the database. Pessimistic strategies avoid collisions altogether by apparently locking resources for exclusive use. There are no collisions when writing data, but they may occur when trying to acquire a lock. Trying to acquire a lock may be done explicitly by the application programmer or implicitly by ObjectExtender.

If you tell ObjectExtender to generate locking implementations for the service code for (some of) your classes and your transaction is set to support repeatable reads, ObjectExtender implicitly tries to procure a lock when an object is read (lock-on-read). If the transaction’s isolation policy is unrepeatable reads, an attempt to acquire a lock is automatic when an object is changed (lock-on-write).

Your code must take care of exceptions that are raised if an object is already locked. For instance, if you use allInstances within a transaction with a repeatable read strategy, add: Transaction begin. [ titles := Rb90TitleHome singleton allInstances ] when: #ExVapObjectLocked do: [ :aSignal | "Notify the user that an object is locked" ]

Regardless of the transaction’s isolation policy, you can try to explicitly lock an object: [ title lock ] when: #ExVapObjectLocked do: [ :aSignal | "Notify the user that the object is locked" ]

Concurrent Transactions 335 15.4 Summary of Transaction Characteristics The characteristics of transactions are as follows: • Whenever a transaction is created, it becomes the current transaction. • Whenever a transaction is committed or rolled back, its parent transaction becomes the current transaction. • When a transaction is committed, all of its child transactions are also committed. • A transacted variable part will ensure that its contents are always accessed in the context of its specified transaction, but it will not change the current transaction. • Changes to objects in top-level transactions are committed to the data store. • Changes to objects in nested transactions are committed to the parent transaction. • The isolation policy implemented in the home collections can be either optimistic (nonlocking) or pessimistic (locking). The policy cannot be changed at run time. • The isolation policy for a transaction can be either repeatable read (copy/lock-on-read) or nonrepeatable read (copy/lock-on-write). It can be changed at run time. • Collision detection between sibling nested transactions is done automatically by ObjectExtender. • Collision detection between top-level transactions is done through the database by telling ObjectExtender to use optimistic predicates.

336 Using VisualAge Smalltalk ObjectExtender Chapter 16. Binary Large Objects

This chapter is a short excursion into binary large objects (BLOBs), a subject that is not directly related to ObjectExtender but sets the scene for Chapter 17, “Lite Collections” on page 341.

A BLOB is a database data type for varying-length strings measured in bytes that can be as large as 2+ GB (2 147 483 647 bytes). A BLOB is primarily intended to hold nontraditional data such as pictures, voice, and mixed media. Another use is to hold structured data for exploitation by user-defined types and functions.

In the example in Chapter 17, “Lite Collections” on page 341, we store pictures in BLOBs. One standard for the definition of pictures is the graphics interchange format (GIF), and that is the one we want to adhere to. In this chapter we describe the construction of a nonvisual part that can hold a GIF image, read a GIF image from a file, display the image in a window, and make the necessary conversions to and from strings. ObjectExtender can then store these strings as BLOBs in the database.

16.1 Create a New Part Named GifImage To create the new part, select an existing application or create a new one (we created a new application called Rb10GifImage) in the VisualAge Organizer. In the dialog that follows after you select Parts->New..., enter the part name (we chose GifImage) and be sure that the part type is set to Nonvisual part. We use this part type because we want the part to be usable in all situations, whether a user interface is involved or not.

16.2 Define the Part’s Public Interface The part’s public interface is that subset of the whole class functionality that is visible when parts are plugged together in the Composition Editor. To define the public interface, use the Public Interface Editor.

We want the GifImage part to interact with three other parts: • We want to be able to read an image from a GIF file. Therefore we define an action called contentsFromGifFile (action selector: contentsFromGifFile:), which takes a string holding the file’s path and name as a parameter.

© Copyright IBM Corp. 1999 337 • Most of the time the image the part holds must be displayed somewhere. An attribute called displayWindow serves that purpose. • Last but not least, we want the GifImage part to be able to build its image from a string and, in the other direction, convert the image back to a string. That is why we define an attribute called imageAsString with a get selector imageAsString and a set selector contentsFromString:. The change event symbol is also imageAsString, and the attribute data type is String.

When the public interface is defined, File->Generate Default Scripts can be used to create implementation skeletons. Select all the selectors but be sure not to select imageAsString in the instance variable list box, because we do not want an instance variable for that; we only want the part to behave as if it had an instance variable. Click Generate Selected to continue.

16.3 Provide the Part’s Implementation Once you have defined the interface, the next step is the implementation. As mentioned, what the part is intended to hold—but what we did not make public—is an image called a CgDeviceIndependentImage. Therefore, an instance variable called image, with its private getter image and private setter image:, needs to be defined.

Remember that we are working on a part. Therefore we have to ensure that our part does what is expected from a part: that is, it notifies other interested parts of changes to its public attribute values. So we have to add the required code in the image: setter method. Note we do not notify others of a change in image, because that is not visible to the outside world, but rather we notify of a change in what we provide to other parts: imageAsString. The script is:

image: aCgDeviceIndependentImage

"Private - Save the value of image."

image := aCgDeviceIndependentImage. self signalEvent: #imageAsString with: self imageAsString

The implementation for contentsFromGifFile:, which reads an image from a GIF file, looks like this:

338 Using VisualAge Smalltalk ObjectExtender contentsFromGifFile: path

"Sets the receiver's contents with the data from the GIF file specified by path."

self image: ( CgGIFFileFormat new loadFromFile: path; yourself ).

self displayImage

The contentsFromGifFile: method calls as its last statement the private method displayImage that draws the image on the window associated with the part:

displayImage

"Private - Displays the image on the associated window"

self displayWindow isNil ifFalse: [ self image isNil ifTrue: [ self displayWindow primaryWidget window clearWindow ] ifFalse: [ self displayWindow primaryWidget window putDeviceIndependentImage: CgGC default image: self image srcRect: (0@0 extent: self image extent) destRect: (0@0 extent: self image extent) ] ]

The most interesting methods for us, however, are those that perform the conversion to and from strings. The easier direction is building the image from a string. As in contentsFromGifFile:, the essential work is done by an instance method of CgGIFFileFormat:

contentsFromString: aString

"Builds the receiver's image from aString."

aString isNil ifTrue: [

Binary Large Objects 339 self image: nil ] ifFalse: [ self image: ( CgGIFFileFormat new loadFromByteObjects: (Array with: aString) offsetsIntoByteObjects: (Array with: 0) yourself ) ].

self displayImage

The conversion from the image to a string is a bit more complicated. We build an array of ByteArrays, which is then filled from a method in CgGIFFileFormat again. The filled arrays must then be flattened again into a string:

imageAsString

"Answers the image as a string"

| gifFileFormat imageSize byteArrays offsets ws |

self image isNil ifTrue: [^nil].

gifFileFormat := CgGIFFileFormat new. imageSize := gifFileFormat totalSizeBeforeUnload: self image.

byteArrays := OrderedCollection new. byteArrays add: (ByteArray new: imageSize). byteArrays := byteArrays asArray. offsets := Array with: 0.

( gifFileFormat unload: image intoByteObjects: byteArrays offsetsIntoByteObjects: offsets ) ifFalse: [ self error: gifFileFormat currentErrorString ].

ws := WriteStream on: ByteArray new. byteArrays do: [ :each | ws nextPutAll: each].

^ws contents

With this part, we are now ready to plunge right back into ObjectExtender issues; Lite Collections are next.

340 Using VisualAge Smalltalk ObjectExtender Chapter 17. Lite Collections

Lite Collections are useful for retrieving a subset of the information from a particular object in the database without instantiating every object that is retrieved. This feature can allow you to tune performance: For example, when building views to display different parts of the model, you can load the necessary information for a particular view only, and "drill deeper" as needed. Lite Collections can be thought of as a way of filtering an object that might have many attributes not applicable in a given context.

Imagine that we want to enhance the Employee example with photos of the employees. Considering the size of pictures stored in the database as BLOBs for that purpose, methods such as allInstances in the home collections, which retrieve all the data for every object, would take a very long time. With the help of Lite Collections, we therefore retrieve a list of employees with their text data only and load the picture of the selected employee on demand.

17.1 Define Model, Schema, and Mapping For the sake of simplicity, we want to keep the model very lean. The Employee class needs only the attributes listed in Table 80. It is worth noticing that String is the type to use for holding things that are stored to the database as BLOBs—in this example, the picture of the employee. Table 80. Attributes of Class Rb11Employee Attribute Name Attribute Type Required

empNo String Yes (Object ID)

firstName String No

lastName String No

picture String No

It is in the Class Editor where not only attributes but also Lite Collections are defined. Figure 207 shows how a Lite Collection encompassing only the empNo, firstName, and lastName attributes are defined. We chose here not to use any filtering (that is, being able to select objects on the value of a filter property), or packeting (that is, reading only subsets of large result sets one at a time from the database).

© Copyright IBM Corp. 1999 341 Figure 207. Definition of a Lite Collection for Rb11Employee

As far as defining the schema is concerned, the only thing out of the ordinary is the specification of the database type for picture: BLOB. We set the maximum length (in bytes) to 50,000, because the GIF images we stored as BLOBs had about that size. You can adjust the length to meet your needs.

Mapping is straightforward, but there is one point to observe: select Generate static Queries in the Generation Options dialog before you generate your service code! The interface between VisualAge Smalltalk and DB2 cannot currently handle dynamic SQL calls of the size that results from working with BLOBs. It is not necessary, however, to precompile the SQL statements as static; choosing the static flavor in ObjectExtender is enough.

17.2 Create a View with a Lite Collection To illustrate the workings of Lite Collections, we use a visual part consisting of a container and two buttons, as shown in Figure 208. For full details on the connections, see Table 81.

342 Using VisualAge Smalltalk ObjectExtender Figure 208. Rb11EmployeeView Part

The container holds and displays the Lite Collection of the employees without their pictures. The contents of the part are set to the result of the getEmployeeWithoutPictureLiteCollection action of the employeeHome, which has been generated as a consequence of the definition of the Lite Collection beforehand (see Connections 1 and 2 in Table 81 on page 344). Be aware that the Lite Collection holds only mere data objects, not the entire business objects. Later, we show how to get the respective business objects from the data objects.

We use the Refresh button as the focal point for the getEmployeeWithoutPicture action, which can be triggered by either the openedWidget event, the topLevelTransactionCommitted event of the details view, or the button’s click event. This technique helps to reduce clutter, and we use it, too, to open the details view of the selected employee by a click on

Lite Collections 343 the Edit employee button as well as by a double-click on the selected employee in the container. Table 81. Connections of Rb11EmployeeView Source (S) Feature of S Target (T) Feature of T

1 refreshButton clicked employeeHome getEmployee WithoutPicture LiteCollection

2 (refreshButton,clicked --> normalResult liteCollectionContainer items employeeHome,getEmplo yeeWithoutPictureLiteColl ection)

3 Rb11EmployeeDetailsView employeeDataObject liteCollectionContainer selectedItem

4 liteCollectionContainer defaultActionRequested editEmployeeButton click

5 editEmployeeButton clicked Rb11EmployeeDetails new View

6 detailsView topLevelTransactionCo refreshButton click mmitted

7 Window openedWidget refreshButton click

8 Rb11EmployeeDetailsView instance detailsView self

9 editEmployeeButton clicked detailsView openWidget

To be able to open detail views on more than one employee at a time, we use a factory to create the Rb11EmployeeDetailsViews. We pass the selected employee data object to the factory, so that, after the view is created, the data object variable promoted from there is correctly initialized automatically.

When you first run this part, you are likely to find that the container does not show the employee information. Because the generated data object access methods look up the attribute values in dictionaries with the attribute names as keys. The lookup is done with the all upper-case database column names, but the keys in the dictionaries are the mixed-case column names. The first idea for a workaround would be to enter all upper-case physical column names in the Schema Browser, but that causes an error during service code generation. A way to make it work is to patch the fieldNames class method, Rb11EmployeeRb11EmployeeDataObject, in the data object class, so that the strings are all upper case there.

344 Using VisualAge Smalltalk ObjectExtender 17.3 Create a View with a BLOB Figure 209 on page 347 shows the Rb11EmployeeDetailView.

The view contains a form to display the employee’s picture stored in the database and a user interface to load a picture from a file for the displayed employee and store it in the database.

Two things are of particular interest in the EmployeeDetailsView part that is opened by the Employee Lite Collection window: • We have a transacted variable for an employee business object, which is initialized from businessObject of the data object variable. The data object variable is promoted and set from outside. Calling the getBusinessObject method on a data object causes the real business object to be built, if necessary by issuing the respective SQL queries behind the scenes. In our case, the retrieval of the entire employee business object including the picture is triggered (Connections 7 and 8 in Table 82), causing the expected short delay. We cannot have an attribute-to-attribute connection from the data object to the business object, because that would provoke an immediate load of the complete business object as soon as the data object is loaded. • The retrieved data is then displayed in the corresponding fields. The picture is handled with the help of the GifImage part we defined in Chapter 16, “Binary Large Objects” on page 337. The image is automatically converted from the string in the picture attribute of Employee to the internal GIF representation by contentsFromString: and asString in GifImage, the methods behind the attribute-to-attribute connection, Connection 4. Because of Connection 5 to the Form part, the GifImage knows where to display itself. The other direction of conversion is demonstrated by Connection 9 from the Load image from file button to the GifImage. As soon as the image is loaded from the file by contentsFromGifFile: in GifImage, the image is at once converted to a string and the string is stored in the Employee’s picture attribute.

Table 82 provides the full documentation for the connections in the EmployeeDetailsView part.

Lite Collections 345 Table 82. Connections of Rb11EmployeeDetailsView Source (S) Feature of S Target (T) Feature of T

1 employeeBusinessObject empNo empNoField object 2 employeeBusinessObject firstName firstNameField object 3 employeeBusinessObject lastName lastNameField object 4 employeeBusinessObject picture gifImage imageAsString

5 Form self gifImage displayWindow

6 Rb11EmployeeDetailsView openedWidget sharedTransaction beginChild

7 Rb11EmployeeDetailsView openedWidget employeeBusinessO self bject

8 (Rb11EmployeeDetailsView,ope value employeeDataObject businessObject nedWidget --> employeeBusinessObject,self)

9 LoadImageFromFileButton clicked gifImage contentsFromGifFile

10 (LoadImageFromFileButton,click path pathField object ed --> gifImage,contentsFromGifFile)

11 OKButton clicked topLevelTransaction commit

12 OKButton clicked Window closeWidget

13 (Rb11EmployeeDetailsView,ope normalResult topLevelTransaction self nedWidget --> sharedTransaction,beginChild)

14 topLevelTransaction committedOrR sharedTransaction beginChild olledback

15 (topLevelTransaction,committed normalResult topLevelTransaction self OrRolledback --> sharedTransaction,beginChild)

16 topLevelTransaction self employeeBusinessO transaction bject

17 CancelButton clicked topLevelTransaction rollback

18 CancelButton clicked Window closeWidget

346 Using VisualAge Smalltalk ObjectExtender Figure 209. Rb11EmployeeDetailsView

Lite Collections 347 348 Using VisualAge Smalltalk ObjectExtender Appendixes

© Copyright IBM Corp. 1999 349 350 Using VisualAge Smalltalk ObjectExtender Appendix A. Overview of the Examples in This Book

Table 83 lists all examples used in this book. The example names are used as the project names to build the component names required and created by ObjectExtender. The naming conventions for the components are listed in Table 84 on page 353. Table 83. Overview of Sample Applications in This Book Example/Project Name Description

Rb20Employee Employee Example Top-Down (nothing given)

Rb21Employee Employee Example Bottom-Up(legacy database)

Rb30 EmpDept Employee and Department Example Top-Down

Rb31EmpDept Employee and Department Example Bottom-Up

Rb30EmpDeptRb31EmpDept Employee and Department ExampleOutside-In

Rb50Library Library Example: M-to-M-Relationships

Rb61Library Library Example: Inheritance - Single Table Mapping

Rb62Library Library Example: Inheritance - Multiple Table Mapping

Rb80Library Library Example: Nested Transactions

Rb90Library Library Example: Concurrent Transactions

Rb10GifImage Part for GIF Images

Rb11Employee Employee Example with Pictures

© Copyright IBM Corp. 1999 351 352 Using VisualAge Smalltalk ObjectExtender Appendix B. Naming Conventions

Table 84 shows the naming conventions used in this book for the components created and required by ObjectExtender. The starting point is always the project name, to which various suffixes are added. The rightmost column shows the resulting names of the classes generated by ObjectExtender. The project names are derived from the example names, which are listed in Table 83 on page 351. Table 84. Naming Conventions Element Name Applications Classes

D Model Project Project+ModelApp Project+Model [Metamodel] [Project+MetamodelApp] [Project+Metamodel]

R Real Class Project+App Class [Model] [Project+ModelApp] Class+Home Class+Key Class+To+Role+Relationship

D Map Project Project+MapApp Project+Map (Model+ (Model+Schema+MapApp) (Model+Schema+Map) Schema)

D Schema Project Project+SchemaApp Project+Schema

R Services Project+ServicesApp Project+Services (Model+Schema+ServicesApp) (Model+Schema+Services)

RDatastore Project+DataStore (Model+Schema+DataStore)

R Helpers Project+Class+DataObject Project+Class+Extractor Project+Class+Injector Project+Class+QueryPool Project+Class+ServiceObject (Model+Schema+DataObject) (Model+Schema+Extractor) (Model+Schema+Injector) (Model+Schema+QueryPool) (Model+Schema+ServiceObject)

- (when generated) - D=Development-time-only elements—have a name and an application and class - R=Runtime elements—have no name, just an application and class - [Alternative], when the Model name suffix is used to differentiate the classes in a Model-View-Controller (MVC) context. See also Note below this table.

© Copyright IBM Corp. 1999 353 Note: For situations—not unusual in Smalltalk—where the project name is the same as the class name: For some names ObjectExtender suggests defaults. For the application where the generated model class code is stored, for example, ObjectExtender suggests the name built from the model name, for example, Rb20Employee, with ModelApp as suffix. Now, if you name your application where you store the model—actually the model definition—with suffix ModelApp, this very application will then by default also be the storage of the generated code. But at runtime the model definitions and all of the ObjectExtender development time prerequisites, such as the classes in VapMetadataApp, are nor longer needed. Therefore, if you want to use ObjectExtender’s default application naming (and the Model View Controller (MVC) kind of application name suffixing) but do not want the metadata overhead at runtime (or packaging), we suggest that you use the alternative naming for the model definitions, such as MetamodelApp and Metamodel for the storage application and class, respectively. Otherwise, you have to change the Model Code Generation Options before the first generation by changing the model application name suffix from ModelApp to just App (see for example, 5.2.1, “Set Model Code Generation Options” on page 54).

ObjectExtender sets automatically the VapMetadataApp as metadata prerequisite for all metadata storage applications. For lean packaging and runtime systems we suggest to store the generated model and services code never together in the same applications as the model, schema, and map.

Whenever you get an error message like this: Class creation failure: Rb20EmployeeDataStore cannot change its superclass to AbtDataStore because it is not visible to Rb20EmployeeServicesApp.

you mixed up some names by reusing them. This may happen, for example when you keep—not unload—the services application generated for the local image data store and try to generate the services for the database from a map with the same name as the model.

354 Using VisualAge Smalltalk ObjectExtender Appendix C. Downloadables, ConfigMaps, Applications

This appendix supplies information about the downloables files (Table 85 on page 356), selective configuration maps (Table 86 on page 357 and Table 87 on page 357), and two configuration maps with all applications and classes (Table 88 on page 358 through Table 91 on page 360).

© Copyright IBM Corp. 1999 355 Table 85. Downloadable File RBOE.zip and Its Contents No. File(s) Description (Contents [Size])

0 SG245258.zip The actual downladable zip file containing all zipped files. (zip)

1 aaRead.txt Information about the contents of the SG245258.zip file, including late (text/workspace) breaking news.

2 RBOED.dat Contains all configuration maps and applications and classes which are built (libary) in this book, some other samples, and some ObjectExtender supporting stuff (enabled browser, enhanced physical name building support,...). Contains: Rb00ConfigMap for full import/export; C01, C02, C03, C04, C05, C98 and C99 config maps for application oriented import/export and loading.

3 RBOEZD.dat Contains the configuration map with changes to ObjectExtender. Import and (libary) browse changes first, then load selectively on demand... (Rb01ConfigMap)

4 Rb1to1OW.wsp Workspace with test scripts, zap, and transcript output of the detailed (text/workspace) 1:1/non-required sample in theRb1to1App

5 Rb1MSROW.wsp Workspace with test scripts, zap, and transcript output of the detailed (text/workspace) 1:Many/Self referencing Required sample in the Rb1toMselfReqApp.

6 Rb20DBCR.txt Report of the DB Rb20Employee table and key creation (text)

7 Rb20EmpL.lis Save of a local image data store containing one Rb20Employee instance (local image datastore)

8 Rb20HDLW.wsp Workspace to run the Rb20... sample with scripts headless (text/workspace)

9 Rb20LISW.wsp Workspace script sequence to verify the local image schema and data store (text/workspace ) generation

10 Rb50TstW.wsp Workspace to test M-to-M associations. (text/workspace )

11 Rb6XTstW.wsp Workspace to test inheritance. (text/workspace )

12 Rb70TstW.wsp Workspace to test selective queries. (text/workspace )

13 Rb90TstW.wsp Workspace to test transaction concurrence. (text/workspace )

14 RbBlob(XX).jpg Gif BLOBs to test BLOBs and Lite Collections. (gif BLOBs) (XX)= B2[416KB], BK[31KB], DS[31KB], MM[36KB], PB[30KB], UB[33KB]

356 Using VisualAge Smalltalk ObjectExtender Table 86. Application Configuration Maps ID ConfigMap Name (Prerequisites) Description [Applications ( Prereqs) | Applications (Prereqs) ]

C01 Rb20EmployeeLISConfigMap (LC99) Everything to run the Employee - Top-Down sample with a local image data store—requires no database—and with a GUI. It is a nice sample for a stand-alone application. Load and run It! Note: LC99 means that C99 must be loaded first.

A01 Rb20EmployeeModelApp A03 Rb20EmployeeServicesApp (A02) A02 Rb20EmployeeApp A04 Rb20EmployeeViewLISApp (A02 A03 A99)

C02 Rb20EmployeeDBConfigMap (LC99). Everything to run the Employee - Top-Down sample with the database image store—requires DB2, GUI, and Transaction samples.

A01 Rb20EmployeeModelApp A06 Rb20EmployeeSchemaApp A02 Rb20EmployeeApp A07 Rb20EmployeeRb20employeeServicesApp (A02) A05 Rb20EmployeeMapApp A08 Rb20EmployeeViewApp (A02 A07 A98 A99)

C03 Rb21EmployeeDBConfigMap (LC99) Everyting to run the Employee - Bottom-Up sample with the database image store—requires DB2—and with a GUI.

A09 Rb21EmployeeModelApp A12 Rb21EmployeeSchemaApp A10 Rb21EmployeeApp A13 Rb21EmployeeRb20employeeServicesApp A11 Rb21EmployeeMapApp A14 Rb21EmployeeViewApp (A10 A99)

C04 Rb30EmpDeptDBConfigMap (LC99, LC98). Everything to run the Employee and Department - Top-Down sample with the database image store—requires DB2, GUI, and drag-&-drop support..

A15 Rb30EmpDeptModelApp A19 Rb30EmpDeptSchemaApp A16 Rb30EmpDeptApp A20 Rb30EmpDeptRb30empDeptServicesApp (A16) A18 Rb30EmpDeptMapApp A21 Rb30EmpDeptViewApp (A16 A95 A96 A97 A99)

C05 Rb31EmpDeptDBConfigMap (LC99, LC98) Everyting to run the Employee and Department - Bootom-Up sample with the database image store—requires DB2—and with a GUI.

A22 Rb31EmpDeptModelApp A25 Rb31EmpDeptSchemaApp A23 Rb31EmpDeptApp A26 Rb31EmpDeptRb31empdeptServicesApp (A23) A24 Rb31EmpDeptMapApp A27 Rb31EmpDeptViewApp (A23 A95 A99)

(XX##): C##=ConfigMap, A##=Application, LX##=X## must be loaded 1st, RC##=Required ConfigMap

Table 87. Application Utility Configuration Maps ID ConfigMap Name (Prerequisites) Description [Applications ( Prereqs) | Applications (Prereqs) ]

C98 Rb99UtilityViewsConfigMap contains reusable non-visual parts

A96 Rb99UtilityViewsApp A97 Rb99OeUtilityViewApp

C99 Rb99UtilityPartsConfigMap contains reusable visual parts

A99 Rb99UtilityPartsApp A98 Rb99EnhancedBusinessTransactionApp A95 Rb99VasXOeApp A94 Rb99EnhancedRelationshipApp

Downloadables, ConfigMaps, Applications 357 Table 88. Import/Export Rb00ConfigMap with All Application (Part 1 of 3) A## Application (A## Prerequired Application) - Comment - in Import/Export Rb00ConfigMap

----- Employee Forward/Top-Down... A01 Rb20EmployeeModelApp A02 Rb20EmployeeApp A03 Rb20EmployeeServicesApp (A02) - Local Image Data Store A04 Rb20EmployeeLISViewApp (A03 A99) - Local Image Data Store A05 Rb20EmployeeMapApp A06 Rb20EmployeeSchemaApp A07 Rb20EmployeeRb20employeeServicesApp (A02) A08 Rb20EmployeeViewApp (A02 A07 A99 A98) - Transactions

----- Employee Backward/Bottom-Up... A09 Rb21EmployeeModelApp A10 Rb21EmployeeApp A11 Rb21EmployeeMapApp A12 Rb21EmployeeSchemaApp A13 Rb21EmployeeRb20employeeServicesApp (A10) A14 Rb21EmployeeViewApp (A13 A99)

----- Employee and Department Forward/Top-Down... A15 Rb30EmpDeptModelApp A16 Rb30EmpDeptApp A17 Rb30EmpDeptServicesApp (A16) - Local Image Data Store A18 Rb30EmpDeptMapApp A19 Rb30EmpDeptSchemaApp A20 Rb30EmpDeptRb30empdeptServicesApp (A16) A21 Rb30EmpDeptViewApp (A16 A95 A96 A97 A99) - Drag and Drop Departments and Employees

----- Employee and Department Backward/Bottom-Up... A22 Rb31EmpDeptModelApp A23 Rb31EmpDeptApp A24 Rb31EmpDeptMapApp A25 Rb31EmpDeptSchemaApp A26 Rb31EmpDeptRb31empdeptServicesApp (A23) A27 Rb31EmpDeptViewApp (A23 A99) - Department Management View

----- Employee and Department Outsid-In (map model and schema)... A28 Rb30EmpDeptRb31EmpDeptMapApp (A15 A25) A29 Rb30EmpDeptRb31EmpDeptServicesApp (A16) A30 Rb30EmpDeptRb31EmpDeptViewApp (A16)

----- Self-Referencing One-To-Many with Link-Object and Link-Table (1:m-1:1 like bill of material) A31 Rb40CategoryModelApp A32 Rb40CategoryApp - model code and views A33 Rb40CategoryMapApp A34 Rb40CategorySchemaApp A35 Rb40CategoryServicesApp (A31)

358 Using VisualAge Smalltalk ObjectExtender Table 89. Import/Export Rb00ConfigMap with All Application (Part 2 of 3) A## Application (A## Prerequired Application) - Comment - in Import/Export Rb00ConfigMap

----- Many-to-Many Associations/Relationships A36 Rb50LibraryModelApp - metamodel and model code A37 Rb50LibraryMapApp A38 Rb50LibrarySchemaApp A39 Rb50LibraryServicesApp (A36) A40 Rb50LibraryViewApp (A36)

----- Inheritance A41 Rb61LibraryModelApp - metamodel and model code A42 Rb61LibraryMapApp A43 Rb61LibrarySchemaApp A44 Rb61LibraryServicesApp (A41) A45 Rb62LibraryModelApp - metamodel and model code A46 Rb62LibraryMapApp A47 Rb62LibrarySchemaApp A48 Rb62LibraryServicesApp (A45)

----- Selective Queries - more than XyzHome allInstances select: [ each: | each parse... ] A49 Rb70LibraryModelApp - metamodel and model code A50 Rb70LibraryMapApp A51 Rb70LibrarySchemaApp A52 Rb70LibraryServicesApp A53 Rb70LibraryXSelSQLApp (A52) - additional queries supported (SQLs in query pool, new services)

----- Nested Transactions A54 Rb80LibraryModelApp - metamodel and model code A55 Rb80LibraryApp (A48) - views and utility parts A56 Rb80LibraryMapApp A57 Rb80LibrarySchemaApp A58 Rb80LibraryServicesApp (A54)

----- Concurrent Transactions A59 Rb90LibraryModelApp - metamodel and model code, predicate definition A60 Rb90LibraryMapApp A61 Rb90LibrarySchemaApp A62Rb90LibraryServicesApp (A59) - modified queries (SQL with current timestamp extensions) A63 Rb90LibraryViewApp (A59) - collision detection in image and backend data store

----- GIF Image / BLOB Reusable Part - (gif from string and file, string from gif) A64 Rb10GifImageApp

----- Lite Collection and BLOB A65 Rb11EmployeeModelApp - metamodel and model code A66 Rb11EmployeeMapApp A67 Rb11EmployeeSchemaApp A68 Rb11EmployeeServicesApp (A65) A69 Rb11EmployeeViewApp (A65) - list view with lite collectin, detail view with BLOB

Downloadables, ConfigMaps, Applications 359 Table 90. Import/Export Rb00ConfigMap with All Application (Part 3 of 3) A## Application (A## Prerequired Application) - Comment - in Import/Export Rb00ConfigMap

----- Other Samples A71 Rb1to1App - one-to-one detailed; contains model, schema, map, model code, and services all in one A72 Rb1toMselfReqApp on-to-many detailed; contains mode, schema, map, model code, and services A73 Rb0DB2SampleMetaDataApp - metamodel, map, schema (employee/department forward) A74 Rb0DB2SampleModelApp - model code A75 Rb0DB2SampleServicesApp A76 Rb0DB2SampleViewApp (A74) - A77 Rb0DB2SampleImportMetaDataApp - metamodel,map,schema (employee/department bottom-up) A78 Rb0DB2SampleImportModelApp - model code A79 Rb0DB2SampleImportServicesApp A80 Rb0DB2SampleImportViewApp (A78) - tree and assign view (employee/manager--->department) A81 Rb1DB2SampleMetaDataApp - metamodel, map, schema (employee top-down) A82 Rb1DB2SampleModelApp - model code A83 Rb1DB2SampleServicesApp A84 Rb1DB2SampleViewApp (A82)

----- ObjectExtender Helpers A88 VapDatabasePhysicalRulesXApp - enhanced physical name building support A89 VapInspectorUtilitiesApp V0.2 ("100% unintrusive" business object inspector)

----- Other Helpers A90 DfksListConnectionsApp - Lists connections of VisualAge Smalltalk parts in text/delimiter format for cut/copy/past into parts documentation.

----- Application Helpers A94 Rb99EnhancedRelationshipApp - inserted root class for non-required relationship support (^nil) A95 Rb99VasXOeApp - #referenceString for UndefinedObject A96 Rb99UtilityViewsApp - reusable SingleReferenceForm with drag-drop support A97 Rb99OeUtilityViewApp - form to display basicHash as transaction name for auto ID / easy tracking A98 Rb99EnhancedBusinessTransactionApp A99 Rb99UtilityPartsApp - Comparators, Concatenators, Extractor (by selector for sending visually any message (method selector), Copier,...

Table 91. Import/Export Rb01ConfigMap with ObjectExtender Modifications) X## Application - Comment - in Import/Export Rb01ConfigMap LOAD with CAUTION!

X01 VapModelGenerationApp V4.5a mm 0.01 - one-to-one/many nil-able associations/relationships X02 VapSchemaBrowserApp V4.5a mm 0.01 - foreign key relationship consistency check softener X03 VapTransactions V 4.5a mm 0.01. - for #hasChanges support X04 CollisionManagement V 4.5. - unchanged sub-app of X03 X05 VapViewPartsApp V 4.5a mm 0.01. - for #hasChanges support X99 VapInspectorUtilitiesApp V0.1 ("not 100% unintrusive" business object inspector)

360 Using VisualAge Smalltalk ObjectExtender Appendix D. Workspaces and System Transcript Output

See also Appendix C, “Downloadables, ConfigMaps, Applications” on page 355.

D.1 Rb20HDL.wsp - Headless Test Workspace

" Rb20HDLW.wsp " " Rb20 Employee HeaDLess test Workspace " " **************************************************** " "NOTE: setup the 'Detailed Trace' in the ObjectExtender Tools"

" ########## sample output with loacal image datastore ########### "

"******** stepwise ********"

self halt. Rb20EmployeeHome singleton allInstances "--->" '>>>executing allInstances'

Rb20EmployeeDataStore singleton activate "--->"

Rb20EmployeeHome singleton allInstances "--->" '>>>executing allInstances'

Transaction begin. Rb20EmployeeHome singleton create empNo: 'A12345'; irstNme: 'Fred'; midInit: 'Q'; lastName: 'Smith'; edLevel: 18. Transaction current commit. "--->" *** Resuming ->{0}( > 14226) *Committing* {1}( > 14226 > 2083) '>>>executing insert' >>>>committing

Rb20EmployeeHome singleton allInstances "--->" '>>>executing allInstances'

" ******* inspect ******** "

"01" | employee | "02" System vapReset. "03" Rb20EmployeeDataStore singleton activate. "04" Transaction begin. "05" employee := Rb20EmployeeHome singleton allInstances first. "06" (DbgInspector on: employee) open; halt; close. "07" (DbgInspector on: employee lastName) open; halt; close.

© Copyright IBM Corp. 1999 361 "08" (DbgInspector on: employee) open; halt; close. "09" Transaction current commit "--->" *** Resuming ->{0}( > 19631) '>>>executing allInstances' *Committing* {1}( > 19631 > 19018) >>>>committing

"******** allInstances ********"

"01" | employee | "02" System vapReset. "03" Rb20EmployeeDataStore singleton activate. "04" Transaction begin. "05" (DbgInspector on: Rb20Employee allInstances) open; halt; close. "06" employee := Rb20EmployeeHome singleton allInstances first. "07" employee firstNme. "08" (DbgInspector on: Rb20Employee allInstances) open; halt; close. "09" employee firstNme: 'Mike'. "10" (DbgInspector on: Rb20Employee allInstances) open; halt; close. "11" Transaction current commit. "12" employee := nil. "13" (DbgInspector on: Rb20Employee allInstances) open; halt; close "--->" *** Resuming ->{0}(Shared)( > 19871) *** Resuming ->{0}( > 20463) '>>>executing allInstances' *Committing* {1}( > 20463 > 19871) >>>>committing

" ########## sample output with DB2 datastore ########### "

"******** stepwise********"

self halt. Rb20EmployeeHome singleton allInstances "--->" *** Resuming ->{0}(Shared)( > 17871) '>>>executing allInstances' >>>executing: SELECT T1.bonus, T1.workDept, T1.firstNme, T1.empNo, T1.comm, T1.hireDate, T1.job, T1.midInit, T1.edLevel, T1.lastName, T1.phoneNo, T1.salary, T1.sex, T1.birthDate FROM USERID.Rb20Employee T1 >>>executed without error

Rb20EmployeeDataStore singleton activate "--->"

Rb20EmployeeHome singleton allInstances "--->" *** Resuming ->{0}(Shared)( > 17871) '>>>executing allInstances' >>>executing: SELECT T1.bonus, T1.workDept, T1.firstNme, T1.empNo, T1.comm, T1.hireDate, T1.job, T1.midInit, T1.edLevel, T1.lastName, T1.phoneNo, T1.salary, T1.sex, T1.birthDate FROM USERID.Rb20Employee T1

362 Using VisualAge Smalltalk ObjectExtender >>>executed without error

Transaction begin. Rb20EmployeeHome singleton create empNo: 'A12345'; irstNme: 'Fred'; midInit: 'Q'; lastName: 'Smith'; edLevel: 18. Transaction current commit. "--->" *** Resuming ->{0}( > 4753) *Committing* {1}( > 4753 > 17871) '>>>executing insert' >>>executing: INSERT INTO USERID.Rb20Employee ( empNo, bonus, workDept, firstNme, comm, hireDate, job, midInit, edLevel, lastName, phoneNo, salary, sex, birthDate ) VALUES ( 'A12345', NULL, NULL, 'Fred', NULL, NULL, NULL, 'Q', 18, 'Smith', NULL, NULL, NULL, NULL ) >>>executed without error >>>>commiting

Rb20EmployeeHome singleton allInstances "--->" '>>>executing allInstances' >>>executing: SELECT T1.bonus, T1.workDept, T1.firstNme, T1.empNo, T1.comm, T1.hireDate, T1.job, T1.midInit, T1.edLevel, T1.lastName, T1.phoneNo, T1.salary, T1.sex, T1.birthDate FROM USERID.Rb20Employee T1 >>>executed without error AbtIbmCliRow of type: AbtDatabaseDecimalField(BONUS(11)) Data: nil AbtIbmCliFixedCharField(WORKDEPT(3)) Data: nil AbtOdbcVarCharField(FIRSTNME(12)) Data: 'Fred' AbtIbmCliFixedCharField(EMPNO(6)) Data: 'A12345' AbtDatabaseDecimalField(COMM(11)) Data: nil AbtDatabaseDateField(HIREDATE) Data: nil AbtIbmCliFixedCharField(JOB(8)) Data: nil AbtIbmCliFixedCharField(MIDINIT(1)) Data: $Q AbtDatabaseShortIntegerField(EDLEVEL) Data: 18 AbtOdbcVarCharField(LASTNAME(15)) Data: 'Smith' AbtIbmCliFixedCharField(PHONENO(4)) Data: nil AbtDatabaseDecimalField(SALARY(11)) Data: nil AbtIbmCliFixedCharField(SEX(1)) Data: nil AbtDatabaseDateField(BIRTHDATE) Data: nil

" ******* inspect ******** "

"01" | employee | "02" System vapReset. "03" Rb20EmployeeDataStore singleton activate. "04" Transaction begin. "05" employee := Rb20EmployeeHome singleton allInstances first. "06" (DbgInspector on: employee) open; halt; close. "07" (DbgInspector on: employee lastName) open; halt; close. "08" (DbgInspector on: employee) open; halt; close. "09" Transaction current commit "--->"

Workspaces and System Transcript Output 363 *** Resuming ->{0}(Shared)( > 22888) *** Resuming ->{0}( > 23512) '>>>executing allInstances' >>>executing: SELECT T1.bonus, T1.workDept, T1.firstNme, T1.empNo, T1.comm, T1.hireDate, T1.job, T1.midInit, T1.edLevel, T1.lastName, T1.phoneNo, T1.salary, T1.sex, T1.birthDate FROM USERID.Rb20Employee T1 >>>executed without error AbtIbmCliRow of type: AbtDatabaseDecimalField(BONUS(11)) Data: nil AbtIbmCliFixedCharField(WORKDEPT(3)) Data: nil AbtOdbcVarCharField(FIRSTNME(12)) Data: 'Fred' AbtIbmCliFixedCharField(EMPNO(6)) Data: 'A12345' AbtDatabaseDecimalField(COMM(11)) Data: nil AbtDatabaseDateField(HIREDATE) Data: nil AbtIbmCliFixedCharField(JOB(8)) Data: nil AbtIbmCliFixedCharField(MIDINIT(1)) Data: $Q AbtDatabaseShortIntegerField(EDLEVEL) Data: 18 AbtOdbcVarCharField(LASTNAME(15)) Data: 'Smith' AbtIbmCliFixedCharField(PHONENO(4)) Data: nil AbtDatabaseDecimalField(SALARY(11)) Data: nil AbtIbmCliFixedCharField(SEX(1)) Data: nil AbtDatabaseDateField(BIRTHDATE) Data: nil *Committing* {1}( > 23512 > 22888) >>>>commiting

"******** allInstances ********"

"01" | employee | "02" System vapReset. "03" Rb20EmployeeDataStore singleton activate. "04" Transaction begin. "05" (DbgInspector on: Rb20Employee allInstances) open; halt; close. "06" employee := Rb20EmployeeHome singleton allInstances first. "07" employee firstNme. "08" (DbgInspector on: Rb20Employee allInstances) open; halt; close. "09" employee firstNme: 'Mike'. "10" (DbgInspector on: Rb20Employee allInstances) open; halt; close. "11" Transaction current commit. "12" employee := nil. "13" (DbgInspector on: Rb20Employee allInstances) open; halt; close "--->" *** Resuming ->{0}(Shared)( > 25115) *** Resuming ->{0}( > 25719) '>>>executing allInstances' >>>executing: SELECT T1.bonus, T1.workDept, T1.firstNme, T1.empNo, T1.comm, T1.hireDate, T1.job, T1.midInit, T1.edLevel, T1.lastName, T1.phoneNo, T1.salary, T1.sex, T1.birthDate FROM USERID.Rb20Employee T1 >>>executed without error AbtIbmCliRow of type: AbtDatabaseDecimalField(BONUS(11)) Data: nil AbtIbmCliFixedCharField(WORKDEPT(3)) Data: nil

364 Using VisualAge Smalltalk ObjectExtender AbtOdbcVarCharField(FIRSTNME(12)) Data: 'Fred' AbtIbmCliFixedCharField(EMPNO(6)) Data: 'A12345' AbtDatabaseDecimalField(COMM(11)) Data: nil AbtDatabaseDateField(HIREDATE) Data: nil AbtIbmCliFixedCharField(JOB(8)) Data: nil AbtIbmCliFixedCharField(MIDINIT(1)) Data: $Q AbtDatabaseShortIntegerField(EDLEVEL) Data: 18 AbtOdbcVarCharField(LASTNAME(15)) Data: 'Smith' AbtIbmCliFixedCharField(PHONENO(4)) Data: nil AbtDatabaseDecimalField(SALARY(11)) Data: nil AbtIbmCliFixedCharField(SEX(1)) Data: nil AbtDatabaseDateField(BIRTHDATE) Data: nil *Committing* {1}( > 25719 > 25115) '>>>executing update' >>>executing: UPDATE USERID.Rb20Employee SET bonus = NULL, workDept = NULL, firstNme = 'Mike', comm = NULL, hireDate = NULL, job = NULL, midInit = 'Q', edLevel = 18, lastName = 'Smith', phoneNo = NULL, salary = NULL, sex = NULL, birthDate = NULL WHERE empNo = 'A12345' >>>executed without error >>>>commiting

D.2 Rb30DBCR.txt - DB Table Creation Report

" Rb30DBCR.txt " " Rb30 Employee DB Creation Report " " ****************************************** " (copied from the System Transcript) >>>>setting auto commit on *** Resuming ->{0}(Shared)( > 26866) *** Resuming ->{0}( > 27615)

>>>executing: DROP TABLE USERID.Rb30Employee >>>executed with error: [SQLSTATE=S0002 - [IBM][CLI Driver][DB2/NT] SQL0204N "USERID.RB30EMPLOYEE" is an undefined name. SQLSTATE=42704 [Native Error=-204]]

>>>executing: DROP TABLE USERID.Rb30Department >>>executed with error: [SQLSTATE=S0002 - [IBM][CLI Driver][DB2/NT] SQL0204N "USERID.RB30DEPARTMENT" is an undefined name. SQLSTATE=42704 [Native Error=-204]]

*Committing* {1}( > 27615 > 26866) *** Resuming ->{0}( > 30232) >>>executing: CREATE TABLE USERID.Rb30Department ( deptName VARCHAR(29) NOT NULL, location CHARacter(16), deptNo CHARacter(3) NOT NULL, mgrNo CHARacter(6), admrDept CHARacter(3)) >>>executed without error

Workspaces and System Transcript Output 365 >>>executing: CREATE TABLE USERID.Rb30Employee ( firstNme VARCHAR(12) NOT NULL, midInit CHARacter(1) NOT NULL, hireDate DATE, salary DECimal(9,2), edLevel SMALLINT NOT NULL, sex CHARacter(1), comm DECimal(9,2), phoneNo CHARacter(4), job CHARacter(8), bonus DECimal(9,2), birthDate DATE, lastName VARCHAR(15) NOT NULL, empNo CHARacter(6) NOT NULL, workDept CHARacter(3)) >>>executed without error *Committing* {1}( > 30232 > 26866) *** Resuming ->{0}( > 10157) >>>executing: ALTER TABLE USERID.Rb30Employee ADD PRIMARY KEY ( empNo) >>>executed without error >>>executing: ALTER TABLE USERID.Rb30Department ADD PRIMARY KEY ( deptNo) >>>executed without error >>>executing: ALTER TABLE USERID.Rb30Employee ADD CONSTRAINT employe_workDepart FOREIGN KEY (workDept) REFERENCES USERID.Rb30Department >>>executed without error >>>executing: ALTER TABLE USERID.Rb30Department ADD CONSTRAINT managedDepar_manag FOREIGN KEY (mgrNo) REFERENCES USERID.Rb30Employee >>>executed without error >>>executing: ALTER TABLE USERID.Rb30Department ADD CONSTRAINT depart_administrat FOREIGN KEY (admrDept) REFERENCES USERID.Rb30Department >>>executed without error *Committing* {1}( > 10157 > 26866) >>>>setting auto commit off

D.3 Rb30ObjW.wsp - System Transcript (Output Only) " Rb30ObjW.wsp " " Rb30 Employee and Department Object setup and navigate Workspace " " ************************************************************************************** " "(in Tools set: Basic Trace, Enable Inspectors)"

...... System Transcript output only

366 Using VisualAge Smalltalk ObjectExtender " ======BEG TRANSCRIPT OUTPUT ======" '' '01 Perform a global reset of ObjectExtender' '' '02 Activate the Rb20EmployeeHome''s datastore.' '' '03 Add TOP department.' ======a Rb30Department(nil) ------a Rb30Department(nil) '>>>executing insert' >>>executing: INSERT INTO USERID.Rb30Department ( deptNo, deptName, location, Rb30Departme_deptN, Rb30Employee_empNo ) VALUES ( 'TOP', 'TOP Department', 'TOP location', 'TOP', NULL ) >>>executed without error >>>>commiting '' '04 Check TOP department. NOTE: Resume in the Debugger.' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'TOP' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Departme_deptN = 'TOP' >>>executed without error 'Shows TOP with TOP in departments and as administrativeDepartment, and nil as manager' '* self: a Rb30Department(''TOP'') * administrativeDepartment: a Rb30Department(''TOP'') * departments: OrderedCollection(''TOP'' ) * manager: nil' '' '05 Add S1A, S1B, and S2A departments.' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'TOP' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Departme_deptN = 'TOP' >>>executed without error ======a Rb30Department(nil) a Rb30Department(nil) a Rb30Department(nil) ------a Rb30Department(nil)

Workspaces and System Transcript Output 367 a Rb30Department(nil) a Rb30Department(nil) '>>>executing insert' >>>executing: INSERT INTO USERID.Rb30Department ( deptNo, deptName, location, Rb30Departme_deptN, Rb30Employee_empNo ) VALUES ( 'S1A', 'S1A Department', 'S1A location', 'TOP', NULL ) >>>executed without error '>>>executing insert' >>>executing: INSERT INTO USERID.Rb30Department ( deptNo, deptName, location, Rb30Departme_deptN, Rb30Employee_empNo ) VALUES ( 'S1B', 'S1B Department', 'S1B location', 'TOP', NULL ) >>>executed without error '>>>executing insert' >>>executing: INSERT INTO USERID.Rb30Department ( deptNo, deptName, location, Rb30Departme_deptN, Rb30Employee_empNo ) VALUES ( 'S2A', 'S2A Department', 'S2A location', 'S1A', NULL ) >>>executed without error >>>>commiting '' '06 Add employees & managers except one to departments.' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'TOP' >>>executed without error '>>>executing retrieveEmployeesForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.Rb30Departme_deptN = 'TOP' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Departme_deptN = 'TOP' >>>executed without error '>>>executing retrieveEmployeesForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.Rb30Departme_deptN = 'S1B' >>>executed without error '>>>executing retrieveEmployeesForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.Rb30Departme_deptN = 'S1A' >>>executed without error ======a Rb30Department('TOP') a Rb30Employee(nil) a Rb30Employee(nil)

368 Using VisualAge Smalltalk ObjectExtender a Rb30Department('S1B') a Rb30Employee(nil) a Rb30Employee(nil) a Rb30Employee(nil) a Rb30Employee(nil) ------a Rb30Employee(nil) a Rb30Employee(nil) a Rb30Department('S1B') a Rb30Employee(nil) a Rb30Employee(nil) a Rb30Employee(nil) a Rb30Department('TOP') a Rb30Employee(nil) '>>>executing insert' >>>executing: INSERT INTO USERID.Rb30Employee ( empNo, firstNme, midInit, hireDate, salary, edLevel, sex, comm, phoneNo, job, bonus, birthDate, lastName, Rb30Departme_deptN ) VALUES ( 'EMP1A2', 'EMP1A2first', 'E', NULL, NULL, 1, NULL, NULL, NULL, NULL, NULL, NULL, 'EMP1A2last', 'S1A' ) >>>executed without error '>>>executing insert' >>>executing: INSERT INTO USERID.Rb30Employee ( empNo, firstNme, midInit, hireDate, salary, edLevel, sex, comm, phoneNo, job, bonus, birthDate, lastName, Rb30Departme_deptN ) VALUES ( 'EMP1B0', 'EMP1B0first', 'E', NULL, NULL, 1, NULL, NULL, NULL, NULL, NULL, NULL, 'EMP1B0last', 'S1B' ) >>>executed without error '>>>executing update' >>>executing: UPDATE USERID.Rb30Department SET deptName = 'S1B Department', location = 'S1B location ', Rb30Departme_deptN = 'TOP', Rb30Employee_empNo = 'EMP1B0' WHERE deptNo = 'S1B' >>>executed without error '>>>executing insert' >>>executing: INSERT INTO USERID.Rb30Employee ( empNo, firstNme, midInit, hireDate, salary, edLevel, sex, comm, phoneNo, job, bonus, birthDate, lastName, Rb30Departme_deptN ) VALUES ( 'EMP1B1', 'EMP1B1first', 'E', NULL, NULL, 1, NULL, NULL, NULL, NULL, NULL, NULL, 'EMP1B1last', 'S1B' ) >>>executed without error '>>>executing insert' >>>executing: INSERT INTO USERID.Rb30Employee ( empNo, firstNme, midInit, hireDate, salary, edLevel, sex, comm, phoneNo, job, bonus, birthDate, lastName, Rb30Departme_deptN ) VALUES ( 'EMP1A1', 'EMP1A1first', 'E', NULL, NULL, 1, NULL, NULL, NULL, NULL, NULL, NULL, 'EMP1A1last', 'S1A' ) >>>executed without error '>>>executing insert' >>>executing: INSERT INTO USERID.Rb30Employee ( empNo, firstNme, midInit, hireDate, salary, edLevel, sex, comm, phoneNo, job, bonus, birthDate, lastName, Rb30Departme_deptN ) VALUES ( 'EMPTP0', 'EMPTP0first', 'T', NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, 'EMPTP0last', 'TOP' ) >>>executed without error '>>>executing update' >>>executing: UPDATE USERID.Rb30Department SET deptName = 'TOP Department', location =

Workspaces and System Transcript Output 369 'TOP location ', Rb30Departme_deptN = 'TOP', Rb30Employee_empNo = 'EMPTP0' WHERE deptNo = 'TOP' >>>executed without error '>>>executing insert' >>>executing: INSERT INTO USERID.Rb30Employee ( empNo, firstNme, midInit, hireDate, salary, edLevel, sex, comm, phoneNo, job, bonus, birthDate, lastName, Rb30Departme_deptN ) VALUES ( 'EMPXXX', 'EMPXXXfirst', 'E', NULL, NULL, 1, NULL, NULL, NULL, NULL, NULL, NULL, 'EMPXXXlast', NULL ) >>>executed without error >>>>commiting '' '07 Verify Object Setup - List Department Hierarchy with Contents...' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'TOP' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Departme_deptN = 'TOP' >>>executed without error '>>>executing find by key' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.empNo = 'EMPTP0' >>>executed without error '>>>executing retrieveEmployeesForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.Rb30Departme_deptN = 'TOP' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Departme_deptN = 'S1B' >>>executed without error '>>>executing find by key' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.empNo = 'EMP1B0' >>>executed without error '>>>executing retrieveEmployeesForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.Rb30Departme_deptN = 'S1B' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE

370 Using VisualAge Smalltalk ObjectExtender T1.Rb30Departme_deptN = 'S1A' >>>executed without error '>>>executing retrieveEmployeesForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.Rb30Departme_deptN = 'S1A' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Departme_deptN = 'S2A' >>>executed without error '>>>executing retrieveEmployeesForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.Rb30Departme_deptN = 'S2A' >>>executed without error ' |---a Rb30Department(''TOP'') | |---admrDept: a Rb30Department(''TOP'') | |---departments: OrderedCollection(''TOP'' ''S1A'' ''S1B'' ) | |---manager: EMPTP0 | |---employees: OrderedCollection(''EMPTP0'' ) | |---a Rb30Department(''S1B'') | | |---admrDept: a Rb30Department(''TOP'') | | |---departments: OrderedCollection() | | |---manager: EMP1B0 | | |---employees: OrderedCollection(''EMP1B0'' ''EMP1B1'' ) | |---a Rb30Department(''S1A'') | | |---admrDept: a Rb30Department(''TOP'') | | |---departments: OrderedCollection(''S2A'' ) | | |---manager: none | | |---employees: OrderedCollection(''EMP1A2'' ''EMP1A1'' ) | | |---a Rb30Department(''S2A'') | | | |---admrDept: a Rb30Department(''S1A'') | | | |---departments: OrderedCollection() | | | |---manager: none | | | |---employees: OrderedCollection()' '' 'N0 Setup Navigate (ObjectExtender general reset, dataStore activate.' '' 'N1a Find the departments of the S1A department.' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'S1A' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE

Workspaces and System Transcript Output 371 T1.Rb30Departme_deptN = 'S1A' >>>executed without error OrderedCollection('S2A' ) '' 'N1b Find the departments of the S1B department.' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'S1B' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Departme_deptN = 'S1B' >>>executed without error OrderedCollection() '' 'N1c Find the departments of the TOP department.' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'TOP' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Departme_deptN = 'TOP' >>>executed without error OrderedCollection('TOP' 'S1A' 'S1B' ) '' 'N2a Find the administr. dept. of the S1A department.' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'S1A' >>>executed without error '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'TOP' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Departme_deptN = 'TOP' >>>executed without error 'TOP' '' 'N2b Find the administr. dept. of the TOP department.' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'TOP' >>>executed without error '>>>executing retrieveDepartmentsForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN,

372 Using VisualAge Smalltalk ObjectExtender T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Departme_deptN = 'TOP' >>>executed without error 'TOP' '' 'N3a Find the employees of the S1B department.' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'S1B' >>>executed without error '>>>executing retrieveEmployeesForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.Rb30Departme_deptN = 'S1B' >>>executed without error OrderedCollection('EMP1B0' 'EMP1B1' ) '' 'N3b Find the employees of the S2A department.' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'S2A' >>>executed without error '>>>executing retrieveEmployeesForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.Rb30Departme_deptN = 'S2A' >>>executed without error OrderedCollection() '' 'N4a Find the work department of empl. EMP1B0 (if any).' '>>>executing find by key' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.empNo = 'EMP1B0' >>>executed without error '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'S1B' >>>executed without error '>>>executing retrieveEmployeesForRb30Department: aRb30DepartmentKey ' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.Rb30Departme_deptN = 'S1B' >>>executed without error a Rb30Department('S1B') '' 'N4b Find the work department of empl. EMPXXX (if any).' '>>>executing find by key'

Workspaces and System Transcript Output 373 >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.empNo = 'EMPXXX' >>>executed without error nil '' 'N5a Find the manager of the S1B department (if any).' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'S1B' >>>executed without error a Rb30Employee('EMP1B0') '' 'N5b Find the manager of the S1A department (if any).' '>>>executing find by key' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.deptNo = 'S1A' >>>executed without error nil '' 'N6a Find the managed department of EMP1B0 (if any).' '>>>executing find by key' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.empNo = 'EMP1B0' >>>executed without error '>>>executing retrieveManagedDepartmentForRb30Employee: aRb30EmployeeKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Employee_empNo = 'EMP1B0' >>>executed without error a Rb30Department('S1B') '' 'N6b Find the managed department of EMP1B1 (if any).' '>>>executing find by key' >>>executing: SELECT T1.firstNme, T1.midInit, T1.hireDate, T1.salary, T1.edLevel, T1.sex, T1.comm, T1.phoneNo, T1.job, T1.bonus, T1.birthDate, T1.lastName, T1.empNo, T1.Rb30Departme_deptN FROM USERID.Rb30Employee T1 WHERE T1.empNo = 'EMP1B1' >>>executed without error '>>>executing retrieveManagedDepartmentForRb30Employee: aRb30EmployeeKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Employee_empNo = 'EMP1B1' >>>executed without error /////////////////// DEBUGS - ZAP EDITING THE RECEIVER CLASS //////////////////////////// overide implementation with copy of it in the receiver class, but with change below: Rb1to1EmployeeToManagedDepartmentRelationship>>#objectForForeignKey: anFkey .... ifTrue: [^nil "self error: VapNoObjectFound"] "No object found"

Info: 42 Rb30EmployeeToManagedDepartmentRelationship overriding #objectForForeignKey: from

374 Using VisualAge Smalltalk ObjectExtender Relationship. ////////// DROP IN FRAME PRECEDING THE ERROR AND RESUEM ////////////////////// '>>>executing retrieveManagedDepartmentForRb30Employee: aRb30EmployeeKey ' >>>executing: SELECT T1.deptName, T1.location, T1.deptNo, T1.Rb30Departme_deptN, T1.Rb30Employee_empNo FROM USERID.Rb30Department T1 WHERE T1.Rb30Employee_empNo = 'EMP1B1' >>>executed without error nil ''

Workspaces and System Transcript Output 375 376 Using VisualAge Smalltalk ObjectExtender Appendix E. Library Management System

This appendix recalls the details of the Library Management System as modeled in the IBM Redbook, Using VisualAge UML Designer, SG24-4997. VisualAge (Smalltalk) UML Designer is IBM’s object-oriented modeling tool to roundtrip engineer Smalltalk and forward engineer Java code. The details about the Library Management System are: • Requirements - (Table 92 on page 378) • Concepts - (Table 93 on page 378 and Table 94 on page 379) • Actors - (Table 95 on page 380) • List of use cases - (Table 210 on page 381) • Use case descriptions with hyper link marks - (Table 96 through Table 98) • System overview - (Figure 211 on page 385) • Use case diagrams - (Figure 212 on page 385 through Figure 214) • Class diagram - (Table 215 on page 387)

© Copyright IBM Corp. 1999 377 Table 92. Library Management System: Requirements Requirement Name Details

Book Information Keep information about the books of a library. Books have to Keeping be kept as titles and physical copies - the books themselves. Each must have his own unique identification.

Customer Information Keep information about the customers of a library. Each Keeping customer must have a unique identification.

Book Loan Keep book loan information. Distinguish among available, Information Keeping borrowed, overdue, reserved, and lost.

Books on Loan Support the search for books on loan (borrowed and/or Searching overdue) of one title or of a customer.

Customer Searching Support the search for customers.

Title Searching Support the search for titles by words in the title and abstract, by author, and/or by category.

Title Reservation Support the reservation of a title for a customer for a limited time up to a specific date, if all physical copies are loaned.

End-of-Day Perform end-of-day processing, which includes updating Processing reservations and loans.

Table 93. Library Management System: Concepts 1 Concept Name Details (so far)

Available A book available for any customer has the status available.

Borrowed A book on loan - but not overdue - is in the status borrowed.

Overdue A book longer on loan than agreed is in the status overdue.

Lost A book on loan that the customer is unable to find and return.

Reserved A book available for only that customer for whom the reservation was made.

Returned A book just returned by a customer - becomes either available or reserved.

Book Loan ... the current and legal user (reader) is not the legal owner... Book borrowed from the Library for temporary use - up to the agreed return date.

Identification Unique information to uniquely identify something. For example: Social Security Number (SNN), employee number or any serial number. Abbreviations used: ID or id.

Category Contents qualification. For example: Science Fiction.

378 Using VisualAge Smalltalk ObjectExtender Table 94. Library Management System: Concepts 2 Concept Name Details (so far)

Abstract Short text about the book’s contents.

Author Name of the person(s) who wrote the book.

Bar Code Sequence of bars coding a number. Easy, fast, and reliable for machines to read.

Book Label A label with the book’s identification in standard type face and as barcode, primary category (of the title), author, and some other pertinent information to stick on the book spine and on its first page.

Customer A set of selection values used to search a specific customer in the customer file. It is Search Criteria obvious, that latest at implementation time it looks like an object.

Default Number of days a Title can be reserved. Every Title can have its own default. If not Reservation set, the systemwide default is taken. Period

Expiration Date of a Reservation: the date up to which a title reservation is kept.

Expired Book Book not returned before or at the agreed return date. An expired loan is overdue. Loan

Expired Reserved Book of a Title not picked up before or at the reservation expiration date. Reservation The reservation is removed.

ISBN International Standardized Book Number, identification defined by the publishers

Library The state of being a member of the Library organization - the borrowing part. Membership

Library A card in credit card size identifying a person or customer of the Library Membership. Membership The customer id, firstname, middle initial, lastname, member Since date, and photo Card are printed on it. Id and member Since date are also printed as bar code.

Match String A search criteria item that can have wildcards for fuzzy search. The number sign (#) represents any single character, whereas an asterisk (*) represents any character string.

Matching Title A title which matches the match strings of the title search criteria.

Publisher Name of person(s) or organization(s) publishing the book.

System The group of the acting objects building a reasonable, complete unit. If a system is called doing this or that, in fact one of its objects is doing this or that.

Title Search A set of selection values used to search a specific title in the title catalog. It is obvious, Criteria that latest at implementation time it looks like an object.

ZIP 5-digit postal delivery area. - ZIP Code - A trademark for a system to expedite the delivery of mail by assigning a 5-digit number to each postal delivery area in the U.S.

Library Management System 379 Table 95. Library Management System: Actors Actor Name Description

Librarian Employee of a Book Library. Main user of the Book Loan System, having extended access to information and processes. Serving and helping the Library Customers in Book Loans and Returns, Title Searches and Reservations.

Maintains Books - Add, Update, Remove -, sticks the printed Labels on the Book, is aware of the Book’s Title and the Title’s Categories.

Maintains Customers - Add, Update, Remove -, hands out the printed Library Membership Card.

Manages Book Loans - Check Out Books, Book Returns.

Searches Books on Loan with Title or Customer information, Searches Customers and Searches Titles.

Creates Reservation with Title and Customer information, can overwrite the Reservation Expiration Date.

Enters and Looks at Book and Title Details.

Enters and Looks at Customer Details.

Maintains the Category List.

Enters the condition of returned Books, if changed, and sets the Status of not returned Books to lost.

Customer Customer borrowing Books from the Library. Casual user of the Library System, having restricted access to information and processes.

Searches Books on Loan with his or her ID and Title information

Searches Titles.

Reserves Titles with his or her ID and Title information.

-

System Timer Device or Operator triggering predefined, scheduled tasks.

Starts the daily Process End-Of-Day task.

-

380 Using VisualAge Smalltalk ObjectExtender Figure 210. Library Management System: Use Case List and Sample Use Case

Note: The "human being" represents an actor, the circle a thing (a potential object class), the bulb a concept, and the oval a use case.

Library Management System 381 Table 96. Library Management System: Main Use Cases with Hyper Link Marks Name Details with Hyper Links: , (Thing), |Concept|, {extends} & [used] use case

Maintain The does maintain (Book) information in the (Book File) - {Add Book}, Books {Update Book}, and {Remove Book} information -, and print |Book Label|s. When adding a book, the Librarian has to be aware of the Book’s (Title) and |Category|s.

Maintain The does maintain (Customer) information in the (Customer File) - Customers {Add Customer}, {Update Customer}, and {Remove Customer} information -, and print |Library Membership Card|.

Manage Book The does manage |Book Loan|s through {Check Out Books} with a Loans default Return Date and a (Customer) individual maximum Number Of Items On Loan check, and through {Return Books} - affecting (Book) (Status) in the (Status File) - according to |Available|, |Borrowed|, |Overdue|, |Lost|, or |Reserved|.

Search Books The does search (Books) of a (Title) or a (Customer) has on loan, On Loan whereas the does search Books of a Title or his or her Books on loan.

- The Librarian optionally enters a Customer |Identification| whereas the Customer must enter his or her Identification, in order to start the search. - {Search Customers} and {Search Titles} are available.

Search The and search (Customer)s in (Customer File) and can Customers {Look At Customer Details}. A customer has restricted search only.

- The |Customer Search Criteria| are: id |Identification|, firstname, middle initials, lastname, street, city, and Customer mandatory: |ZIP|, phone number, and |SSN|.

Search Titles The and search (Title)s in the (Title Catalog). The |Title Search Criteria| are: words in the |Title| and |Abstract|, name of the |Author|, and by |Category|s from the (Category List).

- The Librarian and Customer enter the Title Search Criteria as |Match String|s, start the search, get a list with |Matching Title|s presented, and can {Look At Title Details}.

Reserve Title The and create a (Reservation) of a (Book) (Title) for a (Customer) in the (Reservation File) in the event that all physical copies are on loan.

- The Librarian and Customer enter the Customer and Title |Identification|. The Librarian can set the reservation |Expiration Date|, where as the Customer has to use the default. - (Search Customers) and (Search Titles) are available.

Process The does start the Process End-Of-Day task of the Files: delete End-Of-Day |Expired Reservation|s (Title)/(Book) (Reservation)s in the (Title Catalog) (Reservation File) and update |Expired Book Loan|s with (Book) (Status) changes |Reserved|/|Available| and |Borrowed|/|Overdue| in the (Book File) (Status File).

382 Using VisualAge Smalltalk ObjectExtender Table 97. Library Management System: Extending and Used Use Cases 1 with Link Marks Name Details with Hyper Links: , (Thing), |Concept|, {extends} & [used] use case

Add Book When the Library gets new books delivered, the does {Enter Book Details} and add (Book) to the (Book File), and [Print Book Labels]. The System - Book File - does assign Book ID |Identification| and entry Date.

Update Book When a Book information needs corrections, the does enter the Book ID |Identification| directly or via {Search Books}, [Enter Book Details], and update the (Book) in the (Book File), and when necessary, re{Print Book Labels}.

Remove Book When a Book is no longer up to date or is damaged, the does enter the Book ID |Identification| directly or via {Search Books}, [Look At Book Details], and remove the (Book) from the (Book File).

Search Books The enters the (Title) |Identification| directly or via {Search Titles} to search (Book)s in the (Book File).

Enter Book The assigns the (Title) directly or via {Search Titles} to the (Book), then Details enters its (Status), and condition. The Librarian might {Maintain Titles} first.

Look at Book The and look at the Book Information: (Title), (Status) with Details (Customer) reference, if any and Librarian only, condition, and entry Date.

Print Book The prints the |Book Label|s to stick on the spine and the first page: Labels (Book) ID, (Title) ID, title, |Author|, and primary |Category| - IDs also as |Bar Code|.

Maintain Titles The does maintain (Title) information in the (Title File) - {Add Title}, {Update Title}, and {Remove Title} information. When adding a Title, the Librarian has to be aware of its primary and secondary |Category|s.

Add Title If the Title of a new delivered book is not yet in the system, the does {Enter Title Details} and add the (Title) to the Title File, called (Title Catalog). The System - Title Catalog - does assign Title ID - |Identification| - and entry Date.

Update Title When a Title information needs corrections, the does enter the Title ID |Identification| directly or via {Search Titles}, [Enter Title Details], and update the (Title) in the Title File, called (Title Catalog). The (Book) labels might be reprinted.

Remove Title If there are no Books of that Title in the Library anymore, the title might be removed: the does enter the Title ID |Identification| directly or via {Search Titles}, [Look At Title Details] and remove the (Title) from the Title File, called (Title Catalog).

Enter Title The enters the |Title| details - title, |Author|, |ISBN|, |Abstract|, Details number Of Pages, |Publisher|, publication Date, assigns one primary and none to many secondary |Category|s from the (Category List), and scans or shoots an image of the cover. The Librarian might {Maintain Category List} first.

Look at Title The or look at the (Title) details, on request including cover Details image, and number of (Book)s |Available|, |Reserved| and total in Library.

Library Management System 383 Table 98. Library Management System: Extending and Used Use Cases 2 with Link Marks Name Details with Hyper Links: , (Thing), |Concept|, {extends} & [used] use case

Add Customer When a person becomes a Customer of the Library, the does [Enter Customer Details], add the (Customer) to the (Customer File), and [Print Library Membership Card]. The System - Customer File - does assign Customer ID - |Identification| - and the member Since date.

Update When a Customer information needs corrections, the does enter the Customer Customer ID |Identification| directly - from the |Library Membership Card| - or via {Search Customer}, [Enter Customer Details], update the (Customer) in the (Customer File), and when necessary, re{Print Library Membership Card}.

Remove When a Customer cancels his or her |Library Membership|, the does Customer enter the Customer ID |Identification| directly - from the |Library Membership Card| - or via {Search Customers}, [Look At Customer Details], and - when no |Books| on loan - remove the (Customer) from the (Customer File).

Enter The enters the (Customer) details - firstname, middle initial, lastname, Customer street, city, state, |ZIP|, phone Number, |SSN| -, optionally changes the system defaulted Details maximum Number Of Items On Loan, and shoots a photo. ID |Identification| and member Since date are set by the system at add time.

Look at The and look at the (Customer) details: id for Customer |Identification|, firstname, middle initial, lastname, street, city, state, |ZIP|, Details phone Number, member Since date, maximum Number Of Items On Loan, |SSN|, and photo. A customer can only see own details.

Print Library The prints the |Library Membership Card| for the (Customer) with: id for Membership |Identification|, firstname, middle initial, lastname, memberSince date, and photo. ID Card and memberSince date are printed also as |Bar Code|.

Maintain The maintains |Category|s (Category)s - with ID |Identification|, word or Category List phrase, and short text - in the (Category List) stored in the (Category File).

Return Books The Customer returns Books on loan at the Loan Returns window to the Librarian. The enters each (Book) ID |Identification| and condition - if condition changed. A return affects the Book (Status) accordingly... sometimes to |Lost| - {Search Books}, {Search Books On Loan}, and {Search Customers} are available. The system displays Customer details, books on loan with return Dates, and Books lost, as soon as it knows the |Customer| ID - by entry or via Books in return.

Check Out The Customer checks out Books at the New Loans window with the Library Membership Books Card. The identifies the (Customer) by the |Library Membership Card| or {Search Customers} and {Look At Customer Details}, enters the Customer ID |Identification| - from the Library Membership Card -, and each (Book) ID - from the Label. A loan affects the Book (Status) accordingly. The system displays customer details, books on loan with returnDate, and lost books, and restricts the loan to the maximum Number Of Items On Loan.

384 Using VisualAge Smalltalk ObjectExtender Figure 211. Library Management System: Overview Diagram

Figure 212. Library Management System: Book Management Use Case Diagram

Library Management System 385 Figure 213. Library Management System: Customer Management Use Case Diagram

Figure 214. Library Management System: Book Loan Management Use Case Diagram

386 Using VisualAge Smalltalk ObjectExtender Figure 215. Library Management System: Class Diagram

Note: The status attribute—with the values available, borrowed, overdue, lost, and reserved—was removed from the Book (book copy) class and converted into the Status class hierarchy (Figure 215). The Status class hierarchy implements the state design pattern to make the loan part of the system a reusable framework.

Library Management System 387 388 Using VisualAge Smalltalk ObjectExtender Appendix F. Some Survival Stuff

In this appendix we present some survival stuff to make life easy.

F.1 VapModelBrowserControl>>#deleteAssociation Figure 216 shows the zap that enables you to delete unfinished associations in the model. The unfinished associations have nil in the model, are not editable, are not deleteable, prevent you from adding new ones (except you delete the whole model), and sometimes do not show up in the associations list of the Model Browser. (Close the Model Browser and reopen it to force them to show up).

VapModelBrowserControl>>#deleteAssociation

deleteAssociation > | association model | association := self selectedAssociation. > (model := association model) == nil ifTrue: [ > model := self selectedModel. > (CwMessagePrompter confirm: 'Delete Association ' > , association printString , ' in Model ' > , model printString , '?' ) ifFalse: [^self] ]. > model removeElement: association.

self markModelDirty

Figure 216. Zap to Enable Deletion of Unfinished Class Associations

F.2 VapSchemaColumnEditorModel>> #areForeignKeysValid

When you try to change the type of columns that are part of a foreign key relationship, you get an error such as that shown in Figure 217. ObjectExtender’s consistency check stops you from changing key column types directly, if they are part of a relationship.

You either delete all foreign key relationships, change the key column types, and reenter the foreign key relationships, or you make a minor zap as suggested in Figure 218. If you choose to zap, the foreign key definition consistency becomes your responsibility, and you have to take care of broken property mappings in the map caused by key definition inconsistencies. Another approach is to define the model without the associations, generate

© Copyright IBM Corp. 1999 389 the schema, change the primary key column types, and then define the associations in the model and complement the schema by generating just the foreign key associations.

Figure 217. Error When Trying to Change a Key Column Type

VapSchemaColumnEditorModel>>#areForeignKeysValid

areForeignKeysValid

"Answer a boolean if the any foreign keys that the column participates in are valid First create a column that has the values we intend to commit ( the preview column ) The bomb around each foreign key and if it has a column that maps to us check whether we are > isomorphic. If not error and return false --- if not confirmed to change anyway."

| previewColumn |

previewColumn := myColumn copy. self updateColumn: previewColumn.

myColumn table foreignKeys do: [ :key | | oppositeColumn | oppositeColumn := key columnMappedWith: myColumn. oppositeColumn notNil ifTrue: [ ( oppositeColumn isomorphic: previewColumn ) ifFalse: [ > ^(CwMessagePrompter warn: "System errorMessage:" ( VapSchemaBrowserString122 "The foreign key component %1 does not match the type of its primary key counter-part" > bindWith: key name, ' ', oppositeColumn printString) , ’, and - may be - others as well.’ ) == true. > "^false" ] ] ].

^true

Figure 218. Zap to Enable Direct Foreign Key Relationship Column Type Change

With the zap shown in Figure 218, the first mismatch triggers the warning shown in Figure 219. After you accept the mismatches, your Foreign Key Relationships list shows inconsistent items, indicated by two less than (<<) characters in front of the relation name (Figure 220 on page 391). You may have to refresh the foreign key relationships list by clicking on

390 Using VisualAge Smalltalk ObjectExtender the model. After you change all affected key column types to the new definitions, the inconsistencies disappear. Again, you may have to refresh the foreign key relationships list by clicking on the model.

Figure 219. Key Column Type Definition Inconsistency Warning

Figure 220 shows foreign key relationships with inconsistent key type definitions.

Figure 220. Inconsistent Foreign Key Relationships

F.3 Required Self-Associations The Resource>>#updateIndex:in:startingAt: runs for the TOP department into a Stack Overflow walkback, because the TOP department contains also itself in the departments collection, and ObjectExtender iterates over the departments collection, to reach all objects and update the sort indices for each collection element. The runtime system code zap of the line with the nil-check of the targetB(usiness)o(bject) aTargetBo notNil in Figure 221 ensures that required self-references are processed only once. The check must be complemented with the self-referencing or recursion-break check. Finally the line reads (new part in bold): aTargetBo notNil & (aTargetBo ~~ version businessObject)

Some Survival Stuff 391 Resource>>#updateIndex:in:startingAt:

updateIndex: aVersionIndex in: theIndexes startingAt: aLastIndex

"Add a version to be the last in the collection."

| index version targetIndex |

index := aLastIndex + 1. aVersionIndex key: index. version := aVersionIndex value.

version links do: [:aLink | (aLink isConnected and: [version hasPrecedenceOver: aLink]) ifTrue: [ aLink targetCollection do: [:aTargetBo | > aTargetBo notNil & (aTargetBo ~~ version businessObject) ifTrue: [ (targetIndex := theIndexes at: aTargetBo ifAbsent: [nil]) notNil ifTrue: [ index := self updateIndex: targetIndex in: theIndexes startingAt: index]]]]].

^index

Figure 221. Zap for Required Self Referencing Associations (Stack OVerflow)

For a sample with test scripts focusing on the one-to-many and self-referencing-required association, see workspace Rb1MSROW.wsp and application Rb1toMselfReqApp in Appendix C, “Downloadables, ConfigMaps, Applications” on page 355.

F.4 Non-Required, One-to-Xxx Association ObjectExtender needs a little help to run one-to-xxx, non-required associations properly. You may either change the generated code—override a method or change the inheritance, or change the code generator to solve the problems.

For a sample with test scripts focusing on the one-to-one association, see workspace Rb1to1OW.wsp and application Rb1to1App in Appendix C, “Downloadables, ConfigMaps, Applications” on page 355.

392 Using VisualAge Smalltalk ObjectExtender F.4.1 Forward: Nil-Ability In a department you can set an unset manager and replace a set manager with another manager, but you cannot set a manager nil (for example in 7.6.1, “Set up Sample Objects” on page 148, CLEANUP part). In that case the foreign key has to be updated with NULL. When it comes to getting the foreign key value for the update, the key is taken from the dataObject, and the dataObject is and must be still the read value. The required change is in the generated code. You can either zap ObjectExtender’s model code generation code (Figure 222), or zap the generated model code manually after each generation (Figure 223).

StRelationshipClass>>#addGetForwardKey

addGetForwardKey

| method source template |

method := (StMethod new: 'primGetForwardKeyForLink:' parms: #('aLink')) methodComment: 'Get the forward key value to populate the passed link'; setPublic; addCategory: 'accessing'; yourself.

> "template := ' ^aLink source %1'." > > "Get NULL key column(s) for nil set 1:1 links." > template := (WriteStream on: String new) > cr; tab; nextPutAll: '| key |'; > cr; tab; nextPutAll: 'key := aLink source %1.'; > cr; tab; nextPutAll: 'aLink target isInitialTarget ifFalse: [’; > cr; tab; tab; nextPutAll: ’key := key class new ].'; > cr; tab; nextPutAll: '^key'; > contents. > template := template bindWith: self associationEnd name , 'Key'.

source := method source. source nextPutAll: template. self addInstanceMethod: method

Figure 222. Zap of Non-Required One-to-One Associations Model Code Generator

Some Survival Stuff 393 Rb30DepartmentToManagerRelationship>>#primGetForwardKeyForLink:

primGetForwardKeyForLink: aLink

"Get the forward key value to populate the passed link"

> aLink target isInitialTarget ifFalse: [ > ^aLink source managerKey class new ]. ^aLink source managerKey

Figure 223. Zap of Generated Non-Required One-to-One Associations Model Code

F.4.2 Backward: Individual Access Solution (Overriding) Asking a non managing employee for the managed department (for example in “Finding the Department (If Any) an Employee Manages” on page 159) opens the debugger window (Figure 224).

Figure 224. Dubugger: Non Managing Rb30Employee>>#managedDepartment

Use the following four steps to fix this situation on flight by overriding the failing method in the generated model code class (see also F.4.3, “Backward: Generic Access Solution (New Inheritance)” on page 396):

394 Using VisualAge Smalltalk ObjectExtender 1. Get the Class: In the Debugger (Figure 224) select Rb30EmployeeToManagedDepartmentRelationship(Relationship) >>#objectForForeignKey: 2. from the stack (left middle list) and select Stack->Browse Receiver Class to open a Class Editor on the receiver class (Figure 225). 3. Get the Method: In the Class Editor select Methods->Visibility->To Named Class... and select the class Relationship in the selection dialog (Figure 226, left). Ensure that the Methods->Visibility->Show All Implementations and Categories->Select All By Default toggles are checked. 4. Override the Method: Now, in the Class Editor select the objectForForeignKey:(Relationship) method in the list, change the passage ...[self error: VapNoObjectFound]... (in the last line) to: ...[^nil "self error: VapNoObjectFound"]... as shown in Figure 225, save the method (by pressing Ctrl-S), choose the Rb30EmployeeToManagedDepartmentRelationship class in the save dialog (Figure 226, right side), choose the Not categorized category categorization, and close the Class Editor window.

Figure 225. Overriding #objectForForeignKey: Method Just Before Saving It

Some Survival Stuff 395 Figure 226. Class for Method Visibility and Source, and Class for the Method Save

5. Resume the Execution: In the Debugger (Figure 224) select Rb30EmployeeToManagedDepartmentRelationship(Relationship) >>#fetchTargetForLink: from the stack (left middle list, just below the item that you selected in step 1) and select Stack->Browse Receiver Class to resume with a restart of the dedicated, newly created, no longer inherited Rb30EmployeeToManagedDepartmentRelationship >>#objectForForeignKey: method.

Wow! That’s IBM Smalltalk!

F.4.3 Backward: Generic Access Solution (New Inheritance) We have recognized Appendix F.4.2, “Backward: Individual Access Solution (Overriding)” on page 394 that the implementers of ObjectExtender had decided how the objectForForeignKey: method of the Relationship class should work. We decided that if no such object existed, then an error should be asserted. We prefer to have nil returned. Therefore, the generic solution is to implement a subclass of the Relationship class with the single method objectForForeignKey:, and let the one-to-one relationships, for example, our employee-to-managed-department relationship in the samples inherit from it.

We proceed as follows: 1. Create a new application to hold the new class. For the purposes of this sample, we named the application Rb99EnhancedRelationshipApp. 2. Create a new part in the application. The relevant New Part dialog, after completion, should look like Figure 227.

396 Using VisualAge Smalltalk ObjectExtender Figure 227. New Part Dialog for the Rb99EnhancedRelationship Class

3. Remove all prerequisites in the Rb99EnhancedRelationshipApp except the Va pTra nsa cti on . Use the Prerequisites dialog from the application pop-up menu in the VisualAge Organizer. 4. Create a new method objectForForeignKey: in this class with the code as shown in Figure 228, and assign it to the category hydrating.

Figure 228. Rb99EnhancedRelationship>>#objectForForeignKey: Method

5. Make the Rb99EnhancedRelationshipApp visible to the Rb31EmpDeptApp by adding it as a prerequisite for the application. When this has been done, the resulting prerequisites dialog should look

Some Survival Stuff 397 like Figure 229. The relevant Prerequisites command is available on the Applications menu of the VisualAge Organizer. This step is essential for the next step.

Figure 229. Prerequisites for Rb99EmpDeptApp

6. Locate the Rb31departmentToManagerRelationship class in the Rb31EmpDeptApp and open it with the Script Editor. Change the class definition so that it is now a subclass of Rb99EnhancedRelationship rather than Relationship and save the changes.

When you have made all these changes, you are ready to try the test again, to check whether a department has a manager.

This solution comes with a warning: When we modify the script in Step 6 above, we are modifying automatically generated code. However, subsequent model code class regeneration is not affected, because the re-generation updates only the relationship class and does not change the inheritance of the class. However, you should always review the model definition if you regenerate the code, to ensure that the class inheritance hierarchy remains as we set it in Step 6.

F.5 SQL Execution Order in Multiple Table Inheritance Mapping On multiple table inheritance mapping ObjectExtender generates as many SQLs as tables are involved in and for each of the basic operations, such as insert, update, and delete. If constraints for the root and leaf tables exist in

398 Using VisualAge Smalltalk ObjectExtender the database, the execution order of the SQLs is relevant. If ObjectExtender generates them incorrectly, SQL errors -530 occur as printed in the System Transcript output below (running the test script in 12.1.4, “Test Single Table Inheritance Mapping Sample” on page 264 with basic trace): ======a Rb62Book(1) a Rb62Borrowed(1) ------a Rb62Book(1) a Rb62Borrowed(1) '>>>executing insert' >>>executing: INSERT INTO USERID.Rb62Book ( serialNumber, condition ) VALUES ( 1, NULL ) >>>executed without error '>>>executing insert' >>>executing: INSERT INTO USERID.Rb62Borrowed ( id, returnDate, startDate ) VALUES ( 1, {d '1998-08-09' }, {d '1998-07-10' } ) >>>executed with error: [SQLSTATE=23503 - [IBM][CLI Driver][DB2/NT] SQL0530N The insert or update value of the FOREIGN KEY "USERID.RB62BORROWED.BORROWEDTOSTATUS" is not equal to any value of the parent key of the parent table. SQLSTATE=23503 [Native Error=-530]]

>>>executing: INSERT INTO USERID.Rb62Status ( id, type, bookSerialNumber, customer ) VALUES ( 1, 'Borrowed', 1, 999 ) >>>executed with error: [SQLSTATE=23503 - [IBM][CLI Driver][DB2/NT] SQL0530N The insert or update value of the FOREIGN KEY "USERID.RB62BORROWED.BORROWEDTOSTATUS" is not equal to any value of the parent key of the parent table. SQLSTATE=23503 [Native Error=-530]]

Note: Borrowed is subclass of Status and the Status table is the parent table (with the primary key) of the Borrowed table (with the foreign key). The primary key of the Borrowed table is also the foreign key. The entry in the table with the parent table has to be inserted first and deleted last in order to satisfy the constraints.

Either remove the constraints from the database or check and correct the sequence in the generated Query Pools in the services application. For the modifications see, for example, Figure 230. Note that indices in the argArray at: statements stay the same. Changing the Query Pools generation source code is not possible, because it is not available.

Some Survival Stuff 399 Figure 230. SQL Sequence for Multiple Table Inheritance Mapping

For the sample in 12.2, “Multiple Table Inheritance Mapping” on page 269 the insertQuery:withInjector: and the deleteQuery:withInjector: methods of the following Query Pool classes in the Rb62LibraryServicesApp are affected: • Rb62LibraryRb62AvailableQueryPool • Rb62LibraryRb62BorrowedQueryPool • Rb62LibraryRb62OverdueQueryPool • Rb62LibraryRb62ReservedQueryPool

F.6 Transaction>>#hasModifications Support The Transaction and BusinessTransaction of the current version of ObjectExtender have no #hasModifications support to use, for example, trigger a message such as Unsaved Changes. Save?—Yes/No/Cancel. A few changes to the classes in the modification notification chain make this very useful support available. The Transaction>>#hasModifications support is used, for example, in the Rb30DepartmentDetailView and Rb30EmployeeDetailView in 7.7.3, “GUI with List and Detail Views Supporting Drag and Drop” on page 170.

400 Using VisualAge Smalltalk ObjectExtender Figure 231 gives an overview of a modification notification chain. A modification of an attribute of a business object version performs a modification level increment, which is promoted up to the transactions as versionChanged. The versionChanged method in the transactions asks for the read-only attribute hasModifications by drilling to the version object and reporting the result with the hasModification event.

BusinessTransaction Transaction View Version 11 11 1n hasModifications hasModifications hasModifications isModified increment Modification versionChanged versionChanged versionChanged Level

fires event hasModifications invokes with read-only attribute hasModifications 2. Event hasModificaitons 1. Object Modification

Figure 231. Sequence of a Modification Notification

Table 102 through Table 103 show the new (N) and modified (M) methods to support the hasModifications on the BusinessTransaction and the Transaction. The new methods are in the newly created OERedbook category; the modified methods are in the newly created OERedbook-changed category.

Note that both the Transaction class and the BusinessTransaction class must define the read-only attribute hasModifications(Boolean) with the event selector in their public interfaces. See, for example, the hasModifications attribute definition of the Transaction public interface in Figure 232.

Some Survival Stuff 401 Table 99. New Transaction Methods to Notify Modifications Transaction>>#methods (N=New, M=Modified)

N versionChanged

self signalEvent: #hasModifications with: self hasModifications

N hasModifications

view notNil ifTrue: [^view hasModifications]. ^false

Table 100. New and Modified TransactionView Methods to Notify Modifications TransactionView>>#methods (N=New, M=Modified)

N versionChanged

transaction notNil ifTrue: [transaction versionChanged]

N hasModifications

self versions do: [ :v | v isModified ifTrue: [^true] ]. ^false

M primAddVersion: aVersion "Add a version into the view"

self versions at: ( self versionIdentifierFor: aVersion ) put: aVersion. > self versionChanged. ^aVersion

Table 101. Modified Version Method to Notify Modifications Version>>#incrementModificationLevel (M=Modified)

M incrementModificationLevel

> modificationLevel := modificationLevel + 1. > view notNil ifTrue: [view versionChanged]

402 Using VisualAge Smalltalk ObjectExtender Table 102. New and Modified BusinessTransaction Methods to Notify Modifications BusinessTransaction>>#methods (N=New, M=Modified)

N hasModifications

transaction notNil ifTrue: [^transaction hasModifications]. ^false

M generateTransactionFollowingCommitOrRollback

"Following our transaction signalling that it was the root of a commit or rollback ( i.e. not committed or rolledback as part of a cascade we need to re-generate our transaction"

self generateTransaction ; attachDependenciesToTransaction ; > signalEvent: #transaction with: transaction

M transaction

transaction isNil ifTrue: [ self generateTransaction ; > attachDependenciesToTransaction ; > signalEvent: #transaction with: transaction. ].

^transaction

M attachDependenciesToTransaction

"A committed or rolledback transaction has to be replaced by a one"

transaction notNil ifTrue: [ transaction abtWhen: #rootCommittedOrRolledback perform: ( DirectedMessage new receiver: self ; selector: #transactionWasCommittedOrRolledback: ; > arguments: ( Array with: nil ) ). > transaction abtWhen: #hasModifications perform: ( > DirectedMessage new > receiver: self ; > selector: #signalEvent:with: ; > arguments: ( Array with: #hasModifications with: nil ) ) ]

Some Survival Stuff 403 Table 103. New and Modified TransactionView Methods to Notify Modifications TransactionView>>#methods (N=New, M=Modified)

N versionChanged

transaction notNil ifTrue: [transaction versionChanged]

N hasModifications

self versions do: [ :v | v isModified ifTrue: [^true] ]. ^false

M primAddVersion: aVersion "Add a version into the view"

self versions at: ( self versionIdentifierFor: aVersion ) put: aVersion. > self versionChanged. ^aVersion

Figure 232. Public Interface: hasModifications Attribute of Transaction

404 Using VisualAge Smalltalk ObjectExtender F.7 Transaction Name Form Figure 233 shows the Rb99TransactionForm in the Rb99OeUtilityViewApp. This form is very helpful when tracking transactions. If the transaction has no name set by the application, it displays its basicHash. The transaction(self) variable is the promoted part feature to make the connections to either a transaction itself or to the transaction feature of the BusinessTransaction. The Rb99TransactionForm is used, for example, in the Rb30DepartmentDetailView and Rb30EmployeeDetailView in 7.7.3, “GUI with List and Detail Views Supporting Drag and Drop” on page 170.

Figure 233. Form with Script to Display the Transaction Name

F.8 List Connections Utility Throughout this book we used our own DfksListConnectionsView (Figure 234) tool in the DfksListConnectionsApp application to capture the connections of a part and display them in tabular form. In Figure 235 it shows its very own connections. Mark and copy the connection descriptions in the text pane to paste them in your documentation tool.

Some Survival Stuff 405 Figure 234. DfksListConnectionsView in Composition Editor

Figure 235. DfksListConnectionsView at Runtime Listing Its Own Connections

406 Using VisualAge Smalltalk ObjectExtender Appendix G. Special Notices

This publication is intended to help application developers understand and use the ObjectExtender feature of the IBM VisualAge Smalltalk Enterprise product family. The information in this publication is not intended as the specification of any programming interfaces that are provided by the IBM VisualAge Smalltalk Enterprise product family. See the PUBLICATIONS section of the IBM Programming Announcement for the IBM VisualAge Smalltalk Enterprise product family for more information about what publications are considered to be product documentation.

References in this publication to IBM products, programs or services do not imply that IBM intends to make these available in all countries in which IBM operates. Any reference to an IBM product, program, or service is not intended to state or imply that only IBM’s product, program, or service may be used. Any functionally equivalent program that does not infringe any of IBM’s intellectual property rights may be used instead of the IBM product, program or service.

Information in this book was developed in conjunction with use of the equipment specified, and is limited in application to those specific hardware and software products and levels.

IBM may have patents or pending patent applications covering subject matter in this document. The furnishing of this document does not give you any license to these patents. You can send license inquiries, in writing, to the IBM Director of Licensing, IBM Corporation, 500 Columbus Avenue, Thornwood, NY 10594 USA.

Licensees of this program who wish to have information about it for the purpose of enabling: (i) the exchange of information between independently created programs and other programs (including this one) and (ii) the mutual use of the information which has been exchanged, should contact IBM Corporation, Dept. 600A, Mail Drop 1329, Somers, NY 10589 USA.

Such information may be available, subject to appropriate terms and conditions, including in some cases, payment of a fee.

The information contained in this document has not been submitted to any formal IBM test and is distributed AS IS. The use of this information or the implementation of any of these techniques is a customer responsibility and depends on the customer’s ability to evaluate and integrate them into the customer’s operational environment. While each item may have been reviewed by IBM for accuracy in a specific situation, there is no guarantee

© Copyright IBM Corp. 1999 407 that the same or similar results will be obtained elsewhere. Customers attempting to adapt these techniques to their own environments do so at their own risk.

Any pointers in this publication to external Web sites are provided for convenience only and do not in any manner serve as an endorsement of these Web sites.

The following terms are trademarks of the International Business Machines Corporation in the United States and/or other countries: IBM  VisualAge DB2

The following terms are trademarks of other companies:

C-bus is a trademark of Corollary, Inc.

Java and HotJava are trademarks of Sun Microsystems, Incorporated.

Microsoft, Windows, Windows NT, and the Windows 95 logo are trademarks or registered trademarks of Microsoft Corporation.

PC Direct is a trademark of Ziff Communications Company and is used by IBM Corporation under license.

Pentium, MMX, ProShare, LANDesk, and ActionMedia are trademarks or registered trademarks of Intel Corporation in the U.S. and other countries.

UNIX is a registered trademark in the United States and other countries licensed exclusively through X/Open Company Limited.

Other company, product, and service names may be trademarks or service marks of others.

408 Using VisualAge Smalltalk ObjectExtender Appendix H. Related Publications

The publications listed in this section are considered particularly suitable for a more detailed discussion of the topics covered in this redbook.

H.1 International Technical Support Organization Publications

For information on ordering these ITSO publications see “How to Get ITSO Redbooks” on page 411. • Using VisualAge UML Designer, SG24-4997 • VisualAge Java - RMI - Smalltalk The ATM Sample from A to Z, SG24-5418

H.2 Redbooks on CD-ROMs Redbooks are also available on CD-ROMs. Order a subscription and receive updates 2-4 times a year at significant savings.

CD-ROM Title Subscription Collection Kit Number Number System/390 Redbooks Collection SBOF-7201 SK2T-2177 Networking and Systems Management Redbooks Collection SBOF-7370 SK2T-6022 Transaction Processing and Data Management Redbook SBOF-7240 SK2T-8038 Lotus Redbooks Collection SBOF-6899 SK2T-8039 Tivoli Redbooks Collection SBOF-6898 SK2T-8044 AS/400 Redbooks Collection SBOF-7270 SK2T-2849 RS/6000 Redbooks Collection (HTML, BkMgr) SBOF-7230 SK2T-8040 RS/6000 Redbooks Collection (PostScript) SBOF-7205 SK2T-8041 RS/6000 Redbooks Collection (PDF Format) SBOF-8700 SK2T-8043 Application Development Redbooks Collection SBOF-7290 SK2T-8037

© Copyright IBM Corp. 1999 409 410 Using VisualAge Smalltalk ObjectExtender How to Get ITSO Redbooks

This section explains how both customers and IBM employees can find out about ITSO redbooks, CD-ROMs, workshops, and residencies. A form for ordering books and CD-ROMs is also provided.

This information was current at the time of publication, but is continually subject to change. The latest information may be found at http://www.redbooks.ibm.com/.

How IBM Employees Can Get ITSO Redbooks

Employees may request ITSO deliverables (redbooks, BookManager BOOKs, and CD-ROMs) and information about redbooks, workshops, and residencies in the following ways: • Redbooks Web Site on the World Wide Web http://w3.itso.ibm.com/ • PUBORDER – to order hardcopies in the United States • Tools Disks To get LIST3820s of redbooks, type one of the following commands: TOOLCAT REDPRINT TOOLS SENDTO EHONE4 TOOLS2 REDPRINT GET SG24xxxx PACKAGE TOOLS SENDTO CANVM2 TOOLS REDPRINT GET SG24xxxx PACKAGE (Canadian users only) To get BookManager BOOKs of redbooks, type the following command: TOOLCAT REDBOOKS To get lists of redbooks, type the following command: TOOLS SENDTO USDIST MKTTOOLS MKTTOOLS GET ITSOCAT TXT To register for information on workshops, residencies, and redbooks, type the following command: TOOLS SENDTO WTSCPOK TOOLS ZDISK GET ITSOREGI 1998 • REDBOOKS Category on INEWS • Online – send orders to: USIB6FPL at IBMMAIL or DKIBMBSH at IBMMAIL

Redpieces For information so current it is still in the process of being written, look at "Redpieces" on the Redbooks Web Site (http://www.redbooks.ibm.com/redpieces.html). Redpieces are redbooks in progress; not all redbooks become redpieces, and sometimes just a few chapters will be published this way. The intent is to get the information out much quicker than the formal publishing process allows.

© Copyright IBM Corp. 1999 411 How Customers Can Get ITSO Redbooks

Customers may request ITSO deliverables (redbooks, BookManager BOOKs, and CD-ROMs) and information about redbooks, workshops, and residencies in the following ways: • Online Orders – send orders to: IBMMAIL Internet In United States usib6fpl at ibmmail [email protected] In Canada caibmbkz at ibmmail [email protected] Outside North America dkibmbsh at ibmmail [email protected] • Telephone Orders United States (toll free) 1-800-879-2755 Canada (toll free) 1-800-IBM-4YOU

Outside North America (long distance charges apply) (+45) 4810-1320 - Danish (+45) 4810-1020 - German (+45) 4810-1420 - Dutch (+45) 4810-1620 - Italian (+45) 4810-1540 - English (+45) 4810-1270 - Norwegian (+45) 4810-1670 - Finnish (+45) 4810-1120 - Spanish (+45) 4810-1220 - French (+45) 4810-1170 - Swedish • Mail Orders – send orders to: IBM Publications IBM Publications IBM Direct Services Publications Customer 144-4th Avenue, S.W. Sortemosevej 21 Support Calgary, Alberta T2P 3N5 DK-3450 Allerød P.O. Box 29570 Canada Denmark Raleigh, NC 27626-0570 USA • Fax – send orders to: United States (toll free) 1-800-445-9269 Canada 1-800-267-4455 Outside North America (+45) 48 14 2207 (long distance charge) • 1-800-IBM-4FAX (United States) or (+1) 408 256 5422 (Outside USA) – ask for: Index # 4421 Abstracts of new redbooks Index # 4422 IBM redbooks Index # 4420 Redbooks for last six months • On the World Wide Web Redbooks Web Site http://www.redbooks.ibm.com IBM Direct Publications Catalog http://www.elink.ibmlink.ibm.com/pbl/pbl

Redpieces For information so current it is still in the process of being written, look at "Redpieces" on the Redbooks Web Site (http://www.redbooks.ibm.com/redpieces.html). Redpieces are redbooks in progress; not all redbooks become redpieces, and sometimes just a few chapters will be published this way. The intent is to get the information out much quicker than the formal publishing process allows.

412 Using VisualAge Smalltalk ObjectExtender IBM Redbook Order Form

Please send me the following: Title Order Quantit

First name Last name

Company

Address

City Postal code Country

Telephone number Telefax number VAT number

Invoice to customer number

Credit card number

Credit card expiration date Card issued to Signature

We accept American Express, Diners, Eurocard, Master Card, and Visa. Payment by credit card not available in all countries. Signature mandatory for credit card payment.

413 414 Using VisualAge Smalltalk ObjectExtender Glossary

ACID. A mnemonic for the properties a records. ObjectExtender provides these transaction should have to satisfy the Object operations on objects as well. Management Group Transaction Service data definition language (DDL). A language specifications. A transaction should be Atomic, enabling the structure and instances of a its result should be Consistent, Isolated database to be defined in a human- and (independent of other transactions) and machine-readable form. Durable (its effect should be permanent). data model. The term, data model is used here, atomic. An atomic database transaction is one to make a distinction between it and an object which is guaranteed to complete successfully or model that you wish to persist. For example, the not at all. In an error prevents a schema for a relational database represents the partially-performed transaction from data model. Your object model is represented proceeding to completion, it must be backed-out differently and will not need to be tightly to prevent the database from being left in an coupled with the data model representation. inconsistent state. data object (DO). Data objects contain the data broken mapping. An inconsistent mapping, for the business objects. The data is in the form for example, caused by overlays. in which it was retrieved from the data store. business object (BO). This term denotes the durability. In the context of ACID: Once a objects in your problem domain that you wish to transaction completes successfully (commits), persist in a data store. its changes to the state survive failures. cardinality. Cardinality expresses the framework. In object-oriented systems, a set of constraints on the number of instances that are classes that embodies an abstract design for related through a relationship. solutions to a number of related problems. code-generation. The function whereby code headless. without a user interface. is generated automatically given certain specifications. home collection. Home collections provide the logical home for business objects. They provide collision. A collision between transaction APIs for creating or locating instances. occur, when they try to commit changes on the same objects or data. hydration. The activity of populating the properties and relationships of a model object. concurrence. Concurrence is the situation, when two or more program are changing the isolation. In the context of ACID: even though same objects or data at the same time. transactions execute concurrently, it appears to each transaction, T, that others executed either connect/disconnect. The attachment (or before T or after T, but not both. unattachment) of a target business object to/from a link. link. The infrastructure that connects source and target business objects in a relationship. consistency. In the context of ACID: a transaction is a correct transformation of the metadata. Any information which describes state. The actions taken as a group do not according to prescribed specification a target violate any of the integrity constraints data. associated with the state. This requires that the multiplicity. See cardinality. transaction be a correct program. nested transaction. A nested transaction is a CRUD. Create/Read/Update/Delete, the four tree of transactions, the sub-trees of which are basic types of operations on database rows,

© Copyright IBM Corp. 1999 415 either nested or flat transactions. Transactions at the leaf level are flat transactions. The transaction at the root of the tree is called the top-level transaction; the others are called subtransactions. A transaction’s predecessor in the tree is called a parent; a subtransaction at the next lower level is also called a child. A subtransaction can either commit or roll back; its commit will not take effect though, unless the parent transaction commits. Therefore, any subtransaction can finally commit only if the top-level transaction commits. The rollback of a transaction anywhere in the tree causes all its subtransactions to roll back. object model. Object model is to data model what a hierarchy of classes is to a schema of database tables. It is simply a distinction between the two representations of how the data is represented. object-oriented. Can apply to analysis, design, and programming disciplines. persistence. A property of a programming language where created objects and variables continue to exist and retain their values between runs of the program. This is in contrast to transient objects that cease existing when the application that created them is not running. pre-fetch. The notion of defining a path to set of data that you want to preload from data store to object model to reduce the number of database trips improving performance. relationship. As understood in the context of the ObjectExtender framework, a relationship is an instance variable in a business object which contains a reference to another persistent object. transaction. A unit of interaction with a DBMS or similar system. It must be treated in a coherent and reliable way independent of other transactions.

416 Using VisualAge Smalltalk ObjectExtender List of Abbreviations

ACID Atomic/Consistent/Isolated/ Durable API Application Programming Interface BLOB Binary Large Object BO Business Object CLI Command Language Interface CRUD Create/Read/Update/Delete DB Database DBMS Database Management System DDL Data Definition Language DO Data Object GIF Graphic Interchange Format GUI Graphical User Interface IBM International Business Machines Corporation IDE Integrated Development Environment ITSO International Technical Support Organization ODBC Opend Database Connectivity OE ObjectExtender OO object-oriented SQL Structured Query Language TRX Transaction UDB Universal Database UML Unified Modeling Language VA VisualAge VA P VisualAge Persistence

© Copyright IBM Corp. 1999 417 418 Using VisualAge Smalltalk ObjectExtender Index speed of database with inheritance 259 tune 19 Symbols ACID 312 #abtRemove 305 acronyms 417 #allInstances 267, 277, 341 activate #areForeignKeysValid 145, 389 data store 35, 227, 232 #basicHash 405 menu 75 #canMergeWith: 332 active transaction 11 #commitWhenFailureDo: 332 add #deleteAssociation 389 attribute 218 #hasModification support 178 foreign key relationship 197 #hasModifications 190, 400 method 218 #manyQueryUsing:with: 281 method to home collection class 279 #markRemoved 249 public interface 218 #maximumLength 131 service to the service object 280 #openedWidget 328 SQL query string to query pool 279 #referenceAttributeName 172 add... method 136 #referenceString 164, 170 adjust #setToCompleted 281 many 203 #singleQueryUsing:with: 281 navigable 203 #validationError 97 physical name 143 #vapAsSQLStringFor: 281 schema 197 #xyzReferenceString 170 administrator, database administrator 62, 146 (a) attribute allInstances 116, 277 property 203 inspect 124 property map 33, 205 allow nulls 145 (r) association (relationship) alternative 22 property 203 application 8 property map 33, 205 behavior on changes 326 superclass 260 contains all model code 135 207 contains part of the model code 135 context 28 data 15 Numerics develop 19 1999..., Y2K 265, 266 download 355 2000, Y2K 265, 266 extension 19 image 15 A initial version 19 abbreviations 417 name 41, 135, 300 abstraction levels for data 15 name for map storage 230 abstraction of data 15 name for services 69 access prerequisite 136 backward 148 rapid development 19 control 15 real world 14, 332 database 270 script 15 optimize 35 application layers in ObjectExtender 5 speed 270 application name

© Copyright IBM Corp. 1999 419 for model code 54 editor 24, 51 for services 112 for convenience 164 application prerequisites for metadata storage map 64, 229 354 map to column 229 application programming layer 5 name 50, 109 approach to inheritance 258 new 51 appropriate non business for predicate 333 single table inheritance map 259 page of property map editor 229 value for discriminator 263 property 52 argument, name of argument 136 property map editor 33 artificial object ID 259 public interface 214 aspect, behavioral aspect 257 read-only 161 association 4, 7, 127 reference 170 among classes 14 relational 134 class 136, 203, 241 separate table 269 define 129, 299 timestamp 334 direction 132 transaction of variable 328 display object 214 value 38 editor 8, 24, 129, 132, 203 value not required 203 forward 203, 207 vs. behavior for inheritance 259 generate model code 135 attribute property map editor 33 inheritance 257 automatic naming 109 many 131 many-to-many 241 map 10, 230 B backward map to foreign key relationship 230 access 148 name 129 non-required one-to-one relationship 394, navigable 131, 203 396 nil-able 231 path 19, 20, 105, 195 non-required 231 basic trace 37, 280 not mapped 207 basicHash for transaction ID 405 one-to-many 7 be part of optimistic predicate 332 one-to-one 7, 231 behavior one-to-xxx 203 desired for changes 326 page 205 for transactions last wins 332 page of property map editor 230 vs. attributes for inheritance 259 property map editor 33 behavioral aspect 257 reference 164 bidirectional related method 136 navigable 203 required 131 navigable relationship 156 required self-association 391 bill of material 42 role 4, 131 binary large object 337 self 148 BLOB 337 unfinished 389 create view with BLOB 345 attach transaction framework 83 database 337 attribute DB2 342 business logic for collision 333 mixed media 337 class 9, 14, 203, 205 picture 337 define 50, 128, 299

420 Using VisualAge Smalltalk ObjectExtender Smalltalk 342 system diagram 385 static query 342 use case 381, 382 voice 337 use case diagram 385 bottom-up 19, 20 chain combined with top-down 22 generation 137 path 105, 195, 227 of modification notification 401 broken change map 205, 229 class 54 property map 389 column type 143, 389 browse data 12 dirty 268 browser 14 key column type 144 documentation 22 manual 20 map 9, 15, 22, 32, 64, 110, 210, 227 merge 13 model 7, 14, 22, 24, 47, 49, 127, 203, 209, 260 name 205 overview 23 parallel 326 refresh 17 SQL in query pool 334 schema 8, 15, 22, 27, 61, 106, 109, 146, 198, superclass 354 344 type 143, 144 status 35, 75, 87, 113, 123 uncommitted 325 tool 6 changes business concurrent 331 object 343 unsaved 190, 400 rule 14 visibility anywhere else 326 transaction 87, 296 characters allowed in name 29 business class, generate business class 20 check business logic defines attributes for predicate consistency 389 333 foreign key relationship consistency 145 business object 6, 7, 9 check-out 19 definition 5 child transaction 12, 287, 325, 328 generate class 19 circular reference 147 locking 326 class relationship 5 association 14, 136, 203, 241 transacted 328 attribute 9, 14, 203, 205 version 39 change 54 businessTransaction 87, 95, 119 create 7 part in view 90 define 49, 128, 299 diagram 4, 47, 129, 227, 257 discriminator 258 C edition 16 cache, clear relationship cache 35 editor 24, 49, 244, 260 capture editor (lite collection) 342 database schema 20 generate 6, 19 model 19 generate model code 136 schema 20 helper for queries 281 cardinality 242 hierarchy 55, 257 default 203 home 136 case study inheritance 257 concept 378 key 136 requirement 378 leaf 398

421 metadata storage 16, 17 detect 332 model 54, 136, 354 detect in image 326 model code 54, 354 detection in the image 326 name 49, 199, 354 detection on the backend data store 332 name for map storage 230 exception block 332 new 49, 54 merge 13 new created 57 of concurrent transactions 325 object 14 of transactions, definition 326 persistent 64, 229 resolution 333 query pool service 279 resolution implementation 332 root 398 rollback 13 service 279 SQL error 333 services 58, 111, 210 transaction 11 storage 37 collision management VisualAge editor 37 optimistic 11 class diagram for library 44, 387 pessimistic 11 class editor, edit model class 24 column 28 class generate 20 additional for inheritance 261 class, domain class 11 change type 143, 389 cleanup 35 define type 229 cleanup menu 75 discriminator 261, 272 clear edit 30 home collection cache 35 editor 62, 141 local image data store 61, 75 foreign key relationship 144 cluster map with no inheritance 228, 272 key 140, 144, 197 cluster name 354 map 10, 229 code map to attribute 229 application 15 name 109, 140 for data access 19 NOT NULL 145 generate 15 primary key 108, 197 set generation option in model 54, 354 relationship 144 to add a method to a query pool 279 table 9 to add a method to the service object 280 type 62, 139, 143, 229 to add for dealing with locked objects 335 type for foreign key 390 to delegate work to service object 279 type for primary key 390 to generate omitting time stamp 334 column, delete 140 to insert a record into the database 74 combination of bottom-up and top-down 22 to insert current time stamp 334 combo box 86, 99 to make retrieved object accessible 280 comment in storage class 37 to notify association object removal 249 commit 13, 120 to query for multiple objects, corrected 281 database 317 to return exact result set 278 error 330 collect nested transaction 330 method to collect the result 280 top-level transaction 326 query result 279 transaction 268 collection compare before write 65 home 277, 341 compatible type 229 lite 26, 337, 340, 341 complete collision generation 137

422 Using VisualAge Smalltalk ObjectExtender instantiation 26 context complex application 28 dependencies among tables 20 naming 28 model 19 of a transacted variable 328 composition editor 247, 337 transaction 38, 116 concept control, access control 15 case study 378 convenience ObjectExtender 3 add... method 136 object-oriented 257 attribute 164 conclusion method 136, 164 of your first samples 237 remove... method 136 top-down 102 convenience method 161 concurrent 65 convention changes 331 freedom 22 overwrites 123 name 351, 353 transaction 325 naming 41, 50 configuration map, downloadable 355 convert image to string 337 confirm delete 120 converter conflict 65 type 30 of concurrent transactions 325 converter, VapConverter 61, 62 transaction 11 cooperative work 22 conform copy from data object 326 name 140 copy-on-read 326 physical name 140 correct model code 137, 231 connection cost 278 database 112, 146, 190 create information 146, 190 child transaction 328 part 84 class 7 to database locked 268 constraint 146 to physical database 6 CRUD 115, 214 connection list utility 405 DDL 8 connection type of database 190 image schema 139 consider key 146 direction for update 268 map 9, 228 navigation 268 model 47, 128, 227 user interface 12 model manually 227 consideration along outside-in path 22 object 248 consistency part 337 check 389 re-create transaction tree 286 check keys 389 schema 8 responsibility 389 table 146 consistent 15 table map 228 foreign key relationship 145 view with BLOB 345 unit of work 5 view with lite collection 342 constraint CRUD create 146 create, read, update, delete 115, 214 database 8, 130, 231 CURRENT TIMESTAMP in database 197, 198, 398 in insertSqlString,updateSqlString 334 name 140 in SQL 334

423 current transaction 13 legacy 9 custom query 277 physical 6, 8, 61, 69 customize naming 29 relational 4, 8, 15, 19, 257 sample 105, 195 schema 8, 32 D source 231 data 5 table 5, 8, 9, 146 abstraction 15 timestamp for collision detection 334 abstraction level 15 tune access 19 application 15 use CURRENT TIMESTAMP for collision de- browse 12 tection 334 source 106 database schema data access capture 20 code 19 import 20 optimize 35 map to model 19, 22 programming layer 5 DB2 3, 105, 195, 231 data entry error 97 BLOBs 342 data entry view 119 naming limitations 244 data object, copy business object from 326 DDL 15 data store 11, 15 create 8 activate 35, 227, 232 generate 19, 146, 198 backend detects collision 332 SQL 146 clear 61, 75 debug session 35 dump to file 20 debugger framework 15 fix method in debugger 394 generate 227 model editor 389 image 138 standard 77 in image 20 default load from file 20 cardinality 203 map 210, 230 direction 203 map metadata 16 generate default scripts 338 map new 227 many 203 name 210 mapping 64 physical 15 navigable 203 view 15 not required 203 database 3, 6, 19, 20 repeatable-read 326 access 270 deferred write schema 11 administrator 62, 146 define BLOB 337 association 129, 299 commit 317 attribute 50, 128, 299 connection 112, 146, 190 class 49, 128, 299 connection type 190 foreign key 9 constraint 8, 130, 197, 198, 398 foreign key relationship 197, 271 constraints 231 map 244, 262, 341 existing 9 map for inheritance 272 generate physical 145 model 14, 47, 127, 227, 241, 259, 270, 298, in image 69 341 index 8 object model 14 in-image 61 public interface 337

424 Using VisualAge Smalltalk ObjectExtender relationship 54, 271 diagram, class diagram 4, 129, 227, 257 schema 244, 260, 270, 341 difficult mapping 22 schema manually 227 direct edit 51 define primary key 197 direction definition association 4, 132 business object 5 consider for update 268 collision of transactions 326 default 203 delete 12 navigable 7, 203 broken property map 208 navigate 211 column 140 dirty confirm 120 change 268 CRUD 115, 214 object 268 foreign key column 140 discard object version 13 foreign key relationship 140 discriminator row from table 268 appropriate value 263 demonstration purpose 20 class 258 department 41 column 261, 272 derive inheritance 258 information 10 note 263 reference 161 object 258 design 15, 22 row 258 object-oriented 19 subclass 258 state pattern 257 value 263 desired behavior on changes 326 write 263 detail display hide implementation details 305 associated object 214 trace 114, 274, 280 reference 170, 214 type details 30 transaction name 405 view 122 divide (large) transaction 12 detail view 119 documentation drag-drop support 176 browser 22 detect textual summary 22 collision 332 domain collision by SQL error 333 class 11 collision in image 326 download 355 collision on back-end data store 332 downloadable 392 develop drag-drop support application 19 detail view 176 from scratch 19 list view 170 developer of the edition 16 reusable form 171 development 19 single reference 171 environment 6 view 170 integrate 57 drag-source 190 path 19, 47, 105, 127, 195, 227 drag-target 190 repository 17 drawback of single table inheritance map 258 scenario 3 dropped event 191 tasks 14 drop-target 190 time 6 dump data store to file 20 development from scratch 8 duplicate key error 267

425 dynamic ...does not understand ...Key 138 change of locking strategy 325 cannot change its superclass to... 354 SQL 342 data entry 97 database 100 empty key descriptor 109 E handling from input 97 edit Input argument size does not match query column 30 string 335 direct 51 map 205 indirect 51 message prompter 99 parallel 326 meta data validation 207, 229 role name 203 missing object identifier 52 edition on nested transaction commit 330 class 16 SQL 100 developer 16 SQL -530, constraint violation with foreign load 16 key 399 editor 22 top-level transaction 100 association 8, 24, 129, 132, 203 validation 97, 305 attribute 24, 51 event signaling 165 class 24, 49, 244, 260 example, overview of examples in this book 351 class (lite collection) 342 exception column 62, 141 collision 332 composition 247 locked object 335 foreign key relationship 9, 28, 141 exchange metadata 17 interface 337 execution order of SQLs 399 map 10, 205 existing database 9 model 205 export property map 10, 64, 229 metadata 17 property.map 33 schema to database 146 public interface 164, 247, 337 extension, application extension 19 superclass 260 table 62, 108, 197 VisualAge class 37 F VisualAge scripts 37 factory 252 editor composition 337 failure in merge 332 employee 3, 41 failure in transaction 332 top-down 47 file enable download 355 inspector 40 GIF 338 pessimistic locking 66, 325 load picture from file 345 enablement for inspector 160 path 337 enhanced inspector 39 filter enhancedBusinessTransaction 87 objects in lite collections 341 enhancement of transaction 400 fix entity-relationship broken map 207 inheritance 257 method in debugger 394 model 257 method on flight 394 environment, development environment 6 focus on list entry in map 207 error foreign key 4, 9, 133, 137

426 Using VisualAge Smalltalk ObjectExtender column type 390 implementation 22 define 9 naming 22 map 9 front end 5 primary key 270 future model code 203 relationship 14, 28 set 136 SQL error 399 G generate subclass table 270 all model code into the same application 135 table 139, 144 business class 20 foreign key column business object class 19 delete 140 class 6, 19, 20 foreign key relationship code 15 add 197 data store 227 change name 261 DDL 19, 146, 198 check consistency 145 default scripts 338 column 144 framework 15 consistent 145 image schema 57 define 197, 271 map 19, 20, 57, 109, 137, 138, 201, 227, 303 delete 140 metamodel 20 editor 9, 28, 141 model 14, 109, 201 inconsistent 145 model code 56, 111, 135, 137, 209, 300 inheritance 257 model code for a class 136 map 230 model code for an association 135 map to association 230 model code in separate applications 54 schema 197 model code into separate applications 135 side 207 persistent classes option 55 table 389 physical database 145 form result 19 multiple reference 178 schema 19, 57, 61, 137, 138, 139, 260, 302 reusable 170 script 15 single reference 178 selectively 54 forward service classes 111, 210 association 203, 207 services 137, 146, 304 nil-ability 148 services classes 20, 32, 69, 146, 231 nil-ability of non-required one-to-one relation- setter 39 ship 393 SQL 146 path 19, 47, 127 static query 342 relationship 137, 231 VisualAge parts option 55 framework generation attach transaction 83 chain 137 data store 15 complete 137 generate 15 incomplete 138 mapping 15 model code options 209 modeling 14 option for services 69 ObjectExtender 14 options for model code 54, 354 relationship 14 sequence 137 transaction 15, 83 services classes option 210 freedom generation option convention 22

427 model code 111, 135 identifier of an object 52 services classes 146 image 16 getter 136 application 15 generate 39 convert to string 337 GIF 337 data store 20, 101, 138 file 338 detect transaction collision 326 global reset in ObjectExtender 35 from file 337 global shared transaction 11 read 337 good practice 161 image data store application with GUI 101 separate model from implementation 270 image schema 20 graphic interchange format 337 create 139 GUI 83, 101, 115, 161, 170, 214 generate 57 local image schema 101 impedance gap between relational and object-ori- of many-to-many association sample 251 ented world 257 single-window 161 implementation test 122 freedom 22 transaction save 190 hide details 305 object-oriented 19 part 338 H separate from model 270 hasModifications 190, 317 import hasModifications support 400 database schema 20 headless 309 from legacy database 106 test 113, 146, 211, 246, 264, 273, 282, 309 metadata 17 heavy row 26 model 19, 227 Heisenberg, Karl Werner (1901-1976) 39 schema 20, 106, 196 helper used for queries 281 incomplete generation 138 hide inconsistency 389 implementation details 305 inconsistent foreign key relationship 145 implementation details of many-to-many as- incorrect map 229 sociations 246 index in database 8 hierarchy indicator class 257 in row for class and vice versa 258 inheritance 257 indirect edit 51 high-level qualifier 62, 139, 145, 262 information home connection 146, 190 add method 279 derive 10 class 136 inheritance 257 collection 341 all instances for single inheritance map 267 entry point for queries 279 approach 258 serving object 280 association 257 transacted 116 behavior vs. attribute 259 home collection 277 class 257 clear cache 35 cluster map with no inheritance 228 host-variable syntax 279 discriminator 258 hydrate loaded metadata item 17 entity-relationship 257 foreign key relationship 257 I hierarchy 257 Identification of an object 244 impedance gap 257

428 Using VisualAge Smalltalk ObjectExtender insert before delete 267 public attribute 214 map 258 user 5 map tree to table 260 intrude 39 multiple table inheritance map 269, 276 intrusive new for fixing 396 inspector 40 performance 259 intrusive inspector 39 primary key 267 isChanged support 123 relationship 257 isolation root of hierarchy 262 of a transaction 312 root of tree 262 Isolation Policies 325 services 264, 273 isolation policy 325 single table inheritance map 262 isolation policy of transaction 325 special 19 SQL queries 259 subclass 257 J Java, VisualAge for Java Persistence Builder xxi superclass 260 inheritance hierarchy to single table 258 in-image database 61, 69 K initial version of an application 19 key 9, 28, 50, 52, 108, 133, 137 initialize transaction 328 class 136 insert 12 column 140, 144, 197 before delete in inheritance 267 consistency check 389 row into table 268 empty key descriptor error 109 user code 14 for SQL query string 281 insertSqlString with CURRENT TIMESTAMP foreign 4, 9 334 foreign key relationship 14 inspect inconsistent keys 390 allInstances 124 multiple 259 services classes 210 primary 50 inspector SQL error 399 basic 76 subclass table 270 enable 40 table 146 enablement 123, 160 key column enhanced 39 change type 144 intrusive 39, 40 type 144 Smalltalk 38 standard 77 L unintrusive 40 large model 54 instance, all for single table map inheritance 267 large row set 26 instances, all 277 large table 278 instantiate last wins, transaction behavior 332 completely 26 layer 5 object 341 application programming 5 partially 341 data access programming 5 integrated development environment 57 object modeling 5 interested, notify interested parts 338 persistence 6 interface services 6 add public attribute 218 lazy read 76, 77 editor 337

429 lazy set 77 pessimistic 65, 325, 335 leaf set strategy at runtime 325 class 398 strategy 325 table 398 lock-on-read, pessimistic locking 335 legacy logging 36 data access code 19 logical name 28, 139 database 5, 9, 19, 20 length column length 62 M maintenance 54 name 29 management schema for collisions 11 level manual 22 data abstraction 15 change 20 of nested transaction 13 create model 227 subtransaction 12 define schema 227 trace 274 naming 109 library 42 many scenario 41 adjust 203 static class diagram 44, 387 association 131 system overview 42 default 203 use case 42 many-element 136 Library Management System 42 many-to-many limitations in naming 29 association 241 list association sample GUI 251 for selection 115 hide implementation details 246 view 122 map 9, 258 list view 116 (a) attribute property 205 drag-drop support 170 (r) relation (association) property 205 lite collection 26, 337, 340, 341 association 10, 230 class editor 342 association to foreign key relationship 230 performance 26, 341 attribute 64, 229 load attribute to column 229 available schema 139 broken 205, 229 data store from file 20 broken property map 389 edition 16 browser 9, 15, 22, 32, 64, 110, 210, 227 hydrate 17 cluster map with no inheritance 262 map 16 column 10, 229 metadata 16 column to attribute 229 model 16 crate 228 picture from file 345 create 9 refresh 17 create for table 228 schema 16 data store 210, 230 local image data store 20 default 64 local image schema application with GUI 101 define 244, 262, 341 lock resource 11 define for inheritance 272 locked database connection 268 delete broken property mapping 208 locking 39 editor 10, 205 business object 326 error 205 optimistic 65, 325, 326 fix broken mappings 207 optimistic predicate 65

430 Using VisualAge Smalltalk ObjectExtender foreign key 9 repository 16, 17 foreign key relationship 230 revert 17 foreign key relationship to association 230 runtime 354 generate 19, 20, 57, 109, 137, 138, 201, 227, save 16 303 storage 16, 354 incorrect 229 storage class 16, 17 inheritance 258 validation error 207, 229 load 16 metamodel 16, 19 map entry in list has focus 207 generate 20 model 227 model 227 model to database schema 19, 22 method 339 model to schema 227 add 218 name 300 add... 136 new for table 228 convenience 136, 164 of table 64 convenient 161 property for subclass 263 name 129, 136, 203 revert 17 override 394 review 64 related association 136 save 69, 145, 230 remove... 136 schema 227 reuse of collect the result 280 schema to model 227 model 19 self-referencing entry 207 association 389 table 229 browser 7, 14, 22, 24, 47, 49, 127, 203, 209, textual summary 208 260 mapping 5, 6, 15, 109 capture 19 difficult 22 class 54, 136, 354 framework 15 class editor 24 maximum name length 29 class name 199 media BLOB 337 class selection for generation 56 menu code for classes 54, 354 ObjectExtender tool menu 23 code storage application name 54 merge complex 19 change 13 correct code 231 collision 13 create 47, 128, 227 failure 332 define 14, 47, 127, 227, 241, 259, 270, 298, first 39 341 object version 13 editor 205 merge policy 13 entity-relationship 257 message future code 203 prompter 99 generate 14, 109, 201 trace 56 generate code 56, 209, 300 metadata 16 import 19, 227 data store map 16 large 54 exchange 17 load 16 export 17 map 227 import 17 map to database schema 19, 22 load 16 map to schema 227 model 15, 16, 47 metadata 15, 16, 47 move 17 metamodel 227

431 model-view-controller 354 multiplicity 242 MVC 354 MVC 354 name 48, 109, 203, 300, 354 names in MVC layout 354 new 47 object 6, 7, 8, 14, 32 re-generate 14 N name release 60 allowed characters 29 reuse 227 application 41, 135, 300 revert 17 argument 136 review 203 association 129 root of hierarchy/inheritance 262 attribute 50, 109 save 50, 60 change 205 separate from implementation 270 change for foreign key relationship 261 set code generation option in model 54, 354 class 49, 199, 354 storage application name 48 cluster 354 storage class name 48 column 109, 140 textual summary 24, 52, 109 conform 140 unfinished associations 389 constraint 140 version 60 convention 351, 353 model code data store 210 correct 137 for application services 69 generate 111, 135, 137 form to display transaction name 405 generation option 111, 135 in database 29 generation options 55 length 29, 140 re-generate 137 logical 28, 139 review 136 map 300 model code generation 55 map storage application 230 model storage application name 110 map storage class 230 model storage class name 110 method 129, 136, 203 model view controller 354 model 48, 109, 203, 300, 354 modeling 5, 14 model class 199 object-oriented 19, 20, 22 model storage application 110 modeling framework 14 model storage class 110 modeling tool, IBM VisualAge UML Designer 19 MVC 354 modification of services application 112 notification chain 401 physical 28, 130, 198 notify 400 prefix 354 record 11 project 41, 354 modify object 11 role 203, 208 monitor schema 106, 203, 300 headless test 75 services classes 129 status browser 75 storage 110 test with GUI 122 suffix 354 move metadata 17 table 139, 140 multiple primary keys 259 transaction 405 multiple reference name length maximum 29 form 178 naming multiple reference, and form 178 automatic 109 multiple table inheritance map 269, 276, 398

432 Using VisualAge Smalltalk ObjectExtender context 28 column 145 convention 41, 50 required 145 customize 29 notification chain for modifications 401 freedom 22 notify limitation 29 interested part 338 manual 109 modification 400 pattern 7 null 145 subclass 29 support 142 natural object ID 259 O object 5 navigable 7 add service 280 adjust 203 binary large object 337 association 131, 203 business 343 bidirectional 203 class 14 bidirectional relationship 156 create 248 default 203 dirty 268 direction 7, 203 display associated 214 update 268 filter by lite collections 341 navigate ID 244 direction 211 identifier 52 object 154 in transaction 11 object links 146, 211 in transactions 325 relationship path 14 inheritance discriminator 258 navigation instantiate 341 considerations 268 is locked exception 335 test cases 156 model 6, 7, 8, 14, 32 nested modify 11 transaction 12, 254, 325, 330 navigate 154 transaction commit error 330 navigate links 146, 211 transactions 283 non-persistent 13 nested transaction 317 participating in transaction 11 new picture 341 attribute 51 refresh 11 class 49, 54 remove 305 data store map 227 serving the home 279 model 47 setup 146 table map 228 subset of information 341 new classes, creation of 56 transacted 328 nil-ability transient 13 forward 148 version 13, 39 of forward, non-required one-to-one relation- object ID ship 393 artificial 259 nil-able association 231 in subclass 259 non-persistent object 13 natural 259 non-required object identifier association 231 almost sacrosanct 237 one-to-one association 392 missing error 52 not mapped 207 object model NOT NULL

433 define 14 services generation 146 layer 5 order object modification 11 of SQL executions 276 object version organizer in VisualAge 17 discard 13 out of step 17 merge 13 output ObjectExtender 3 system transcript 361 application layers 5 outside transaction 325 concept 3 outside-in framework 14 consideration 22 global reset 35 path 19, 22, 227 tools 23 overqualified SQL 332 VisualAge Smalltalk xxi override method 394 object-oriented 3 overview concept 257 browser 23 design 19 examples 351 implementation 19 tool 23 modeling 19, 20, 22 overwrite, concurrence 65 view 7 overwrite, concurrent 123 world 257 observe 39 on flight fixing methods 394 P packaging 354 one-to-many page association 7 association 205 relationship 131, 136, 241 for association in property map editor 230 one-to-one 5 for attribute in property map editor 229 association 7 parallel association, non-required 392 change 326 mapping 5 child transaction 325 relationship 137 edit 326 one-to-one association 231 transaction 325 one-to-xxx association 203 parent transaction optimistic shared transaction 12 attributes for locking 332 top-level transaction 12 collision management 11 parse all instances 278 locking 65, 325, 326 part predicate 192, 332 connection list 84 optimistic predicate, locking 65 create 337 optimize implementation 338 access 35 notify 338 data access 35 of predicate 332 option parts list 84 generate persistent classes 55 partial instantiation 341 generate services classes 210, 231 participating, object in transaction 11 generate VisualAge parts option 55 parts list 84 model code generation 111, 135, 209 pass top-level transaction 328 root class for persistence 55 path options backward 19, 20, 105, 195 for model code generation 54, 354

434 Using VisualAge Smalltalk ObjectExtender bottom-up 105, 195, 227 printable reference string 161 development 19, 47, 105, 127, 195, 227 separation model from implementation 270 file 337 precompile SQL 342 forward 19, 47, 127 predicate navigate 14 for optimistic locking 332 outside-in 19, 22, 227 non business attribute 333 top-down 47, 127, 227 optimistic 192, 332 pattern timestamp 333 naming 7 prefix state 257 logical table name 199 performance 268, 278 name 354 access 341 prerequisite 396 inheritance 259 application 136 lite collection 26, 341 for metadata storage applications 354 trade-off vs. resource 270 preview SQL 146 Persistence Builder, VisualAge primary key 50, 52, 108 for Java, Persistence Builder xxi column 108, 197 persistence framework, effect of 83 column type 390 persistence layer 6 define 197 persistent 6 for every subclass 267 class 64, 229 foreign key 270 object version 13 inheritance 267 persistent classes multiple 259 generation option 55 subclass table 270 persistent classes generation option 55 table 144 persistent storage 13 principle of uncertainty 39 pessimistic printable collision management 11 reference string 161 locking 65, 325, 335 process locking enable 66, 325 processes with transactions 325 lock-on-read 335 processor physical 6 SQL 281 data store 15 programming 5 database 8, 61, 69 project name 41, 354 name 28, 130, 198 prompter physical name for error 99 adjust 143 unsaved changes 190 conform 140 proof of concept 22 rule 140 property physical naming support 142 (a) attribute 203 picture (r) association (relation) 203 load from file 345 attribute 52 object 341 map editor 229 policy map for subclass 263 isolation of transaction 325 property map merge 13 (a) for attribute 33 pool for query 279, 399 (r) association (relationship) 33 practice editor 10, 64 convenience methods 161 property map editor

435 for associations 33 printable string 161 for attributes 33 string 164, 170 proposal 22 supplies printable string 161 prove of concept 19 refresh public interface browser 17 define 337 load 17 editor 164, 247, 337 object 11 re-generate model 14 Q model code 137 qualifier, high-level 139, 145, 262 relation 4 query relational custom 277 attribute 134 entry point home 279 database 4, 8, 15, 19, 257 generate static query 342 service object 281 helper class 281 view 7 pool 279, 399 world 257 selective 277 relationship 9 static 342 bidirectional 156 string 279 business object 5 query pool 279 clear cache 35 change SQL 334 column 144 service class 279 define 54 query string 279 editor 28 fix through new inheritance 396 R fix through override 394 RAD 19 foreign key 14, 28 rapid application development 19 forward 137, 231, 393 read framework 14 and copy 326 inheritance 257 CRUD 115, 214 many-to-many 241 image 337 navigate path 14 read-only 164 non-required 393 attribute 161 one-to-many 131, 136 shared transaction 11 one-to-one 137 real-world application 14, 332 specify 14 record object modification 11 subclass 396 recursion to sort changes 391 relationship, define 271 recursive release remove recursively 305 all transactions 35 redundancies in tables 20 model 60 reference 161 remove association 164 object 305 attribute 170 recursively 305 circular 147 remove... method 136 derive 161 repeatable-read display 170, 214 default 326 form 178 isolation policy 325 printable 161 repeatableRead transaction status 39

436 Using VisualAge Smalltalk ObjectExtender repository role 7, 244 development 17 association 4, 131 metadata 16, 17 edit name 203 required name 203, 208 adjustments of generated associations 203 rollback 13, 39, 120 association 131 root association to self 391 class 398 attribute value 203 default persistence class option 55 not default 203 of inheritance tree 262 NOT NULL 145 of the model hierarchy 262 self-association 148 root/leaf inheritance table map 272 requirement 19 table 398 case study 378 row reset ObjectExtender 35 class indicator 258 reset, general ObjectExtender 61 delete from table 268 resolution discriminator 258 collision 333 heavy 26 implementation on collisions 332 insert into table 268 resource large set 26 lock 11 subclass indicator 258 sort 268 update in table 268 trade-off vs. performance 270 rule responsibility for consistency 389 business 14 result physical name 140 collect from query 279 runtime 6, 15 generate 19 metadata 354 large set 278 set locking strategy 325 retrieve data 5 row set 26 S sample 3 reusable form 170 database 105, 195, 231 drag-drop support 171 scenario 41 single reference 171 test 246 reuse sample objects setup 146 collect result method 280 save 16 model 227 map 69, 145, 230 schema 227, 278 metadata 16 reverse-engineer 8 model 50, 60 revert schema 64, 107 map 17 save-yes/no/cancel 400 metadata 17 scenario model 17 development 3 schema 17 employee and department 41 review library 41, 42 map 64 sample 41 model 203 schema 6, 8, 15 model code 136 adjust 197 schema 139, 197 browser 8, 15, 22, 27, 61, 106, 109, 146, 198, services classes 210

437 344 generate 137, 146, 304 capture 20 generate classes 210, 231 collision management 11 generate classes options 231 create 8 generation options 69 database 32 inheritance 264, 273 deferred write 11 inspect classes 210 define 244, 260, 270, 341 layer 6 export to database 146 name 300 foreign key relationship 197 review classes 210 generate 8, 19, 57, 61, 137, 138, 139, 260, 302 services classes 32 import 20, 106, 196 generate 20, 69, 111, 146 load 16 generation option 146 load available 139 name 129 map 227 session debug 35 map to model 227 set name 106, 203, 300 foreign key 136 reuse 227, 278 key 136 revert 17 set selector 164 review 139, 197 set, large set of large rows 26 save 64, 107 setter 136 textual summary 64 generated 39 scope of transaction 325 setup scratch sample objects 146 develop from scratch 19 script for objects 148 scratch edition 231 shared transaction 11, 83, 116 script 361 global 11 application 15 parent transaction 12 generate 15 read-only 11 generate default scripts 338 sibling transactions 325 object setup 148 side of foreign key relationship 207 test 211, 309, 361 single reference verify local image data store 58 drag-drop support 171 VisualAge editor 37 form 178 select reusable form 171 class for generation 56 single table inheritance 258 table 196 single table inheritance map 262 selection list 115 appropriate 259 selective generation 54 drawback 258 selective query 277 for all subclasses 263 self-association, required 148 single-window GUI 161 self-referencing, map entry 207 Smalltalk 3 sequence of generation 137 BLOBs 342 service sort class 279 resource 268 object collects the query result 279 source data 106 relational service object 281 specify relationship 14 services 6 speed application name 112 access 270 class 58, 210 to access database with inheritance 259

438 Using VisualAge Smalltalk ObjectExtender spider web 178 storage name 110 SQL 39, 146, 278 strategy DDL 146 change dynamically 325 dynamic 342 for locking 325 error 100 string error -530, constraint violation with foreign convert to image 337 key 399 printable string for reference 161 error for collision detection 333 query 279 execution order 276, 399 reference 164, 170 foreign key 399 SQL 281 generate 146 subclass key for query string 281 all subclasses in one table 258 overqualified 332 discriminator 258 precompile 342 inheritance 257 preview 146 naming 29 processor 281 object ID 259 queries for inheritance 259 primary key 267 static 342 property map 263 string 281 relationship 396 update 332 separate table 269, 398 use of CURRENT TIMESTAMP 334 single table inheritance map for all subclasses SQL query string 279 263 standard table 270 debugger 77 subset inspector 77 of all instances 278 start ObjectExtender and tools 23 of information of an object 341 state pattern 257 subtransaction 12 state pattern design 44, 257, 387 level 12 state-of-the art 19 user interface 12 static suffix generate query 342 for names 354 query 342 summary SQL 342 model 109 statistics of transactions 87, 123 of your first samples 237 status top-down 102 browser 35, 75, 87, 113, 123 transactions 336 tool 16 superclass status browser change 354 monitor 75 editor 260 view menu 35 inheritance 260 storage 5, 6 support class 37 hasModifications 400 metadata 16, 354 physical naming 142 storage application name survival 389 model 48 switch transaction 13 storage class synchronize transaction 11 comment 37 syntax for host-variable 279 metadata 17 system diagram name for model 48 case study 385

439 system overview of the library management sys- of model 24 tem 42 schema 64 System Transcript 303 time system transcript 36, 96, 98, 114, 146, 267, 361 development 6 tool 23 runtime 6 systemwide sharedTransaction 87 timestamp 65, 333 attribute 334 of database for collision detection 334 T predicate 333 table 7, 28 tool 3 column 9 browser 6 containing all subclasses 258 menu 23 create 146 ObjectExtender 23 create map 228 overview 23 database 5, 8, 9, 146 start 23 editor 62, 108, 197 status 16 foreign key 139, 144 system transcript 23 foreign key relation ship 389 top-down 19 large 278 combined with bottom-up 22 leaf 398 conclusion 102 map 64, 229 employee 47 map inheritance tree 260 path 47, 127, 227 multiple for attributes 269 summary 102 name 139, 140 top-level transaction 12, 13, 83 prefix of logical name 199 parent transaction 12 primary key 144 passed 328 redundancies 20 trace 36, 280 root 398 basic 37, 274 select 196 detail 114 separate for each subclass 269, 398 detailed 274 single table inheritance mapping 258 level 274 space waste 258 message 56 subclass 270 parameter definition 36 with complex dependencies 20 trade-off performance vs. resource 270 table and column name lengths 140 transacted table map with root/leaf inheritance 272 business object 328 tasks at development time 14 contents of a variable 328 test 309 home 116 headless 113, 146, 211, 246, 264, 273, 282 object 328 monitor headless 75 variable 13, 328 navigation case 156 variable contents 328 sample 246 transaction 5, 11, 15, 325, 332 script 211, 309, 361 active 11 with GUI 122 attribute of variable 328 with user interface 96 basic parts 83 textual summary businessTransaction 87, 95, 119, 296 documentation 22 changed objects 325 map 208 characteristics 336 model 52, 109 child 12, 325, 328

440 Using VisualAge Smalltalk ObjectExtender child transactions 287 access performance 341 collision 11 database access 19 commit 268 type concurrent 325 change column type 389 conflict 11 column 62, 139, 143, 229 context 38, 116 compatible 229 current 13 converter 30 divide (if large) 12 details 30 enhancedBusinessTransaction 87 key column 144 enhancement 400 user-defined 337 error 100 types 337 failure 332 form to display name 405 framework 15, 83 U UML 3, 7 global 11 IBM VisualAge UML Designer (modeling tool) ID 405 19 in different processes 325 uncertainty principle 39 in view 83 uncommitted changes 325 initialize 328 unfinished association 389 isolation 312 unintrusive inspector 40 lazy initialize in businessTransaction 96 unit of work, consistency 5 level 12 unrepeatable-read level in nesting 13 isolation policy 325, 335 name 405 unsaved nested 12, 254, 283, 317, 325, 330 changes 190, 400 on different machines 325 changes prompter 190 outside 325 update 5, 12 parent 12 CRUD 115, 214 release all 35 navigable 268 save GUI 190 row in table 268 scope 325 SQL 332 shared 11, 83, 116 update counter 65 siblings 325 updateSqlString statistics 87, 123 with CURRENT TIMESTAMP 334 status repeatableRead 39 use case summary 336 case study 381, 382 switch 13 use case diagram synchronize 11 case study 385 systemwide shared 87 use cases in library 42 top-level 12, 83 user top-level commit 326 defined types 337 tree 286 helper for queries 281 tree re-creation 286 interface 5 variable 13 user code insertions 14 transient user interface generation option 69 consideration 12 object 13 for test 96 tree of transactions 286 subtransaction 12 tune

441 user prompter 99 wasting table space 258 utility WHERE clause 278 list connections 405 work cooperative 22 service object 279 V workspace 361 validation 14 world error 305 object-oriented 257 validation error 97 relational 257 validation error in metadata 207, 229 write discriminator to data store 263 value appropriate for discriminator 263 attribute 38 Y attribute value not required 203 Y2K 265, 266 discriminator 263 VapModelBrowserControl edit association 389 Z zap 389 VapSchemaColumnEditorModel valid foreign key 389 variable transacted 13, 328 transaction attribute 328 verify with scripts 58 version business object 39 class 16 model 60 object 13 persistent object 13 version 16 view create with BLOB 345 create with lite collection 342 data store 15 detail 122 drag-drop support 170 list 122 object-oriented 7 relational 7 with businessTransaction part 90 with transaction 83 view menu of status browser 35 visibility of changes anywhere else 326 VisualAge 3 organizer 17 Smalltalk, ObjectExtender xxi

W warning about inconsistent keys 390

442 Using VisualAge Smalltalk ObjectExtender ITSO Redbook Evaluation

Using VisualAge Smalltalk ObjectExtender SG24-5258-00

Your feedback is very important to help us maintain the quality of ITSO redbooks. Please complete this questionnaire and return it using one of the following methods: • Use the online evaluation form found at http://www.redbooks.ibm.com • Fax this form to: USA International Access Code + 1 914 432 8264 • Send your comments in an Internet note to [email protected]

Which of the following best describes you? _ Customer _ Business Partner _ Solution Developer _ IBM employee _ None of the above

Please rate your overall satisfaction with this book using the scale: (1 = very good, 2 = good, 3 = average, 4 = poor, 5 = very poor)

Overall Satisfaction ______

Please answer the following questions:

Was this redbook published in time for your needs? Yes___ No___

If no, please explain:

What other redbooks would you like to see published?

Comments/Suggestions: (THANK YOU FOR YOUR FEEDBACK!)

© Copyright IBM Corp. 1999 443 SG24-5258-00 VisualAge ObjectExtenderUsing Smalltalk Printed in the U.S.A. SG24-5258-00