Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and Java Server Pages Ueli Wahli, Greg Behrend, Daniel Peter International Technical Support Organization www.redbooks.ibm.com SG24-5426-01 SG24-5426-01 International Technical Support Organization VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and Java Server Pages March 2000 Take Note! Before using this information and the product it supports, be sure to read the general information in Appendix A, “Special Notices” on page 429. Second Edition (March 2000) This edition applies to VisualAge for Java Enterprise Version 3 for use with the Windows NT Operating System. It should also apply to VisualAge for Java Enterprise Version 3 running on OS/2 or AIX Operating Systems. 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 2000. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxi The Team That Wrote This Redbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxii Comments Welcome . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiii Sample Code on the Internet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiii Part 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 Chapter 1. VisualAge for Java Version 2 and Version 3 . . . . . . . . . 3 VisualAge for Java Professional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Support for Java Development Kit 1.1.6 and 1.1.7. . . . . . . . . . . . . . . . . . 4 New Integrated Development Environment Features . . . . . . . . . . . . . . . 4 New Visual Composition Editor Features. . . . . . . . . . . . . . . . . . . . . . . . . 4 JavaBeans for Easy Access to Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 New Debugger Enhancements in Version 3 . . . . . . . . . . . . . . . . . . . . . . . 5 VisualAge for Java Enterprise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Java Team Programming Support. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Source Code Management Tools Integration . . . . . . . . . . . . . . . . . . . . . . 6 Open Tool Integrator APIs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Enterprise Toolkits for Workstation, AS/400, and OS/390 . . . . . . . . . . . 7 Enterprise Access Builders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Persistence Builder. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Servlet Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 IDL Development Environment. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Support for SanFrancisco, Tivoli, Lotus, and Component Broker . . . . . . 9 AIX Development Environment. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 VisualAge for Java 2.1 Enterprise Enhancements . . . . . . . . . . . . . . . . . . . . 9 VisualAge for Java Version 3 Enterprise Enhancements . . . . . . . . . . . . . . 10 Chapter 2. Environment Setup and Installation Instructions . . 11 Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Installation of VisualAge for Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 WebSphere Test Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 DB2 Access from VisualAge for Java. . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Installation of the ITSO Sample Applications . . . . . . . . . . . . . . . . . . . . . . . 14 © Copyright IBM Corp. 2000 iii Sample Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Repository Export Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Install DB2 Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Persistence Builder Code Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Part 2. VisualAge for Java Persistence Builder . . . . . . . . . . . . . . . .19 Chapter 3. Persistence Builder Concepts . . . . . . . . . . . . . . . . . . . . . 21 Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 Application Layers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Persistence Layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 Object Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 Schemas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Transactions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Shared Transaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Top-Level Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Nested Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Transacted Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 Commit and Rollback of Transactions. . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Metadata Storage. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Development Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Forward or Top-Down: Start with the Object Model . . . . . . . . . . . . . . . 36 Backward or Bottom-Up: Start with the Database . . . . . . . . . . . . . . . . 38 ‘Outside-in’ or Mapping: Map Object Model to Database. . . . . . . . . . . . 39 Chapter 4. Persistence Builder Tools . . . . . . . . . . . . . . . . . . . . . . . . . 41 Model Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Supporting Editors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Generating a Schema and a Map from a Model . . . . . . . . . . . . . . . . . . . 46 Schema Browser. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Supporting Editors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Map Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 Supporting Editors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 The SQL Query Tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 The Status Tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Tracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Chapter 5. Simple Object Model: Customer . . . . . . . . . . . . . . . . . . . 57 Forward Development Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Define the Model Metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Generate Domain Classes and Interfaces . . . . . . . . . . . . . . . . . . . . . . . . 64 iv VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Generated Domain Classes and Interfaces . . . . . . . . . . . . . . . . . . . . . . . 65 Generate Workspace Schema. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Generate Service Classes for Workspace Schema . . . . . . . . . . . . . . . . . 67 Generated Service Classes for Workspace Schema . . . . . . . . . . . . . . . . 68 Verify the Generated Classes in the Workspace Schema. . . . . . . . . . . . 69 Generate Database Schema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Generate the DDL and the Database Tables . . . . . . . . . . . . . . . . . . . . . 76 Generate Service Classes for Relational SQL Schema . . . . . . . . . . . . . . 79 Generated Service Classes for Relational SQL Schema. . . . . . . . . . . . . 81 Activate a Datastore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Verify the Generated Classes for Relational SQL Schema . . . . . . . . . . 82 SQL Query Tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 Persistence Status Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 Backward Development Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 Import Schema from Relational Database . . . . . . . . . . . . . . . . . . . . . . . 86 Generate Model Definition and Mapping . . . . . . . . . . . . . . . . . . . . . . . . 88 Generate Classes for the Model and Services . . . . . . . . . . . . . . . . . . . . . 89 Verify the Generated Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 Visual Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Retrieve Customer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Edit and Save a Customer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Create and Delete a Customer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 Customer List Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Chapter 6. Simple Associations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 Forward Development Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 Define the Model Metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 Generate Domain Classes and Interfaces . . . . . . . . . . . . . . . . . . . . . . . 108 Generated Domain Classes and Interfaces . . . . . . . . . . . . . . . . . . . . . . 108 Generate Database Schema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 Generate the DDL and the Database Tables . . . . . . . . . . . . . . . . . . . . 111 Generate Service Classes for Relational SQL Schema . . . . . . . . . . . . . 111 Verify the Generated Classes for Relational SQL Schema . . . . . . . . . 111 Regenerate Service Classes for the Workspace Schema . . . . . . . . . . . 113 Backward Development Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 Alter Schema from Relational Database . . . . . . . . . . . . . . . . . . . . . . . . 114 Generate Model Definition and Mapping . . . . . . . . . . . . . . . . . . . . . . . 115 Generate Code to Support Model and Services. . . . . . . . . . . . . . . . . . . 117 Verify the Generated Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 Visual Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 Restriction for Associations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 Preface v Chapter 7. Enhance Business Object Model . . . . Initialize Beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Add Methods and Business Rule Validation . . . . . . . . . Add Transient Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ....... ....... ....... ....... ...... ...... ...... ...... . . . . 123 123 124 126 Chapter 8. Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 Nested Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 Concurrent Transactions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 Isolation Policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 Non-Locking Implementation of Repeatable Read . . . . . . . . . . . . . . . . 133 Non-Locking Implementation of Unrepeatable Read . . . . . . . . . . . . . . 133 Conflict Detection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 Optimistic Predicate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 Optimistic Predicate with Timestamps . . . . . . . . . . . . . . . . . . . . . . . . . 140 Pessimistic Locking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 Facts About Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 BusinessTransaction Bean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 Methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 Visual Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Concurrent Top-Level Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Nested Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 Experiments with Locking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 Chapter 9. Many-to-Many Associations . . . . . . . . . . . . . . . . . . . . . . 165 Define the Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 Define the Schema and Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 Implement Association Methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Implement Custom Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Implement the Card Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Implement the BankAccount Methods . . . . . . . . . . . . . . . . . . . . . . . . . 177 Test Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 Scrapbook Examples. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 Visual Programming: Applet Example . . . . . . . . . . . . . . . . . . . . . . . . . 180 Chapter 10. Inheritance . . . . . . . . . Single Table Inheritance Mapping . . . . Define the Model. . . . . . . . . . . . . . . . Define the Schema and Mapping. . . Test Examples. . . . . . . . . . . . . . . . . . Multiple Table Inheritance Mapping . . Define the Model. . . . . . . . . . . . . . . . Define the Schema . . . . . . . . . . . . . . Define the Mapping . . . . . . . . . . . . . vi ....... ....... ....... ....... ....... ....... ....... ....... ....... ...... ...... ...... ...... ...... ...... ...... ...... ...... VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs ....... ....... ....... ....... ....... ....... ....... ....... ....... ...... ...... ...... ...... ...... ...... ...... ...... ...... . 185 . 186 . 187 . 190 . 195 . 198 . 199 . 199 . 200 Test Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 Chapter 11. Complex Mappings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 Secondary Table Mappings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 Define the Model. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Define the Schema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Define the Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 Test Examples. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 Composers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 Define the Attribute Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 Define the Composer Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 Define Schema, Model, and Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Generate the Model and Service Classes . . . . . . . . . . . . . . . . . . . . . . . 216 Test the Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 Converters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 Chapter 12. Custom Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 The Query Pool Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 SQL String Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 Fixed Queries: allInstancesSqlString . . . . . . . . . . . . . . . . . . . . . . . . . . 223 Variable Queries: findByKeySqlString . . . . . . . . . . . . . . . . . . . . . . . . . 224 Query Methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 Fixed Queries: allInstancesQuery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 Variable Queries: findByKeyQuery . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 Pessimistic Locking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 Example of a Fixed Custom Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 Implement the SQL String Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 Implement the Query Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 Implement a Retrieval Method in the Home Class . . . . . . . . . . . . . . . 233 Implement Methods for Pessimistic Locking . . . . . . . . . . . . . . . . . . . . 233 Test the Fixed Custom Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 Example of a Variable Custom Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 Implement the SQL String Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 Implement the Query Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236 Implement a Retrieval Method in the Home Class . . . . . . . . . . . . . . . 237 Methods for Pessimistic Locking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237 Test the Variable Custom Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 Use Custom Queries in Visual Composition . . . . . . . . . . . . . . . . . . . . . . . 239 GUI Example with Custom Query. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 Run the GUI Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 Custom Query Enhancements in Version 3 . . . . . . . . . . . . . . . . . . . . . . . . 241 Chapter 13. Performance Considerations . . . . . . . . . . . . . . . . . . . . 243 Lite Collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 Preface vii Define the Model, Schema, and Mapping . . . Guidelines for Lite Collections. . . . . . . . . . . . Applet to Test the Lite Collection . . . . . . . . . Preload Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . ...... ...... ...... ...... ....... ....... ....... ....... . . . . . . . 244 . . . . . . . 247 . . . . . . . 248 . . . . . . . 251 Chapter 14. Persistence Builder Enhancements in Version 3 . . 253 Persistence Builder Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 Model, Schema, and Map Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 Generated Code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 Persistence Builder New Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 Refresh from Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 Commit Failure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 WebSphere Connection Pools. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 Database Connection with User ID and Password. . . . . . . . . . . . . . . . 257 Custom Query Enhancements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 Multithreading Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 Persistence Builder Code Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263 Part 3. ITSO Bank Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .265 Chapter 15. ATM Application Requirements and Database. . . . 267 ATM Application Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268 ITSOBANK Database Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . 270 Relationships . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 Database Definition DDL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 DB2 Userid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 Sample Data of ITSOBANK Tables. . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 Chapter 16. ATM Application Business Model and Controller . 281 Application Design. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 Application Layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 Application Layer Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 Business Object Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284 Reverse-Engineer the Business Object Layer . . . . . . . . . . . . . . . . . . . . . . 286 Import the Database Tables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 Update the Foreign Key Relationship . . . . . . . . . . . . . . . . . . . . . . . . . . 289 Generate the Business Model from the Schema . . . . . . . . . . . . . . . . . . 290 Adjust the Mapping for Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 Tailor String Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 Generate the Domain Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298 Generate the Service Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 Save the Model, Schema, and Mapping. . . . . . . . . . . . . . . . . . . . . . . . . 301 Extend the Business Object Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301 viii VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Extend the Business Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302 Persistence Layer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309 Application Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310 Controller Methods and Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310 Controller and Business Model Interaction . . . . . . . . . . . . . . . . . . . . . 311 Implement the Controller. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 Test the Controller and the Business Objects . . . . . . . . . . . . . . . . . . . . . . 318 Chapter 17. Swing GUI for ATM Application . . . . . . . . . . . . . . . . . 319 Design of the GUI Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 Application Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 Panel Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 Implementation of the Application Panels. . . . . . . . . . . . . . . . . . . . . . . . . 323 Card Panel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 PIN Panel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324 Select Account Panel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326 Transaction Panel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328 ATM Applet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330 Run the ATM GUI Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331 Chapter 18. ATM Application Using Servlets . . . . . . . . . . . . . . . . . 333 Create a Skeleton Controller Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334 Servlet Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334 Card Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334 PIN Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338 Account Servlet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 Transaction Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 Thank You Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349 Application Flow Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350 Implement the Controller Servlet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352 Controller Servlet Total Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352 Preparation for Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 Customer Verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354 PIN Verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356 Account Selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357 Deposit Transaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358 Withdraw Transaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360 Query Transaction History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 Termination and Restart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362 Disable Caching of the Output HTML . . . . . . . . . . . . . . . . . . . . . . . . . 363 Test the ATM Servlet Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364 Test with the WebSphere Application Server. . . . . . . . . . . . . . . . . . . . 364 Preface ix Test with the Servlet Runner HTTP Server . . . . . . . . . . . . . . . . . . . . . 365 WebSphere Application Server or Servlet Runner? . . . . . . . . . . . . . . . 365 Deploy Servlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365 Chapter 19. ATM Application Concurrent Processing . When Does the Problem Occur? . . . . . . . . . . . . . . . . . . . . . . . . . . How to Force the Problem? . . . . . . . . . . . . . . . . . . . . . . . . . . . How to Solve the Problem?. . . . . . . . . . . . . . . . . . . . . . . . . . . . Retrieve and Compare Account Information . . . . . . . . . . . . . . . . Pessimistic Locking of Accounts . . . . . . . . . . . . . . . . . . . . . . . . . . Optimistic Predicate for the Account Balance . . . . . . . . . . . . . . . . . . . . . . 367 . . . . . . . 368 . . . . . . . 368 . . . . . . . 368 . . . . . . . 369 . . . . . . . 370 . . . . . . . 371 Chapter 20. ATM Application Using Java Server Pages . . . . . . . 375 Java Server Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 JSPs and Servlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 How Does a JSP Run? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 JSP Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 JSP Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 Complete JSP Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 JSP with a JavaBean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382 JSP Account Listing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 Compile and Run JSPs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387 JSP Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388 Implement the ATM Application with JSPs . . . . . . . . . . . . . . . . . . . . . . . 391 Design the JavaBeans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392 Design the JSPs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394 Implement the JSP Controller Servlet . . . . . . . . . . . . . . . . . . . . . . . . . 399 Servlet Configuration File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406 Test the ATM JSP Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408 Chapter 21. Deployment of Applications, Applets, Servlets, and Java Server Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409 Deployment of Applications. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410 Export an Application from VisualAge for Java . . . . . . . . . . . . . . . . . . 410 Deployment Process for Applications . . . . . . . . . . . . . . . . . . . . . . . . . . 410 Deployment of Applets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412 Export Applets from VisualAge for Java. . . . . . . . . . . . . . . . . . . . . . . . 412 Deployment Process for Applets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412 Deployment of Servlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414 Deployment of Servlets for WebSphere. . . . . . . . . . . . . . . . . . . . . . . . . 415 WebSphere Version 2 Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 WebSphere Version 3 Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420 Deployment of JSPs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424 Deployment of Applications with Swing. . . . . . . . . . . . . . . . . . . . . . . . . . . 425 x VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Tailor the Web Browser. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425 Appendixes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .427 Appendix A. Special Notices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429 Appendix B. Related Publications . . . . . . . . . . . . . . . . . . . . International Technical Support Organization Publications . . . Redbooks on CD-ROMs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Other Publications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ...... ...... ...... ...... . . . . 433 434 435 435 How to Get IBM Redbooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437 IBM Redbook Fax Order Form. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438 List of Abbreviations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441 IBM Redbooks Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 Preface xi xii VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figures 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. WebSphere Test Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Class Diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Application Layers and Components . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Components of Persistence Layers for Development and Runtime . . . 25 Association Editor: Association Customer-BankAccount . . . . . . . . . . . 27 Foreign Key Relationship Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Property Map Editor with Class Attribute to Table Column Mapping 29 Property Map Editor with Association to Foreign Key Mapping . . . . . 29 Example of a Nested Transaction Tree . . . . . . . . . . . . . . . . . . . . . . . . . 32 Commit and Rollback of Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . 33 VisualAge Persistence Builder: Frameworks . . . . . . . . . . . . . . . . . . . . 34 Development Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 VisualAge for Java Persistence Tools Menu . . . . . . . . . . . . . . . . . . . . . 42 Persistence Builder Model Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Model Browser: Class Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Model Browser: Attribute Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Model Browser: Association Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 Persistence Builder Schema Browser . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Schema Browser: Table Editor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Schema Browser: Column Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Schema Browser: Foreign Key Relationship Editor . . . . . . . . . . . . . . . 50 Persistence Builder Map Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Map Browser: Property Map Editor. . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 VisualAge Persistence SQL Query Tool. . . . . . . . . . . . . . . . . . . . . . . . . 53 VisualAge Persistence Status Query Tool . . . . . . . . . . . . . . . . . . . . . . . 54 Class Diagram for the Simple Model . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Model Browser: Create a New Model. . . . . . . . . . . . . . . . . . . . . . . . . . . 59 Model Browser: Create a New Class . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 Class Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Model Browser: Create a New Attribute . . . . . . . . . . . . . . . . . . . . . . . . 60 Attribute Editor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Model Browser: Customer Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Class Editor with Object Identifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 Model Browser with Unsaved Model . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Save Model SmartGuide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Generation Options SmartGuide for Domain Classes and Interfaces . 64 Generated Domain Classes and Interfaces for the Customer Class . . 65 Generation Options SmartGuide for Workspace Schema. . . . . . . . . . . 68 © Copyright IBM Corp. 2000 xiii 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. xiv Generated Service Classes for the Simple Workspace Schema . . . . . . 68 Code Skeleton for Datastore and Transaction. . . . . . . . . . . . . . . . . . . . 69 Scrapbook: Local Database Test (SimpleLocal.scrap). . . . . . . . . . . . . . 70 Schema Browser with Generated Simple Schema . . . . . . . . . . . . . . . . 72 Table Editor for the Customer Table . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Column Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Map Browser: SimpleSimple Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Schema Browser: Generated DDL Script . . . . . . . . . . . . . . . . . . . . . . . 77 Database Connection Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Console Showing Results of Creating the Database Tables . . . . . . . . . 78 Generation Options for Service Classes. . . . . . . . . . . . . . . . . . . . . . . . . 80 Generated Service Classes for the Relational SQL Schema . . . . . . . . . 81 Conceptual View of Activating a Datastore. . . . . . . . . . . . . . . . . . . . . . 82 Scrapbook: Database Retrieve and Update (SimpleUpdate.scrap) . . . 83 Scrapbook: Database Retrieve and Delete (SimpleDelete.scrap) . . . . . 83 Scrapbook:Retrieve All Customers (SimpleReadAll.scrap) . . . . . . . . . 84 SQL Query Tool. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 Select Tables Dialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 Schema Browser: Imported SimpleBU Schema . . . . . . . . . . . . . . . . . . 87 Model Browser: Generated SimpleBU Model . . . . . . . . . . . . . . . . . . . . 88 Map Browser: Generated SimpleBUSimplebu Map . . . . . . . . . . . . . . . 89 Scrapbook: Retrieve All Customers (SimpleBUReadAll.scrap) . . . . . . 90 Scrapbook: Find a Customer (SimpleLocalFind.scrap). . . . . . . . . . . . . 91 Visual Composition: RetrieveCustomerView Class . . . . . . . . . . . . . . . 92 Persistence Builder Palette: ReadOnlyTransaction . . . . . . . . . . . . . . . 92 Retrieve Customer View with Edit and Save . . . . . . . . . . . . . . . . . . . . 95 Persistence Builder Palette: TopLevelTransaction . . . . . . . . . . . . . . . . 95 Retrieve Customer View with Create and Delete . . . . . . . . . . . . . . . . . 97 Customer List Using AWT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Customer List Using AWT and AwtListModel . . . . . . . . . . . . . . . . . . . 99 Customer List Using a Swing Table . . . . . . . . . . . . . . . . . . . . . . . . . . 100 Customer Table Using Swing and VapDefaultTableModel . . . . . . . . 101 ColumnIdentifier Editor for VapDefaultTableModel . . . . . . . . . . . . . 102 Class Diagram for the Simple Association Model . . . . . . . . . . . . . . . . 104 Association Editor View Opens with Name Templates . . . . . . . . . . . 105 Association Editor for OwnedAccountsToAccountOwner Association 106 Model Browser with Defined Association . . . . . . . . . . . . . . . . . . . . . . 107 Simple Association: Generated Domain Classes and Interfaces . . . . 108 Customer and BankAccount Interface Methods . . . . . . . . . . . . . . . . . 109 Foreign Key Relationship Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 Scrapbook: Customers–BankAccounts (SimpleAssocInsert.scrap) . . 112 Scrapbook: Customers–BankAccounts (SimpleAssocReadAll.scrap) . 113 Association Editor: Generated Association . . . . . . . . . . . . . . . . . . . . . 115 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 82. Map Browser: Broken Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 83. Property Map Editor: Association Not Mapped. . . . . . . . . . . . . . . . . . 116 84. Scrapbook: Customers–BankAccounts (SimpleAssocBU.scrap). . . . . 118 85. Retrieve Customer View with Owned Accounts . . . . . . . . . . . . . . . . . 119 86. Association Restriction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 87. OverdrawException Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 88. Enhancements in the BankAccount Interface. . . . . . . . . . . . . . . . . . . 125 89. Enhancements in the BankAccountBean Class . . . . . . . . . . . . . . . . . 125 90. Enhancements in the BankAccountImpl Class. . . . . . . . . . . . . . . . . . 126 91. Scrapbook: Nested Transactions (Nested.scrap) . . . . . . . . . . . . . . . . . 130 92. Scrapbook: Transaction Isolation Policies (IsolationPolicies.scrap). . 134 93. Scrapbook: Conflict Detection (VapMergeFailure.scrap) . . . . . . . . . . 136 94. Scrapbook: Optimistic Locking (OptimisticLocking1.scrap). . . . . . . . 139 95. Scrapbook: Optimistic Predicate with Timestamp (Timestamp.scrap) . 141 96. Persistence Builder Palette: BusinessTransaction. . . . . . . . . . . . . . . 147 97. Customer Details View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 98. Enhanced CustomerSwingTableView . . . . . . . . . . . . . . . . . . . . . . . . . 151 99. Status Browser: Transaction Statistics . . . . . . . . . . . . . . . . . . . . . . . . 154 100. Account Details View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 101. Accounts of One Customer Panel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 102. CustomerDetailsView Enhanced . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 103. Customer–BankAccount Sample GUI Scenario . . . . . . . . . . . . . . . . . 161 104. Transaction Statistics: Sibling Nested Transactions . . . . . . . . . . . . . 162 105. Banking Example: Card and BankAccount Analysis View . . . . . . . . 166 106. Banking Example: Card and BankAccount Design View . . . . . . . . . . 166 107. Association between Card and CardAccount. . . . . . . . . . . . . . . . . . . . 168 108. Association between BankAccount and CardAccount. . . . . . . . . . . . . 169 109. Class Editor: CardAccount . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 110. Foreign Key Relationship Editor: Defining a Physical Name . . . . . . 171 111. Card Interface Method Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 112. CardImpl Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 113. CardBean Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 114. Scrapbook: Display BankAccounts of a Card (FindAccounts.scrap) . 178 115. Add a Card and Associate BankAccounts (AddCard.scrap) . . . . . . . . 179 116. Deleting a Card and Associations (RemoveCard.scrap) . . . . . . . . . . . 180 117. Applet Design for Many-To-Many Association . . . . . . . . . . . . . . . . . . 181 118. Applet Run for Many-To-Many Association . . . . . . . . . . . . . . . . . . . . 183 119. BankAccount with Inheritance: Checking and Savings . . . . . . . . . . . 186 120. Single Table Inheritance Mapping. . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 121. Class Editor: Definition of a Subclass . . . . . . . . . . . . . . . . . . . . . . . . . 189 122. Model Browser: Collapse and Expand of Subclasses . . . . . . . . . . . . . 189 123. Customer to BankAccount Association . . . . . . . . . . . . . . . . . . . . . . . . 190 124. Column Editor: Account Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Figures xv 125. Single TableInheritance Map for BankAccount . . . . . . . . . . . . . . . . . 193 126. Tailored Table Maps for Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . 194 127. Scrapbook: Inheritance Home Classes (InheritHomes.scrap) . . . . . . 196 128. Scrapbook: Inheritance Create Checking (CreateChecking.scrap) . . 197 129. Multiple Table Inheritance Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . 198 130. Foreign Key Relationship Editor: Customer and BankAccount. . . . . 200 131. Root/Leaf Inheritance Table Map: BankAccount . . . . . . . . . . . . . . . . 201 132. Root/Leaf Inheritance Table Map: Checking . . . . . . . . . . . . . . . . . . . . 202 133. Root/Leaf Inheritance Table Map: Checking . . . . . . . . . . . . . . . . . . . . 202 134. Secondary Table Mapping: Database Table Model . . . . . . . . . . . . . . . 206 135. Customer and CustomerData Foreign Key Relationship . . . . . . . . . . 208 136. Complex Mapping: Primary Table Map Properties . . . . . . . . . . . . . . 209 137. Complex Mapping: Secondary Table Map Definition . . . . . . . . . . . . . 210 138. Complex Mapping: Secondary Table Map Properties . . . . . . . . . . . . . 210 139. Scrapbook: Complex Mapping (Complexlist.scrap) . . . . . . . . . . . . . . . 211 140. Composer Attribute Class: CustomerGreeting . . . . . . . . . . . . . . . . . . 213 141. Composer Class: CustomerGreetingComposer . . . . . . . . . . . . . . . . . . 214 142. Complex Mapping: Greeting Attribute . . . . . . . . . . . . . . . . . . . . . . . . 215 143. Complex Mapping: Greeting Attribute . . . . . . . . . . . . . . . . . . . . . . . . 216 144. Scrapbook: Composer Test (Composerlist.scrap) . . . . . . . . . . . . . . . . 217 145. SQL String Method: allInstancesSqlString . . . . . . . . . . . . . . . . . . . . . 223 146. SQL String Method with Parm-Markers: findByKeySqlString . . . . . 224 147. SQL String Method without Parm-Markers: findByKeySqlString . . 224 148. Query Method with Parm-Markers: allInstancesQuery . . . . . . . . . . . 226 149. Query Method of the QueryPool Class: allInstancesQuery . . . . . . . . 227 150. Query Method with Parm-Markers: findByKeyQuery . . . . . . . . . . . . 228 151. Query Method without Parm-Markers: findByKeyQuery . . . . . . . . . 230 152. Sql String Method: goldAccountsSqlString . . . . . . . . . . . . . . . . . . . . . 232 153. Query Method: goldAccountsQuery . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 154. Retrieval Method: retrieveGoldAccounts. . . . . . . . . . . . . . . . . . . . . . . 233 155. SQL String Method for Locking: goldAccountsSqlStringForLocking 234 156. Query Method for Locking: goldAccountsQueryForLocking. . . . . . . . 234 157. Scrapbook: Custom Query Gold Accounts (GoldAccounts.scrap) . . . . 235 158. Sql String Method: allByLastNameAndAccountBalanceSqlString . . 236 159. Query Method: allByLastNameAndAccountBalanceQuery . . . . . . . . 236 160. RetrievalMethod: retrieveAllByLastNameAndAccountBalance . . . . 237 161. Locking: allByLastNameAndAccountBalanceSqlStringForLocking . 238 162. Locking: allByLastNameAndAccountBalanceQueryForLocking . . . . 238 163. Scrapbook: Custom Query (RetrieveAllByNameBalance .scrap) . . . . 239 164. GUI that Invokes a Custom Query (CustomQueryView) . . . . . . . . . . 240 165. GUI Run with Custom Query. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 166. Customer Lite Collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 167. Column Editor: Signature BLOB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246 xvi VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 168. Lite Collection Applet Design. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 169. Lite Collection Applet Run . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249 170. Pre-Load Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 171. Service Class Generation for WebSphere Connection Pool . . . . . . . . 256 172. Transactional Thread Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262 173. Transactional Thread Example Output. . . . . . . . . . . . . . . . . . . . . . . . 263 174. ATM Application Panels and Flow. . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 175. ITSOBANK Tables and Relationships. . . . . . . . . . . . . . . . . . . . . . . . . 270 176. ITSOBANK Database Data Definition Language (Part 1) . . . . . . . . . 274 177. ITSOBANK Database Data Definition Language (Part 2) . . . . . . . . . 275 178. ITSOBANK Database Sample Data Load (Part 1) . . . . . . . . . . . . . . . 279 179. ITSOBANK Database Sample Data Load (Part 2) . . . . . . . . . . . . . . . 280 180. Layers of the ATM Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283 181. Object Model of the ATM Business Object Layer . . . . . . . . . . . . . . . . 284 182. Import Schema Connection Information . . . . . . . . . . . . . . . . . . . . . . . 287 183. Import Schema Select Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 184. Import SchemaResult . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 185. Mixed-Case Names for Foreign Key Relationships. . . . . . . . . . . . . . . 289 186. Generated Business Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290 187. Relationship Tailoring (Before and After) . . . . . . . . . . . . . . . . . . . . . . 291 188. Class Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292 189. Defining the CheckingAccount Class . . . . . . . . . . . . . . . . . . . . . . . . . . 293 190. Account Class withSubclasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 191. Map Browser with Broken Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . 295 192. Single Table Inheritance Mapping Root . . . . . . . . . . . . . . . . . . . . . . . 295 193. Mapping Attributes and Associations . . . . . . . . . . . . . . . . . . . . . . . . . 296 194. Single Table Inheritance Mapping Subclass . . . . . . . . . . . . . . . . . . . . 297 195. Column Converter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298 196. Generate Domain Classes and Interfaces . . . . . . . . . . . . . . . . . . . . . . 299 197. Service Generation Options (Part 1) . . . . . . . . . . . . . . . . . . . . . . . . . . 300 198. Service Generation Options (Part 2) . . . . . . . . . . . . . . . . . . . . . . . . . . 300 199. Controller and Persistence Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . 312 200. Scrapbook for Testing the Controller and the Business Model . . . . . 318 201. ATM Application Panels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 202. GUI Application with Application Controller . . . . . . . . . . . . . . . . . . . 321 203. Visual Composition of the Card Panel . . . . . . . . . . . . . . . . . . . . . . . . . 323 204. Visual Composition of the PIN Panel . . . . . . . . . . . . . . . . . . . . . . . . . 324 205. Visual Composition of the Select Account Panel. . . . . . . . . . . . . . . . . 326 206. Visual Composition of the Transaction Panel . . . . . . . . . . . . . . . . . . . 328 207. Visual Composition of the ATM Applet . . . . . . . . . . . . . . . . . . . . . . . . 330 208. Card Servlet View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335 209. Card Servlet Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 210. PIN Servlet View. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338 Figures xvii 211. PIN Servlet Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 212. Account Servlet View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 213. Account Servlet Design. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342 214. Transaction Servlet View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 215. Transaction Servlet Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346 216. Thank You Servlet View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349 217. Servlet Application Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350 218. Controller Servlet Total Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352 219. Initializing the Controller Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354 220. Customer Verification. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355 221. PIN Verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356 222. Account Selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358 223. Deposit Transaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 224. Withdraw Transaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360 225. Query Transaction History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 226. Termination and Restart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362 227. Disabling Caching for the ATM Servlets . . . . . . . . . . . . . . . . . . . . . . . 364 228. Enable Pessimistic Locking for the Account Classes . . . . . . . . . . . . . 370 229. Enable Optimistic Predicate for the Balance Property. . . . . . . . . . . . 372 230. Customer Listing JSP Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380 231. Customer Listing JSP Source (ItsobankCustlist.jsp) . . . . . . . . . . . . . 381 232. Customer List Formatting JSP Source (ItsobankCustbean.jsp) . . . . 385 233. Account Listing JSP Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386 234. Account Listing JSP Output. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387 235. JSP Execution Monitor Option. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389 236. JSP Execution Monitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390 237. ATM Application: JSP Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 238. Card View JSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 239. Card View JSP Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 240. PIN View JSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396 241. Account View JSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397 242. Transaction View JSP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398 243. Thank You View JSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399 244. JSP Controller Servlet Configuration File . . . . . . . . . . . . . . . . . . . . . 407 245. VisualAge for Java Jar Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410 246. Deployment Process for Applications. . . . . . . . . . . . . . . . . . . . . . . . . . 411 247. Deployment Process for Applets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413 248. Deployment Process for Servlets to WebSphere . . . . . . . . . . . . . . . . . 415 249. WebSphere Application Server Version 2: Administration . . . . . . . . 417 250. WebSphere Application ServerVersion 2: Introduction . . . . . . . . . . . 418 251. WebSphere Application Server Version 2: Java Engine Setup . . . . . 419 252. WebSphere Application Server Version 3: Administration Client . . . 422 253. WebSphere Application Server Version 3: Application Setup . . . . . . 422 xviii VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Tables 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. Directories and Files with Sample Code . . . . . . . . . . . . . . . . . . . . . . . . . 15 Models and Packages in the Repository Samples. . . . . . . . . . . . . . . . . . 16 Attributes of Customer Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Columns for the Customer Table. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Attributes of BankAccount Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 Cardinalities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 Attributes of Card and BankAccount . . . . . . . . . . . . . . . . . . . . . . . . . . 167 Physical Name Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Attributes of Customer and BankAccount . . . . . . . . . . . . . . . . . . . . . . 188 Attributes of Checking and Savings . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 Physical Name Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 Schema Table and Column Definitions. . . . . . . . . . . . . . . . . . . . . . . . . 199 Complex Mapping: Attributes of Customer . . . . . . . . . . . . . . . . . . . . . 207 Complex Mapping: Primary Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Complex Mapping: Secondary Table . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 Converters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 Stream Converters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 Attributes of Customer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 Bank Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 Customer Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 Account Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 Card Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272 CardAccount Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272 Transrecord Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272 Policy Table. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272 Bank Table Sample Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 Customer Table Sample Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 Card Table Sample Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 Account Table Sample Data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 Transaction Table Sample Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 CardAccount Table Sample Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 Policy Table Sample Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 ATM Application Controller Methods . . . . . . . . . . . . . . . . . . . . . . . . . . 310 ATM Application Controller Events . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 GUI Beans in Card Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 GUI Beans in PIN Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 GUI Beans in Account Servlet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342 GUI Beans in Transaction Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347 © Copyright IBM Corp. 2000 xix xx VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Preface The Persistence Builder is the transaction and persistence framework feature of the IBM VisualAge for Java Enterprise Version 2 and 3 product. The Persistence Builder enables your object models to persist in relational data stores. Creating the persistence layer is accomplished using the Persistence Builder 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. The Persistence Builder 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 manually 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 the Persistence Builder. A basic understanding of object and relational technology is assumed. All developed sample applications and test scripts are available as downloadable sources. This redbook is organized into three parts. Part 1 contains an overview of VisualAge for Java Enterprise and the installation instructions for the products and the sample code. Part 2 contains an introduction to the Persistence Builder transaction and persistence framework, describes the concepts and functions of the tool, and provides a general understanding and work path guidelines on how to use the tool to go through the development process. Part 2 also includes many small examples describing how to deal with simple models, relationships, inheritance, complex mappings, custom queries, and performance. Part 3 contains an implementation of the ITSO Bank application that has been used in a number of other redbooks. We start with an existing database that we reengineer into a schema and an object model. We extend the business model with some application processing logic. We implement a GUI front-end as well as an HTML front-end. We implement the HTML front-end with servlets and with Java Server Pages. Finally we explain how to deploy such applications to a runtime environment, such as IBM’s WebSphere. © Copyright IBM Corp. 2000 xxi 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. Ueli Wahli is a Consultant AD Specialist at the IBM International Technical Support Organization in San Jose, California. Before joining the ITSO 16 years ago, Ueli worked in technical support at IBM Switzerland. He writes extensively and teaches IBM classes worldwide on application development, object technology, VisualAge products, data dictionaries, and library management. Ueli holds a degree in Mathematics from the Swiss Federal Institute of Technology. His e-mail address is [email protected]. Greg Behrend is an I/T Specialist at IBM in the Software Sales and Services Group, Application and Integration Middleware Team, in Toronto, Canada. He has been with IBM since 1998, and has over 5 years of experience in Application Development. Greg's roles include customer consulting, proof of technology design and development, and product support and education. His areas of specialty include VisualAge for Java, WebSphere, and Object Technology. Greg holds a degree in Computer Science from the University of Toronto. His e-mail address is [email protected]. Daniel Peter is an Advisory Education Specialist at the Learning Services Center in IBM Switzerland, where he teaches classes on object technology, Java and VisualAge for Java. Since 1991 he has focused on object oriented application development and since 1996 he specialized in Java. In addition to teaching he still works on customer projects. Daniel holds a degree in Electrical Engineering from the Swiss Federal Institute of Technology. Thanks to the following people for their invaluable contributions to this project: Scott Rich, Joe Winchester, and Daniel Berg, IBM Raleigh for their support in regard to code and design problems. Kevin Williams, IBM Los Angeles, for his support on a number of technical questions in regard to the design of the samples. Markus Muetschard, ITSO San Jose, who wrote the redbook on ObjectExtender (the Smalltalk version of the Persistence Builder) and helped us in a number of technical questions. Frederik Haesbrouck, IBM Belgium, who performed some initial testing and writing for the redbook in the fall of 1998. xxii VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 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 “IBM Redbooks Evaluation” on page 447 to the fax number shown on the form. ❑ Use the online evaluation form found at http://www.redbooks.ibm.com/ ❑ Send your comments in an Internet note to [email protected] Sample Code on the Internet The sample code for this redbook is available as a 5426samp.zip file on the ITSO home page on the Internet: ftp://www.redbooks.ibm.com/redbooks/SG245426/ Download the sample code and read “Installation of the ITSO Sample Applications” on page 14. Preface xxiii xxiv VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Part 1 Introduction In this introduction we give an overview of VisualAge for Java Enterprise and provide installation instructions for the products and the sample code. © Copyright IBM Corp. 2000 1 2 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 1 VisualAge for Java Version 2 and Version 3 In this chapter we give a short overview of the new functionality of VisualAge for Java Version 2 and Version 3. VisualAge for Java is available as two products: ❑ VisualAge for Java Professional ❑ VisualAge for Java Enterprise In this redbook we concentrate on the Persistence Builder feature of the VisualAge for Java Enterprise product. For the latest information visit the VisualAge for Java home page at http://www.software.ibm.com/ad/vajava, and the VisualAge Developer Domain at http://www.software.ibm.com/vadd. © Copyright IBM Corp. 2000 3 VisualAge for Java Professional VisualAge for Java Professional provides the new functionality listed here. The redbook, Programming with VisualAge for Java Version 2, SG24-5264, provides a detailed description of the new function of the Professional edition. Support for Java Development Kit 1.1.6 and 1.1.7 VisualAge for Java 2.0 and 2.1 support the JDK 1.1.6. This support includes Swing 1.0.2, inner classes, anonymous classes, and the Java Native Interface (JNI). The enterprise update for the product provides JDK 1.1.7 support. For more details on Swing, see Chapter 17, “Swing GUI for ATM Application.” VisualAge for Java Version 3.0 is based on JDK 1.1.7. New Integrated Development Environment Features The integrated development environment (IDE) provides: ❑ Advanced coding tools such as automatic formatting, automatic code completion, and fix-on-save ❑ Context-sensitive help ❑ Advanced debugging tools such as conditional breakpoints and both multiple and incremental program debug ❑ Support for JavaDoc output ❑ Enhanced searching capabilities Version 3.0 Enhancements: ❑ ❑ ❑ ❑ ❑ ❑ Search/replace facility Access to project resources from the IDE Direct access of reference help Open on selection of a class or method Enhanced code generation for required methods and field accessors SmartGuide to create a visual application New Visual Composition Editor Features The Visual Composition Editor provides: ❑ Visual programming support for Swing beans ❑ Wizards for string externalization to assist in building multilanguage applications ❑ Complete support for object serialization ❑ Ability to import GUIs built in other Java IDEs 4 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Version 3.0 Enhancements: ❑ ❑ ❑ ❑ ❑ Support for customized layout manager GridBag layout enhancements Improved migration from AWT to Swing (for example, Applet to JApplet) Code generation with inner classes for event handling Quick form GUI layout for properties of a JavaBean JavaBeans for Easy Access to Data New data access beans give your Java application the power to access relational data from any Java Database Connectivity (JDBC) enabled database and make it available on the Web. Version 2.0 provides the Select bean to construct and execute an SQL select statement, and the Navigator bean with Swing push buttons to invoke the functions of the Select bean. Version 3.0 provides additional data access beans: the Modify bean for SQL updates (update, insert, delete), the ProcedureCall bean to invoke DB2 stored procedures, and Selector beans to extract partial result data from a Select bean. New Debugger Enhancements in Version 3 The integrated debugger provides additional function in Version 3.0: ❑ Evaluation area window to execute any Java code ❑ Watches window to monitor variables ❑ Extended breakpoint settings (for example, iteration count) ❑ Visible variables window with their values, separate from the main debugger window Chapter 1. VisualAge for Java Version 2 and Version 3 5 VisualAge for Java Enterprise VisualAge for Java Enterprise additionally provides the new function listed here. Java Team Programming Support The ultimate quality of your Java applications depends on how well you manage your development process. VisualAge for Java includes a built-in source code and version control system that provides you with a complete audit trail of your project and helps in recovery from undesired code changes. In addition to source code and version control, Enterprise Edition users also get a fully integrated team development environment that improves productivity and reuse levels for any size team. Each developer gets a personalized workspace that is integrated with a collaborative repository providing fine-grained versioning of individual components, change identification, and impact analysis across multiple projects. This tight integration avoids time-consuming switching between the repository and the development environment and gives every developer instant “live access” to a library of reusable components. For more details on the team support, consult the redbook, VisualAge for Java Version 2 Team Support, SG24-5245. Source Code Management Tools Integration If you are developing on the Windows platform, you can also check in or check out your VisualAge for Java code to or from either VisualAge TeamConnection, ClearCase, or PVCS. Open Tool Integrator APIs Advanced users and commercial software developers who need to extend VisualAge for Java can use the tool integrator API to: ❑ Add third-party tools that are launched from within the IDE ❑ Store and retrieve components from the integrated repository ❑ Add JavaBeans to the Visual Composition Editor's parts palette Version 3 provides remote access to the tool API from a Web browser through tool servlets running inside the IDE. 6 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Enterprise Toolkits for Workstation, AS/400, and OS/390 The increasing popularity of Java as a server language has placed new requirements for application scalability on Java development shops. Enterprise Edition 2.0 is ready to meet those requirements with a new high-performance compiler for Java that maximizes the execution speed of your server code. We have also filled VisualAge for Java’s toolkit with cross-platform debugging, testing, and performance analysis tools that are accessed from your development workstation and that target applications built to run on OS/2, Windows NT, AIX, OS/400, and OS/390. Plus, the VisualAge for Java remote debugger tests and debugs interpreted Java, compiled Java, and C++ on multiple platforms, giving you a true multitier development environment. New for S/390 developers is JPort, which prescreens Java programs to ensure OS/390 portability and profiles OS/390 Java applications to detect performance bottlenecks. Version 3 provides the Distributed Debugger, an enhanced version of the remote debugger. Enterprise Access Builders Extending existing enterprise application servers to the Web is a critical success factor for leading-edge IT shops. VisualAge for Java Enterprise Edition provides a collection of Enterprise Access Builders that give you access to enterprise systems such as relational data, CICS transactions, or SAP R/3 applications from your Java programs. VisualAge for Java’s unique approach lets you access multiple systems from a single Java application and uses a consistent programming interface across diverse enterprise systems to reduce your learning curve, maximize your productivity, and increase the run-time performance of your applications. VisualAge for Java’s Enterprise Access Builders include: ❑ ❑ ❑ ❑ ❑ ❑ ❑ ❑ ❑ ❑ Access Builder for CICS including CICS ECI, CICS EPI, CICS EXCI. Access Builder for Encina using the DCE Encina Lightweight Client Access Builder for SAP R/3 using SAP R/3 Business Objects Access Builder for Data for JDBC access to enterprise data Access Builder for J2C++ for access to C++ programs Access Builder for Remote Method Invocation (RMI) for creating distributed Java applications Access Builder for IMS (Version 3) Access Builder for MQSeries (Version 3) Access Builder for Host-On-Demand (Version 3) Access Builder for Domino and Lotus Notes (Version 3) Chapter 1. VisualAge for Java Version 2 and Version 3 7 Persistence Builder A new Enterprise Access Builder for Persistence (or short, Persistence Builder) provides a set of tools that automate the task of mapping the persistent state of Java objects to relational databases. These tools generate a layer of code that implements all of the JDBC access calls necessary to insert, update, or retrieve the data for an object from an SQL database. The programming model used to create the persistent Java objects is based on the industry standard Enterprise JavaBeans (EJB) Architecture. The Persistence Builder and its generated Java code is compatible to the EJB architecture in VisualAge for Java Version 3.0. This functionality coincides with the delivery of IBM's Enterprise Java Server (EJS) environments, such as IBM's WebSphere Application Server Version 3.0. The Persistence Builder is the subject of this redbook. Servlet Builder Enterprise Edition users can now use visual programming techniques to create and test servlets. With the Servlet Builder, a wide variety of custom and off-the-shelf business objects can be Web-enabled and used within the VisualAge for Java reusable parts library. When the Servlet Builder is used along with the IBM WebSphere Application Server, site builders can test and debug a combination of pages built using pure HTML, compiled Java Server Pages (JSP), and Servlet Builder visual servlets. For more details, see Chapter 18, “ATM Application Using Servlets.” Version 3.0 provides Servlet Builder enhancements with additional beans. IDL Development Environment The Enterprise Edition provides an integrated development environment for CORBA-based applications. Interface Definition Language (IDL) descriptors can be stored in the repository, together with the Java source code that is generated using IDL-to-Java compilers. Products that implement the CORBA standard can be invoked from the VisualAge for Java IDE to develop and test Java applications that communicate using Internet Inter-ORB Protocol (IIOP). For more information, consult the redbook, Using VisualAge for Java Enterprise Version 2 to Develop CORBA and EJB Applications, SG24-5276. 8 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Support for SanFrancisco, Tivoli, Lotus, and Component Broker IBM has a rich portfolio of Java-based solutions, and VisualAge for Java is the tool of choice for developing many of these systems: ❑ Enterprise Edition includes SanFrancisco wizards for building applications from the SanFrancisco Business Application Components. ❑ VisualAge for Java can also be used to build Java-based business productivity applications using the Lotus eSuite components and to develop, debug, and test Lotus Notes Agents. ❑ Version 2.0 includes new Tivoli Beans used to make Java applications “ready to manage” with Tivoli's enterprise management software. Tivoli lets you easily track activities such as version upgrade, daily use monitoring, and operation and distribution to target systems. VisualAge for Java’s complete IDL environment can be used to create and manage applications that can communicate with CORBA business objects, such as those deployed on IBM's Component Broker application server. AIX Development Environment With VisualAge for Java Version 2.0 and 3.0 you can use AIX 4.2 and 4.3 workstations as your development platform. VisualAge for Java 2.1 Enterprise Enhancements In December of 1998, IBM released an improved Servlet Builder and an Enterprise Update with support for JavaServer Pages (JSPs), Enterprise JavaBeans (EJBs), and an improved Persistence Builder. In March 1999 the Enterprise Update was refreshed to the latest code of the Servlet Builder and the Persistence Builder. For more information on JSPs, see Chapter 20, “ATM Application Using Java Server Pages.” For more information on enterprise beans, consult the redbook Enterprise JavaBeans Development Using VisualAge for Java, SG24-5429. Chapter 1. VisualAge for Java Version 2 and Version 3 9 VisualAge for Java Version 3 Enterprise Enhancements Version 3.0 provides a number of new and enhanced facilities for enterprise access: ❑ The Enterprise Access Builders are improved through better record import and edit tools, and a rewritten Command editor. ❑ New access builders for IMS, MQSeries, Host-On-Demand, and Domino ❑ Support for JCICS, the CICS API in Java, that enables you to write CICS transactions in Java ❑ Support of SQLJ to write Java applications with static SQL ❑ Support for the DB2 Stored Procedures Builder (DB2 6.1) ❑ Enhanced EJB development environment with support for inheritance and relationships, a new field SmartGuide, and the generation of access beans that encapsulate the finding of EJB homes ❑ Enhanced WebSphere Test Environment for servlets, JavaServer Pages (JSP), and EJBs, with support for the XML parser for Java 10 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 2 Environment Setup and Installation Instructions In this chapter we describe the hardware and software setup we used to write this redbook. Hardware We used PCs with a speed of 166 MHz or faster and 96 MB of memory. The CPU speed is not as important as having enough memory. Although it is possible to run with less memory (80 MB), 96 MB are necessary to work with VisualAge for Java and DB2, and at the same time write the redbook. The total hard drive space required for our configuration is about 1.5 GB. For VisualAge for Java Enterprise Version 3 we suggest PCs with at least 233 MHz and 128 MB of memory. © Copyright IBM Corp. 2000 11 Software The PCs were configured with: ❑ Windows NT Workstation ❑ DB2 UDB Workgroup Edition Version 6.1 (VisualAge for Java Version 3.0 ❑ ❑ ❑ ❑ ❑ ❑ ❑ requires Version 5.2 with Fixpack 11 or Version 6.1 with Fixpack 2) VisualAge for Java Enterprise Version 3 Netscape Communicator Version 4.6 Internet Explorer Version 5 Adobe FrameMaker 5.5 (to write the redbook) PaintShop Pro Version 4 (to capture screen images) IBM HTTP Server (on one machine for deployment) WebSphere Advanced Edition Version 2.03 and 3.0 (on one machine) Installation of VisualAge for Java We installed VisualAge for Java Enterprise Version 3.0. The first edition of the book was written using VisualAge for Java Enterprise Version 2.0 with Rollup 2 Fixpack and the Enterprise Update of April 1999 (Servlet Builder, Persistence Builder, and Enterprise JavaBeans). VisualAge for Java updates are available at the VisualAge Developer Domain at http://www.software.ibm.com/vadd. Follow the instructions that come with each of the updates. WebSphere Test Environment VisualAge for Java Version 3.0 includes support for WebSphere Version 3.0 running in VisualAge for Java for testing of servlets and JSPs. (The Enterprise Update for VisualAge for Java Version 2.0 included support for WebSphere Version 1.1). Configuration File You can use the WebSphere Test Environment or the Sun Servlet Runner to test servlets in VisualAge for Java. For testing JSPs, the WebSphere Test Environment is required. A configuration file controls which servlet test environment is started: \IBMVJava\ide\project_resources\IBM Servlet IDE Utility class library\ com\ibm\ivj\servlet\runner\configuration.properties 12 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs The non-comment lines in this file should read: serverPort=8080 serverAddress=127.0.0.1 serverDocBase=. serverPollingCount=15 serverClassPathSuffix=../IBM WebSphere Test Environment/lib/ns.jar; ../IBM WebSphere Test Environment/lib/....xxxx.....jar; ...... serverDirectory=/../project_resources/IBM WebSphere Test Environment serverDirectoryRootProperty=user.home #serverClassName=com.ibm.ivj.servlet.runner.JsdkServletRunnerStarter #browserPath= The last line controls what servlet environment is started if the HttpServerStarter class is used. If the line is commented, WebSphere is started, if uncommented the Sun Servlet Runner is started. In VisualAge for Java Version 3.0, we suggest to use the WebSphere Test Environment exclusively for servlet and JSP testing. Starting WebSphere To start WebSphere within VisualAge for Java, find the SERunner class in the com.ibm.servlet package in the IBM WebSphere Test Environment project. Open the properties (Selected -> Run -> Check Class Path) and edit the project path. Your project with the servlets and the project with the compiled JSP must be in the list. The easiest is to select all projects! Note that you must repeat this step after the project with the compiled JSP (JSP Page Compile Generated Code) has been created. This is not necessary in VisualAge for Java Version 3.0. Start the SERunner by clicking the Run button in the tool bar. In VisualAge for Java Version 3.0 you can start the SERunner through Workspace -> Tools -> Launch WebSphere Test Environment. A WebSphere Test Environment Window opens (Figure 1). Figure 1. WebSphere Test Environment Chapter 2. Environment Setup and Installation Instructions 13 Check the progress of the WebSphere server in the Console window. The server is ready when you see the following lines: Version 2.0: ServiceParameters: servletservice: Load (update): endpoint.main.port=80 Version 3.0: Hostname bindings: [hostname-binding]: hostname=localhost----> servlethost=Host for ..... [hostname-binding]: hostname=127.0.0.1----> servlethost=Host for ..... [hostname-binding]: hostname=............. Stopping WebSphere You stop the WebSphere Test Environment by clicking on the Stop and Exit button in the WebSphere Test Environment window, or by clicking on the Terminate button in the tool bar of the Console window. DB2 Access from VisualAge for Java DB2 can be used in many ways from VisualAge for Java. Under all circumstances VisualAge for Java must have access to the DB2 JDBC drivers, which are in the d:\SQLLIB\java\db2java.zip file delivered with DB2 (where d is the driver letter of the DB2 installation). This can be accomplished by adding the file to the workspace class path. Open the Options windows (select Window -> Options) and select Resources. Add d:\SQLLIB\java\db2java.zip; to the workspace class path. Alternatively you could create a new project, for example, DB2 JDBC Drivers, and import the d:\SQLLIB\java\db2java.zip file into the new project. Version the project, for example, 6.1 (for DB2 UDB Version 6.1). Installation of the ITSO Sample Applications The sample code developed for this book is available as a ZIP file on the Internet: ftp://www.redbooks.ibm.com/redbooks/SG245426/ You can also find the sample code by following the link Additional Materials from: http://www.redbooks.ibm.com/ 14 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs The 5426samp.zip file expands into a directory structure containing all the sample code: \Va3PersBk\SampCode \Va3PersBk\Repository <=== sample files for book chapters <=== VisualAge for Java export files (.dat) Sample Code Table 1 describes the sample code subdirectory structure and the files contained in those subdirectories. Table 1. Directories and Files with Sample Code Subdirectory File Description Main directory: \Va3PersBk\SampCode ... all subdir xxx.scrap Scrapbook files simple simple.ddl DDL for Simple model transactions xxx.scrap Scrapbook files many many.ddl DDL for ManyToMany model manydata.sql Sample data inheritance.ddl DDL for Inheritance model multiple.ddl DDL for MultipleInheritance model inheritancedata.sql Sample data for inheritance multiple.sql Sample data for multiple inheritance complex.ddl DDL for ComplexMapping model composer.ddl DDL for CustomerComposer model complexdata.sql Sample data for complex mapping customquery custom.sql Sample data (simple tables) lite lite.ddl DDL for Lite model litedata.sql Sample data for Lite model LoadSignature.class Program to load signatures into table thread CustomerThread.java Program to test transactional threads itsobank itsobank.ddl DDL for ItsoBank model itsobank.sql Sample data for ATM application inheritance complex \itso\lite\gui Chapter 2. Environment Setup and Installation Instructions 15 Repository Export Files The repository subdirectory (Va3PersBk\Repository) contains two files: ❑ 5426samp.dat: Project ITSO SG245426 Samples ❑ 5426bank.dat: Project ITSO SG245426 ITSOBANK ATM Table 2 describes the Persistence Builder models that we developed and the Repository packages where the code is stored. Table 2. Models and Packages in the Repository Samples Model Description and Packages Simple metadata domain services Simple Customer with Association itso.vap.simple.metadata itso.vap.simple.domain itso.vap.simple.wsservices (workspace) itso.vap.simple.services (database) itso.vap.simple.opservices (optimistic predicate) itso.vap.simple.lockingservices (pessimistic) itso.vap.simple.gui itso.vap.simple.thread GUI application Multithreading 16 SimpleBU Simple Customer with Association Bottom-Up itso.vap.simplebu.metadata, .domain, .services ManyToMany Many-to-many Relationship itso.many.metadata, .domain, .services, .gui Inheritance metadata, model Single table Multiple table Single and Multiple Table Inheritance itso.inheritance.metadata, .singletable.domain itso.inheritance.singletable.services itso.inheritance.multipletable.services ComplexMapping Complex Mapping with Root/Leaf Tables itso.complex.metadata, .domain, .services CustomerComposer Complex Mapping Using a Composer itso.complex.metadata itso.complex.composer.domain, .services Lite Lite Collection itso.lite.metadata, .domain, .services, .gui Timestamp Test with Timestamps itso.vap.timestamp.metadata, .domain, .services ItsoBank ITSO Bank ATM Application itso.bank.persistence itso.bank.persistence.model, .services, .gui, .servlet, .jsp VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs To load the packages into the repository, use the import function of VisualAge for Java. You can import the whole projects or just individual packages. After importing you have to load the packages into the Workbench; either into your own project or by using one of the ITSO projects. Install DB2 Databases For all the small samples, we used one database called VAPSAMPL and a table prefix of ITSO. For most models we used a different set of tables: Model: Tables: Simple Customer00, Account00 SimpleBU Customer00, Account00 ManyToMany Card, BankAccount, CardAccount Inheritance CustomerInh, BankAccountInh MultipleInheritance CustomerMult, BankAccountMult, CheckingMult, SavingsMult ComplexMapping CustomerComp, CustomerDataComp CustomerComposer CustomerComp Lite CustomerLite Timestamp Customer01 Create the VAPSAMPL database before running the small samples. The tables can be created from the Schema Browser, or by running the DDL that is provided. For the ITSO Bank ATM Application we use the ITSOBANK database with a set of tables. This database is used by a number of other ITSO redbooks. The DDL and sample data is provided as files; see “ITSOBANK Database Implementation” on page 270. Persistence Builder Code Problems We identified several problems in the code of the Persistence Builder. Some problems were fixed by the Rollup2 Fixpack, but other problems were not fixed at the time of publication of this redbook. They were fixed in VisualAge for Java Version 3.0. Chapter 2. Environment Setup and Installation Instructions 17 18 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Part 2 VisualAge for Java Persistence Builder In this part we explore the functionality of the Persistence Builder with a number of small easy-to-understand examples. © Copyright IBM Corp. 2000 19 20 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 3 Persistence Builder Concepts This chapter introduces the concepts of the VisualAge for Java Persistence Builder. We explore the persistence application model, persistence builder framework, development and runtime environments, transactions, and data storage considerations. The ability of the Persistence Builder to handle multiple development paths is also explained. After completing this chapter, you should have an abstract understanding of how the Persistence Builder works, and how it is used to implement applications that persist their underlying object model in a relational database. © Copyright IBM Corp. 2000 21 Overview The Persistence Builder is an extensive and powerful persistence framework providing a complete solution for building robust, scalable persistence support for object models. Object models, represented by class hierarchies, are said to be persistent when instances created from these classes can be stored to an external data store such as a relational database. The Persistence Builder enables you to map objects, and relationships between objects, to information stored in relational databases. It also provides links to legacy data on a number of other systems. As a feature of VisualAge for Java, the Persistence Builder is especially designed for UI-intensive, nested transaction applications. This tight integration with the VisualAge family enables you to leverage your current VisualAge investment and expertise. A rich set of integrated tools make the persistence effort minimal by providing: ❑ Automated code generation services to underlying frameworks ❑ Import and export facilities for working with database schemas ❑ Debug and monitoring tools for performance tuning where desired As object-oriented technology has matured in the industry, it has proven to be an excellent solution for modeling problems, building prototypes, and rapidly deploying applications. Though object models for these applications are often reused in other applications, one of the more costly tasks of development has been the translation between object-oriented and non-object-oriented representations of business models. The majority of databases in use today are not object-oriented, and the task of mapping objects to relational database tables and legacy data from various sources has been the missing piece in object persistence standards. On a small scale, object persistence is not difficult to solve. However, large scale applications introduce new requirements to frameworks that support object persistence. The Persistence Builder is supported by a framework that delivers on requirements applying to large scale applications. A well designed object model and data model allows you to take full advantage of the functions available in the Persistence Builder. The Persistence Builder is a productive tool that simplifies many of the challenges when developing database applications. Some of the benefits in developing applications with the Persistence Builder include: ❑ Independent object model and datastore design ❑ Rapid Application Development (RAD) 22 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs ❑ ❑ ❑ ❑ ❑ Multidirectional development Advanced transaction support Advanced query support Object relationship support Seamless support for various database paradigms In the sections that follow, we describe the concepts, frameworks, and tool components of the Persistence Builder. In describing concepts and frameworks, it is often necessary to talk in somewhat theoretical terms. To avoid this, we use a banking example relating customers to bank accounts. In our example, a customer may have more than one bank account and a bank account may have only one customer (to simplify things at this level, we do not consider joint accounts). Customers have a customer identification number, a title, a first name, and a last name. Bank accounts have an account identification number, and a balance. Looking at this example in an object-oriented way, there are two objects, the Customer and the BankAccount. These objects are linked to one another because it is necessary to know all of the owners for a bank account, and all of the bank accounts for a customer. Figure 2 shows a class diagram for this customer and bank account model in UML notation. Customer customerId title firstName lastName BankAccount ownedAccounts accountId 1 0..* balance accountOwner Figure 2. Class Diagram In this diagram we introduce the idea of a role. A role is a description of the participation that one class has in the context of another. For example, the role of Customer in the context of the BankAccount class is that of an account owner. This is labelled in Figure 2 as accountOwner. By convention, roles begin with a lowercase letter. These roles are used in the Persistence Builder when defining the object model and will become part of the method names in the objects for retrieving the related objects. For example, accountOwner becomes a getAccountOwner method. Chapter 3. Persistence Builder Concepts 23 Application Layers Persistence Builder 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 between 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 will handle 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 direct one-to-one mapping between the business objects and the related database tables. Dialogs Application Programming Transactions Object Modeling Business Objects Services Data Access Programming Data Store Figure 3. Application Layers and Components 24 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Persistence Layers The previous section discussed the need to map between business objects and the underlying database. The Persistence Builder provides a full set of tools to support the development of these mappings, so that the business object storage can be made persistent. In the development environment, the Persistence Builder provides a set of browser tools that allow the user 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. Development Environment Browsers Models Runtime Environment Classes and Instances Transactions with Object Versions Model Object Meta Model Business Objects & Relationships Home Collections Map Maps Schema Data Base Schema Services Figure 4. Components of Persistence Layers for Development and Runtime In the development environment three browsers are provided to maintain an object model, a database schema, and a mapping between the model and the schema. From this information the runtime classes are generated. Chapter 3. Persistence Builder Concepts 25 The runtime classes consist of the business objects and relationships, and the services that connect to and interact with the database. The home collections are the main interface for applications to retrieve and insert business objects. At the top of the runtime environment is the transaction management that provides versioned views of business objects and controls the commit or rollback of updates. To illustrate this in more detail, the following sections explain how a business object might be mapped to a schema. Object Models The simple example has only two objects and therefore two classes: Customer and BankAccount. The Customer class is defined with the following attributes: ❑ ❑ ❑ ❑ customerId, for the customer identification number title, for the salutation (for example, Mr., Mrs., Dr.) firstName, for the customer’s first name lastName, for the customer’s last name You will notice that we have not placed a bank account attribute in the Customer class. This is deliberate as a bank account is not an attribute of the Employee class. Instead we have defined a link between the Customer and BankAccount classes that associates many bank accounts to one customer. The BankAccount class is defined as follows: ❑ accountId, for the bank account identification number ❑ balance, for the account balance We use the Persistence Builder Model Browser to create the object model, create the classes called Customer and BankAccount, and add the attributes and associations. In Figure 2 on page 23, the association accountOwner (ownedAccounts) is now named Customer-BankAccount. The relationship is a 1-to-many association between these two classes, as shown in Figure 5. Notice that the related roles accountOwner and ownedAccounts are preserved and used to describe the same roles of the Customer for the BankAccount and vice versa respectively. 26 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 5. Association Editor: Association Customer-BankAccount It is important to notice that the association is navigable in both directions. It is possible to find the bank accounts that a customer owns from the Customer object. Similarly, one can identify the customer that owns a bank account from the relevant BankAccount object. Readers should distinguish between this object-oriented view and relational view of this example. In the relational view, it is not possible to find out which customer owns an account by querying the BankAccount table. Instead, it is necessary to query the Customer table as well. If the class names do not clearly specify the roles, or there are multiple associations between two classes and the naming pattern Classname1-Classname2 creates the identical names for them, then the Role1-Role2 naming pattern or a combination of both could be considered. Schemas A physical database with tables (for a relational database) is required to support the object model. If the application was developed completely from the beginning, such as this example, then the Persistence Builder schema generation and Schema Browser is used to generate the necessary DDL to create the required physical database. If the physical database already exists and an object model is to be defined to reflect this database, the Persistence Builder Schema Browser can be used to interrogate DB2 (for example) for the tables to reverse-engineer a schema Chapter 3. Persistence Builder Concepts 27 definition. We can then view or modify properties of the schema such as relationships (Figure 6). CustomerBankAccount "CustomerBankAccount" Figure 6. Foreign Key Relationship Editor Note that the Persistence Builder assigns a generated name to a foreign-key relationship unless a constraint name is assigned when defining the foreign key: ALTER TABLE ITSO.BANKACCOUNT \ ADD CONSTRAINT "CustomerBankAccount" FOREIGN KEY (customerid) \ REFERENCES ITSO.CUSTOMER ON DELETE RESTRICT Maps If a database schema for a new application is being generated directly from the object model, the Persistence Builder will automatically generate 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 a Persistence Builder schema from the tables. Mappings must be created between the business objects and the database tables. 28 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs The Persistence Builder supplies a Map Browser to help with this task. The Map Browser is used to create mappings between class attributes and table columns, as shown in Figure 7. Figure 7. Property Map Editor with Class Attribute to Table Column Mapping Class associations must also be mapped to table foreign keys (Figure 8). Figure 8. Property Map Editor with Association to Foreign Key Mapping Of particular note is that the ownedAccounts association is not mapped to a column in the Customer table. This is because the physical database implementation does not contain a column in the Customer table to say which accounts the customer owns. This information is contained in the BankAccount table only. Chapter 3. Persistence Builder Concepts 29 Transactions As an environment for developing data processing applications, the Persistence Builder naturally supports the concept of the transaction. It supports a number of different types of transactions, as described in the following subsections. With the exception of the global shared transaction, all transactions must be owned by a parent transaction. 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 scheme would imply that modified objects are first recorded within a transaction, then, 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 candidates for collision. When the transaction directs its participants to lock their resources, the objects that are not collision candidates do nothing since they have no resources that require locking. The net effect of collision management strategies depends entirely on what the underlying data store supports. Shared Transaction The Persistence Builder requires that every VisualAge for Java application has always at least one transaction active. To ensure that this is always the case, the the Persistence Builder implements the global shared transaction. This transaction is created when an application is started and remains in existence until the application finishes. However, the shared transaction cannot be used to retrieve objects from the database. The shared transaction acts as the parent transaction for all top-level transactions. 30 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Top-Level Transactions Top-level transactions are always child transactions of the global shared transaction. They supply all 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. We distinguish between read-only transactions and normal transactions. Read-Only Transactions If your only aim when developing an application is to browse data (that is, no updates, inserts, or deletes), then a read-only transaction supplies all the transaction functionality that you need. Another purpose of a read-only transaction is the sharing of objects across child transactions. For example, if you have a series of GUI windows manipulating the same objects, you can begin a top-level transaction for each window as a child of a read-only transaction. Each child transaction can commit the changes to the read-only transaction and at the same time to the database. Transactions To update the database you must be within a normal transaction. When the transaction is committed to the shared transaction (or to a read-only parent transaction under the shared transaction), a commit is issued to the underlying database to make the changes persistent. Nested Transactions It is sometimes necessary to break 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 also called nested transactions. The transaction has been broken up 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. This may be driven by user interface considerations. For example, the user needs to enter information into a number of screens to complete a new customer entry. If the user makes a mistake part-way through, it is not helpful if the user has to go back to the very beginning to correct the error. Chapter 3. Persistence Builder Concepts 31 Shared Transaction Top-Level Transaction Subtransaction A Subtransaction A1 Subtransaction B Subtransaction C Subtransaction A2 Figure 9. Example of a Nested Transaction Tree A subtransaction commits its changes to the parent transaction. In general, this does not result in a commit to the database. The top-level transaction must be committed to make changes persistent in the database. There is one exception to this rule. If the top-level transaction is a read-only transaction, then a subtransaction committed to the top-level (read-only) transaction results in a database commit. 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. The Persistence Builder provides facilities to allow switching between transactions. For variables holding a transient (not persistent) object, this does not present a problem. However, for variables holding a persistent object, where changes to the object can be committed or rolled back, it is important to know which transaction is associated with the object and the variable so that the commit or rollback can be correctly handled. It is therefore possible, in advanced scenarios such as running parallel transactions, that this normal association between a variable and a transaction may be lost. The Persistence Builder uses the current transaction as the context for changing an object through a variable. If the variable was actually associated with another transaction, then 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. 32 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs The Persistence Builder provides, through the transacted variable, a mechanism for explicitly ensuring that a variable is associated with a specific transaction, and so are the objects of these transacted variables. 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. Transacted variables are usually used in GUI applications. If you are running in a Web server environment and you do not create multiple transactions in each server request, then there is no need to use transacted variables. Instead you can specify a TransactionToThreadBindingPolicy that resumes the proper transaction automatically (see “Transaction Binding Policies” on page 260). Commit and Rollback of Transactions 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 promoted to the object version from where the object version to be committed was derived, that is, to the transaction that was the context when the now committing transaction was created. If the object version is the version read from the persistent storage, that is, the associated transaction is the top-level transaction, the object becomes the new persistent version, and is written to the persistent storage (Figure 10). TRANSACTIONS Shared TIME DB DB Retrieve Update Top-Level Commit Change Commit Sub A Change Sub A1 Rollback Figure 10. Commit and Rollback of Transactions Chapter 3. Persistence Builder Concepts 33 The Persistence Builder detects collisions, also called conflicts, while promoting changes to higher object versions or transaction levels as well as to the persistent storage. A collision happens when a second promote to the same level of object version and transaction from a logically lower level is initiated. Merge policies can be defined so that the Persistence Builder can handle collisions automatically or invoke application defined code that handles the collision. Collisions that are not handled result in a rollback of the transaction. Frameworks The Persistence Builder is a framework of frameworks, developed to reduce the amount of code that the applications developer subsequently needs to produce. The Persistence Builder framework consists of the components—frameworks as well—shown in Figure 11. Persistence Builder Framework Modeling Framework Relationship Framework Transaction Framework Mapping Framework Data Store Framework Figure 11. VisualAge Persistence Builder: Frameworks Each of these frameworks addresses a different part of the development task: ❑ Modeling Framework. The modeling framework, supported by the Modeling Browser, allows the developer 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 between the various classes. The framework then provides tools for automatic code generation (and 34 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs regeneration after amendment of the model). The generated code provides places for the user to insert code for special processing, such as business rule validation, and preserves such code should the automatic code regeneration be invoked again. ❑ Data Store Framework. The data store framework, supported by the Schema Browser, allows the developer to build definitions of the underlying physical data store. If a new data store is being developed, the framework will generate the requisite Data Definition Language (DDL) to build the required data store. If a legacy relational data store is available, then the framework can interrogate the data store to build the requisite Persistence Builder view of the data store. ❑ 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, allows the developer to do that. ❑ Relationship Framework. In any real-world application, the classes in the object model are related to one another. The Persistence Builder relationship framework, supported by all three browsers, allows the developer 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. ❑ Transaction Framework. Persistence Builder 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. Metadata Storage When defining object model, schema, and maps with the Persistence Builder browsers, in effect, metadata is described. The information for each of these is stored in a class in the development repository. When a model, schema or map is first created it can be edited without requiring it to be saved. In this case all changes are recorded in the workspace. To save the work and make it available to other users the Save option can be used from each of the browsers. This will store the details of the model, map or schema into a class in the development repository. If a model, map, or schema requires saving, the browser will indicate this in the text pane on the lower half of the browser. Note that the class holding the metadata was empty in VisualAge for Java Version 2 when viewed in the Workbench. In Version 3 the metadata storage Chapter 3. Persistence Builder Concepts 35 classes have a generated method called infoString, which contains a textual copy of the information stored in the repository. This string can be used to compare versions of a metadata storage class to identify their contents. After a model, map, or schema has been saved, it can be loaded by other users into their Workspace by loading the metadata class the same way any other Java class is loaded from the repository. After loading the classes that hold the metadata information for a model, map, or schema into the Workspace, the browsers are not automatically refreshed. This is done with the Load Available menu option. Taking this option will retrieve the models, maps, or schemas in the Workspace from the list of loaded classes that are holding such metadata information. As the model, map, or schema is modified in the browser, the changes are recorded locally in the Workspace. If the developer decides not to save the changes the model, map, or schema can be reverted to the version last saved by selecting the Revert menu option. The menu option Save writes the definitions of the model, map, or schema to the repository. From here, the class can be versioned and then loaded and worked on by another user. It is good practice to version the class containing the meta data for the model, map, or schema before another user loads it. Once the storage class has been versioned it can also be moved between development repositories using the Import and Export menu options. Development Paths The Persistence Builder is a versatile tool that allows development of a persistence application along a variety of application paths. The development path used, depends on whether development begins from a new project, from legacy (data access) code, or from existing databases. Figure 12 gives an overview of the typical three development paths in a relational environment: ❑ Forward or top-down: Start with the object model ❑ Backward or bottom-up: Start with the given database ❑ ‘Outside-in’ or mapping: Map the given object model to the given database Forward or Top-Down: Start with the Object Model The forward or top-down path is the typical path for a new application (Figure 12). 36 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Development Paths (in Relational DB Environment) Meta Model Model Classes RT B (1) Mappings (Attr/Assoc) Database Schema B Export Schema to DB (Generate DB, Tables, ...) Backward or Bottom-Up: Start with the (Legacy) Database Generate Model from Schema (Mapping is generated too) Generate Model Classes (3) RT Generate Service Classes Generate Schema from Model (Mapping is generated too) Define Obj Modeling B create Table... RT Forward or Top-Down: Start with the Metamodel Define Obj Generate Model Model Classes (2) Database (DDL) Service Classes Import Schema to DB (Generate Schema from DB) Implement Database Generate Service Classes ’Outside-In’: Map the Metamodel to the Database Schema Map the Model to the Schema or vice-versa Generate Model Classes Import Schema to DB (Generate Schema from DB) Implement Database Generate Service Classes RT Tasks Done in/by Persistence Builder Automated Tasks Manual Tasks in (Generation/Import/Export/...) Persistence Builder Some Other Tool(s) Runtime Component Development B Comp. in Browser Figure 12. Development Paths Chapter 3. Persistence Builder Concepts 37 The first step on this path is the definition of the given object model in the Persistence Builder. From there, we generate the components: the business model classes, the database schema, the mapping, the service classes, and finally the creation of the database tables. You can control every generation step and make adjustments to the generation options as well as to the results created by the Persistence Builder. 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. And even if you have to attach to legacy (data access) code and database, the generated results up to the database schema are a good base to start with the changes and adjustments. The forward or top-down path is also typical for prototypes, rapid application development (RAD), and initial versions of an application. The operational and subsequent versions and extensions of the applications will then be developed along the ‘Outside-in’ path described later. This path guarantees the option for clean object-oriented implementation of the business model, as well as performing state of the art database implementations. For prototyping, the Persistence Builder Model Browser offers the Workspace Schema generation feature. This feature generates classes to persist the object model in the VisualAge for Java Workspace. These classes are generated from the object model, so a data store schema and mapping are not required. The generation of the Workspace schema from the model definition generates only a simulated local data store and actually no schema, no map, and only minimal service classes. Therefore, using the Workspace schema generation feature of the Persistence Builder does not require a database system to be installed, and is also very convenient for executing scenarios with prototypes for demonstration purposes. Backward or Bottom-Up: Start with the Database The backward or bottom-up path is typical for a new application or application extension based on a given database (Figure 12). The first step in this path is capturing the existing database implementation as database schema in the Persistence Builder. From there we generate the components: the meta model and mapping, the business classes, and finally the service classes. You can control every capture or generation step and make adjustments to the options as well as to the results produced by the Persistence Builder. 38 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs For situations beyond average requirements, such as complex table dependencies, absence of reference definition in the database, or (performance related) redundancies in the tables, the captured and generated results require manual changes and adjustments. The Persistence Builder supports the capturing of the database implementation after connecting to the database through the import of the schema from the database. ‘Outside-in’ or Mapping: Map Object Model to Database The ‘outside-in’ or mapping path offers the best options—freedom in clean object-oriented modeling and freedom in performing database implementation—and is a combination of the top-down and bottom-up (Figure 12). It offers the best match for maintenance. Changes to an application requires consideration for both the object model definition and data store definition. But mapping may become difficult: What is to be done if a reasonable mapping cannot be found? The answer is to refine the object model to accomodate a reasonable mapping. If the database is given and already heavily used in other applications, 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. If you have an application with existing object model classes, you may have to create an object model with the Persistence Builder and write code to map between the old existing model classes and the new object model generated by the Persistence Builder. Chapter 3. Persistence Builder Concepts 39 40 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 4 Persistence Builder Tools In this chapter we provide a functional overview of the Persistence Builder tools. After completing this chapter you should have a basic understanding of what functions each tool provides. The Persistence Builder tools are a collection of development browsers, tools, and code generation services that help you build persistence support for your application. The browsers are the main resources for building an application on the Persistence Framework. They are used to describe the object model, database schema, and data store mapping as well as generate the code needed by the framework to manage the business objects. Using the Status and SQL Query tools, information can be collected and studied to ensure the business objects are exhibiting their expected behavior. The available browsers and tools are outlined below: ❑ The Model Browser is used to define an object model, its classes and associations and generating schemas from a defined model. © Copyright IBM Corp. 2000 41 ❑ The Schema Browser is used to define a logical description of the data store to which the object model will persist. ❑ The Map Browser is used to map the object model, or persistent classes, to the logical (or database) schema. Each persistent class needs a map that associates the attributes of the class with the corresponding columns (or fields) in the database tables (or record) and also associates class associations with table connections. The object model and schema must be defined before mapping occurs. ❑ The SQL Query Tool is used occasionally (often for testing and verification) to query the database directly. ❑ The Status Tool is used to collect various statistics during development, such as persistent object, data store, and transaction statistics. The Persistence Framework Tools are accessed from the Persistence Tools Menu (Workspace -> Tools -> Persistence Builder Tools) (Figure 13) as well as from any browser or tool menu. Figure 13. VisualAge for Java Persistence Tools Menu In what follows, we will briefly describe the features of each browser and tool. 42 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Model Browser Figure 14 shows the Model Browser. The Model Browser is used to define the classes in an object model. The object model consists of the classes that represent the business objects in an application that will persist in a data store. Figure 14. Persistence Builder Model Browser Business objects are described through class descriptions. For example, in an object model for a Bank, you might describe: a Customer class, a Card class, a BankAccount class, and so on. Describing a class with the Model Browser is similar to defining a class using a SmartGuide. Each class consists of attributes (instance variables) and relationships. Attributes describe the information that is to be persisted in an object. Relationships describe how the objects of a model are associated with each other. In the Bank model, the Customer and Account classes might have attributes such as customerId, and accountId, respectively. In addition, there might be a relationship between the two classes. In this example a Customer has an Account, so there is a relationship between a Customer object and an Account object. There is a difference between describing a class in the Model Browser and defining a class using other tools. Describing a class does not create an instance of the class. It creates metadata for the class. The metadata is used later to generate the class instance. The instance of the class is created when Chapter 4. Persistence Builder Tools 43 you use the Generate function under the Models menu. Generating the code for the model is typically done after the class have been described. If changes are made to the model after the code has been generated, the code must be regenerated. The Model Browser provides several views: ❑ The Models view displays the names of the defined models. ❑ The Model Classes view displays the names of the classes for a selected model. ❑ The Attributes view displays the names of the attributes for a selected class in a model. ❑ The Class Associations view displays the associations (or relationships) defined between the classes in the selected model. ❑ The Information view (not labeled) is a read-only view that provides descriptive information in a given context. For example, if only the name of a model is selected, it will provide statistics about that model. Each view is used successively to describe a model, its classes, and its associations. It is easy to verify the design when complete by browsing the contents and associations for each class. When the model definition is complete, it can be saved in the Workspace. In addition to creating models with the Model Browser, you can also edit models, and load other models into your Workspace from the Repository. Supporting Editors The Model Browser has a number of editing dialogs to assist in the creation of a model. The Class Editor (Figure 15) allows for the design of classes in the model. You can create new classes and modify classes. The main function of the class editor is adding, changing, and deleting attributes, and specifying which attribute(s) is the object ID. 44 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 15. Model Browser: Class Editor Attribute names and types are specified in the Attribute Editor (Figure 16). The Attribute Editor is opened by clicking on the New or Edit button in the Class Editor. Figure 16. Model Browser: Attribute Editor Associations between classes are defined in the Association Editor (Figure 17). You specify the role names, the cardinality in each direction, if a related object is required, and if the direction is navigable (a method is generated). Chapter 4. Persistence Builder Tools 45 Figure 17. Model Browser: Association Editor Lite Collections In the Class Editor, a lite collection can be defined on the Lite Collections tab. A lite collection is a subset of the properties of a class (attributes and associations). By using a lite collection, a subset of information from a particular object in the database can be retrieved without necessarily instantiating all associated objects that are retrieved. Using a lite collection can improve performance when retrieving large sets of data. See “Lite Collections” on page 244 for more information. Generating a Schema and a Map from a Model After the model is defined a data store schema can be generated from a model to describe the data store for the persistent classes. Upon generation, this schema is automatically available for viewing and editing in the Schema Browser. This process also generates a map between the model and the schema for viewing and editing in the Map Browser. If a schema is not generated from the Model browser, it can be manually defined in the Schema Browser. 46 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Schema Browser Figure 18 shows the Schema Browser. The Schema Browser is used to describe the schema of the underlying data store into which the persistent classes are stored. Figure 18. Persistence Builder Schema Browser Describing a schema involves the usual tasks of defining any database schema, namely, creating tables, columns, primary keys, foreign keys, and so on. The database tables can be defined from scratch, or they can be imported from an existing database. Once the schema has been described, it can be exported to a database, or DDL or scripts can be generated to be invoked later to create the schema in a database. The Schema Browser presents several views: ❑ The Schemas view displays the names of the defined schemas. ❑ The Tables view displays the table names of a selected schema. ❑ The Columns view displays the column names of a selected table. ❑ The Foreign Key Relationships view displays the foreign key relationships defined in the schema. Chapter 4. Persistence Builder Tools 47 ❑ The Information view (not labeled) is a read-only view that provides descriptive information in a given context. For example, if only the name of a schema is selected, it will provide statistics about that schema. Each view is used to progressively define the database schema. This involves defining database tables with their columns and key definitions. If the database schema for a model was generated from the Model Browser it will appear in the Schemas view, otherwise the schema for a model will have to be manually created. Supporting Editors The Schema Browser has a number of editing dialogs to assist in the creation of a schema. The Table Editor (Figure 19) allows for the design of tables in the schema. The main function of the Table Editor is to define the physical table name with qualifier, the columns of the table, and which column(s) is the primary key. Figure 19. Schema Browser: Table Editor 48 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Column names and types are specified in the Column Editor (Figure 20). The main specifications are the data type and length, and what converter is used to transform the database value into a Java type. See “Converters” on page 217 for more information on datatype conversions. Figure 20. Schema Browser: Column Editor A column can be updated from the Table Editor or you can select the column in the Schema Browser and invoke the Edit Column action. Foreign Key Relationships between tables are defined in the Foreign Key Relationship Editor (Figure 21). For a new relationship, you select the two tables and the column in each table that forms the relationship. For a relationship generated from the Model Browser, this information would be filled in already. The main specifications for a relationships are the physical name (adhere to the underlying database rules) and if the constraint exists in the database. If you select this checkbox, a foreign key definition is generated into the underlying database. Chapter 4. Persistence Builder Tools 49 Figure 21. Schema Browser: Foreign Key Relationship Editor Map Browser Figure 22 shows the Map Browser. The Map Browser is used to logically connect the object model description with the data store schema description. This enables the Persistence Framework to generate the necessary service code for such things as SQL queries and other supporting code needed for persisting the model objects. Mapping an object model to a data store is done by creating table maps and property maps. A table map is required for the classes in the model that are to be persisted in the data store. The property maps are defined for each table map by mapping object attributes to database columns. In forward or backward development, the mapping between the model and the schema is generated by the Persistence Builder. The Map Browser can be used to augment the mapping (for example, for inheritance) or to make small adjustments. 50 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 22. Persistence Builder Map Browser The Map Browser presents several views: ❑ The Datastore Maps view displays the names of all defined data maps. ❑ The Persistent Classes view displays the names of the model classes which will be mapped to database tables. ❑ The Table Maps view displays the names of the defined table maps. These are the maps between the persistent classes and database tables. ❑ The Property Maps view displays the name of the property maps that are defined for each table map. The property maps included are the class attribute to database column mappings as well as class associations to table relationship mappings. ❑ The Information view (not labeled) is a read-only view that provides descriptive information in a given context. For example, if only the name of a data store map is selected, it will provide statistics about that data store map The mapping of each persistent class is described in the Map Browser. A map is required for each persistent model class. The columns to be read for each attribute and the foreign keys for each relationship are also specified in the Map Browser. Chapter 4. Persistence Builder Tools 51 The first step in defining the mapping for a class is to define a table map. The Map Browser supports basically three mapping methods: one class to a single table, one class to multiple tables, and multiple classes to one table. Most mappings use the first method (one class to a table), but a class with many attributes of different access characteristics might be mapped to multiple tables. The mapping of multiple classes into one table is used for inheritance, where classes with similar attributes are mapped into a shared table. (You can also map an inheritance structure into individual tables for each class.) A table map specifies the name of the underlying database table and the mapping of class attributes and relationships to table columns. Supporting Editors The Map Browser provides the Property Map Editor (Figure 23) to view or modify the mapping, and type of mapping of an attribute of a Class in the Model to a column of a Table in the Schema. Figure 23. Map Browser: Property Map Editor The mapping of a class includes both the attributes (Figure 23) and the associations. Most mappings are defined as simple, that is one attribute to one column. Complex mappings allow the specification of a composer class to “calculate” an attribute from multiple columns (see “Composers” on page 212 for more information). 52 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs The SQL Query Tool Figure 24 shows the SQL Query Tool. The SQL Query Tool can be used to execute SQL code against the connection used by a generated data store, and is useful during development to verify interactions with the database. Figure 24. VisualAge Persistence SQL Query Tool The SQL Query Tool presents two views: ❑ The Text Entry view in the top pane allows entry and execution of SQL statements. ❑ The Results view in the bottom pane is a read-only view that displays the results of SQL statements executed from the text entry view. When the SQL Query Tool is opened, the name of a data store must be entered, and then a connection to the corresponding database is made. The name of the data store is displayed in the window title. Chapter 4. Persistence Builder Tools 53 You can enter multiple SQL statements in the top pane. To execute a statement you select its text and Execute SQL from the pop-up menu. Note: You have to commit updates explicitly by executing a COMMIT statement after any insert, delete, or update statements. If you do not commit the data, updates table rows are locked. The Status Tool Figure 25 shows the Status Tool (or Persistence Status Browser). The Status Tool can be used to monitor the transactions, views, and caches in the system, and to reset the state of various components within the selected execution context. It is intended to identify potential errors in the design of a data store. This tool is useful during development, testing, and debugging. Figure 25. VisualAge Persistence Status Query Tool The Status Tool presents a single view where results of various system interrogations are supplied. For example, the state of a database connection can be interrogated, statistics on persistent objects or home collections can be analyzed, and tracing can be activated. 54 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Tracing Tracing of SQL calls and internal activities can be activated through the Status Tool using the Trace menu. The options are: ❑ No Trace ❑ Basic Trace (SQL calls, also called level one) ❑ Detailed Trace (SQL calls and internal activities, also called level two) Alternatively tracing can be set in the application through the Trace class: com.ibm.vap.RuntimeTools.Trace.traceOn(); com.ibm.vap.RuntimeTools.Trace.traceLevelTwo(); com.ibm.vap.RuntimeTools.Trace.traceOff(); start tracing (level one) set trace level two stop tracing In VisualAge for Java Version 3.02 a new trace level three for links has been added and thread information can optionally be included in the trace output. Chapter 4. Persistence Builder Tools 55 56 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 5 Simple Object Model: Customer This simple object model (Figure 26) looks at just one class, the Customer class, with no associations. It explores both the forward (top-down) and the backward (bottom-up) development paths, before showing how to use the generated beans in the visual programming environment. Customer customerId title firstName lastName Figure 26. Class Diagram for the Simple Model © Copyright IBM Corp. 2000 57 Forward Development Path In this section we describe the top-down approach. This approach always starts with these two steps: 1. “Define the Model Metadata” (introduces the Model Browser) 2. “Generate Domain Classes and Interfaces” on page 64 The continuation depends on whether a workspace schema or a relational SQL schema is used. Workspace Schema: 3. “Generate Service Classes for Workspace Schema” on page 67 4. “Verify the Generated Classes in the Workspace Schema” on page 69 Relational SQL Schema: 3. 4. 5. 6. “Generate Database Schema” on page 72 “Generate the DDL and the Database Tables” on page 76 “Generate Service Classes for Relational SQL Schema” on page 79 “Verify the Generated Classes for Relational SQL Schema” on page 82 Between these main sections there are other sections that explain the “Generated Domain Classes and Interfaces” on page 65, the “Generated Service Classes for Workspace Schema” on page 68, and the “Generated Service Classes for Relational SQL Schema” on page 81. The section “Activate a Datastore” on page 81 explains how to switch between different service layers for one and the same model, and the chapter “Verify the Generated Classes for Relational SQL Schema” on page 82 contains additional useful code pieces showing how to retrieve, update, or delete a single business object. The section “SQL Query Tool” on page 84 finally shows another way to access the data whether in the memory datastore or in a relational database. Define the Model Metadata The starting point for defining the model is the Model Browser. The model is defined in five steps: 1. 2. 3. 4. 5. 58 “Create New Model” “Define Classes” “Define Attributes” “Define Relationships” “Save the Model Metadata” VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Create New Model The first step is to create and save a new model by using the Model Browser. From the Workbench choose Workspace -> Tools -> Persistence Builder Tools -> Browse Models to open the Model Browser (Figure 27). Select Models -> New Model... or New Model... from the Models list pop-up menu to create the new model. When prompted for the model name, type Simple. Figure 27. Model Browser: Create a New Model Define Classes Once the model is created, model classes can be added. In this first sample only one class is defined: the Customer class. In the Model Browser select the Simple model and Classes -> New Class... (or New Class... from the Model Classes list pop-up menu) to open the Class Editor (Figure 28). Figure 28. Model Browser: Create a New Class Chapter 5. Simple Object Model: Customer 59 In the Class Editor enter Customer as the new class name and click on OK (Figure 29). Figure 29. Class Editor Define Attributes The Attribute Editor is used to add a new attribute to a class. Make sure the correct model and class are selected and use one of the following ways to open the Attribute Editor: ❑ Open the Class Editor of the class by selecting Classes -> Edit Class... (or Edit Class... in the pop-up menu). In the Class Editor on the Attributes notebook page click on New... (see Figure 29). ❑ In the Model Browser select Attributes -> New Attribute... or select New Attribute... in the pop-up menu (Figure 30). Figure 30. Model Browser: Create a New Attribute 60 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 31. Attribute Editor In the Attribute Editor (Figure 31) specify the name and type, and if the value is required. For the Customer class add the attributes listed in Table 3. Figure 32 shows the resulting Customer class in the Model Browser. Table 3. Attributes of Customer Class Name Type Required (comment) customerId String yes (object id) title String yes firstName String yes lastName String yes Figure 32. Model Browser: Customer Class Scroll through the list of attributes and through the textual summary at the in the bottom frame. Chapter 5. Simple Object Model: Customer 61 The textual summary shows at its very top the information Oid: Undefined. This means that there is no object identifier defined for this class. If you try to generate the supporting model code at this point you will get an error message saying that no object identifier was defined. In the customer sample we use the customerId attribute as object identifier. Use the class editor to select the customerId attribute and make it the object identifier (Figure 33). Figure 33. Class Editor with Object Identifier Define Relationships There are no relationships to be defined in this sample. You will see later how to define relationships when we introduce the BankAccount class. Save the Model Metadata Up to this point the model was not saved. In the Model Browser select the Simple model and make sure no class is selected. Then the textual summary shows the message Storage Status - Storage Class: No associated storage class, Entity is Dirty and should be saved (Figure 34). 62 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 34. Model Browser with Unsaved Model To save the model make sure in the Model Browser the Simple model is selected, then select Models -> Save Model... (or Save Model... from the pop-up menu). In the Save Model SmartGuide enter a project name (we used ITSO SG245426 Samples), itso.vap.simple.metadata as package name, and SimpleModel as class name (Figure 35), then click on Finish. This action creates the new project, package, and class if they do not exist. After any changes the model should be saved again, of course. Figure 35. Save Model SmartGuide After the model has been saved, it can be loaded by other developers into their workspace. To make the model visible in their own Model Browsers, they need to select Models -> Load Available Models. Saving the model also Chapter 5. Simple Object Model: Customer 63 enables you to take advantage of the library management functions, for example, versioning. Generate Domain Classes and Interfaces Now it’s time to generate the Java code for the model. In the Model Browser select the Simple model, and Models -> Generate... (or Generate... from the pop-up menu) to open the Generation Options SmartGuide. On the first notebook page select Domain Classes and Interfaces as type of generation and click on Next. Figure 36 shows the second page of the notebook with the Generation Options. Figure 36. Generation Options SmartGuide for Domain Classes and Interfaces Enter your project name and itso.vap.simple.domain as package name. Make sure the Generate Bound Bean Properties option is checked in order that the property change events get generated. (This checkbox was labeled Generate Bean Info in Version 2 and did not make clear what was generated in the business object class.) Leave <Default> as class name for the default persistence class root. You should only change the default persistence class root to a subclass of the 64 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs standard class when your business objects have to implement additional interfaces required by other frameworks. Notice Changing the persistence class root involves quite a bit of manual effort. If you use your own class root you have to implement the interfaces of VapEntityBeanImpl yourself. We did not experiment with other class On the third page select the classes and associations you want code to be generated for (in this sample only the Customer class) and then click on Finish. This page allows you later on to generate code selectively for changed or new elements. Generated Domain Classes and Interfaces The Persistence Builder generates for each model class three Java interfaces and four Java classes (Figure 37). Additionally it creates a class for each navigable role a class plays in an association. Figure 37. Generated Domain Classes and Interfaces for the Customer Class Here is a description of the interface and classes: Customer The Customer interface is a direct mapping from the (model) class Customer to a Java interface. It generates getters and setters following the JavaBeans standard for each of its attributes. If the class was associated to other classes there would be one getter and setter for each role the class plays in these associations. CustomerKey The CustomerKey class is used to represent the keys to access instances of the Customer class. It wraps the object Chapter 5. Simple Object Model: Customer 65 ID inside an object, and it uniquely identifies the instances of the customer class. CustomerHome The CustomerHome interface describes the behavior of objects that will be able to create an instance of the Customer class, look up an instance by key and query all the instances. These HomeCollections conform to the EJB specifications and adhere to the interfaces specified in Component Broker. CustomerHomeImpl CustomerHomeImpl is an implementation of the CustomerHome interface, following the Singleton pattern (see “Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, published by Addison-Wesley Professional Computing Series, ISBN 0-201-63361”). CustomerImpl The CustomerImpl class is the generated implementation of the Customer. It is the so-called EJBObject. The separation from the CustomerBean allows the use of distributed computing capabilities of Enterprise JavaBeans. Furthermore, this class is important for version management; it is the so-called Business Object Shell. You will use instances of this class in the application programming layer. CustomerBean CustomerBean represents the generated EntityBean. Together with the CustomerImpl class which conforms to EJBObject, you have the key parts for using the Customer class in an Enterprise JavaBeans environment. Furthermore, this class is important for version management, it is the so-called Business Object Version. CustomerServiceObjIfc ServiceObject classes must implement the CustomerServiceObjIfc interface in order to get registered as a class that produces objects that can handle the link with the underlying transactions and datastore. Notice The Persistence Builder implements key parts of the Enterprise JavaBeans specification. Movement toward full compliancy to the EJB specification will continue in subsequent product releases. 66 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Generate Workspace Schema This section shows how to use the workspace schema which allows you to test the model without the need of a database management system, while “Generate Database Schema” on page 72 shows how to generate a schema for relational databases and how to create tables from the Schema Browser into a relational database. Note that you can also directly generate the database schema without having a workspace schema. The purpose of using the workspace datastore is to simplify the early development stage: immediate testing is possible, no database definition or alterations are required, developers do not interfere with each other, and sample test data sets can be stored. When using the workspace datastore there is no requirement for a schema and a map, because the business objects do not have to be mapped to a specific database schema. The workspace datastore is a pure memory datastore and therefore requires no mapping. This means, once the domain classes and interfaces have been generated, you can directly generate the data service classes and interfaces for the workspace schema. The workspace datastore can be saved in serialized form into a file on your workstation. The saved objects are recreated when the same datastore is activated again. (See “Notices” on page 71 for more details.) Generate Service Classes for Workspace Schema To generate the workspace schema open the Model Browser and make sure the Simple model is selected. Select Models -> Generate... (or Generate... from the pop-up menu) to open the Generation Options SmartGuide. On the first page select Data Service Classes and Interfaces and click on Next. This option is not enabled if the domain classes and interfaces have not yet been generated. On the next page select Workspace Schema, enter itso.vap.simple.wsservices as package name, and click on Next (Figure 38). In Version 3 you can specify the name of a file (default is SimpleLclImage.PB) where you can save the memory datastore in serialized object format. This file is located in the project resources directory of your project: d:\IBMVJava\ide\project_resources\ITSO SG245426 Samples In Version 2 the file with the serialized objects is named lcl_db and is located in the d:\IBMVJava\ide\program subdirectory. See “Notices” on page 71 for more information on the serialized object file. Chapter 5. Simple Object Model: Customer 67 Figure 38. Generation Options SmartGuide for Workspace Schema On the last page select all the elements you want code to be generated for (only the customer class in this sample) and click on Finish to start service code generation. Generated Service Classes for Workspace Schema Three service classes are generated (Figure 39): Figure 39. Generated Service Classes for the Simple Workspace Schema 68 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Within one set of service classes Persistence Builder generates one datastore class (‘ModelnameDataStore’), and for each model class it generates a DataObject class (‘ModelnameClassnameDataObject’) and a ServiceObject class (‘ModelnameClassnameServiceObject’). SimpleDataStore Responsible for the database connection. The datastore object is related to the homes of the business objects. SimpleCustomerDataObject Contains the retrieved or to-be-stored data of a business object in a format that is used by the persistent store. SimpleCustomerServiceObject Implements the various database operations (create, retrieve, update, delete) and methods to map data objects to and from the persistent store. Verify the Generated Classes in the Workspace Schema At this point everything that you need for the development of applications that use business objects as constructed in the previous sections is available. The Persistence Builder runtime framework can be used to develop applications manually, or by using tools such as the Visual Composition Editor, where you can manipulate the required components as Java beans. In the following section we describe some basic constructs to access the framework and to test the model manually. Scripting Your code will always contain these basic steps (Figure 40): // activate datastore itso.vap.simple.wsservices.SimpleDataStore.singleton().activate(); // begin transaction com.ibm.vap.Transactions.Transaction.begin(); // Retrieve and update business objects // ... // commit transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 40. Code Skeleton for Datastore and Transaction Chapter 5. Simple Object Model: Customer 69 1. 2. 3. 4. Activate the datastore Begin a transaction Retrieve and update business objects Commit the transaction There is only one active instance of a datastore available in one Java Virtual Machine. Get this instance by using the singleton method of the corresponding class. The home (collection) classes represent the logical storage and let you create, retrieve and delete instances. These home classes also have only one instance, so use the singleton method as well. Figure 41 shows a complete script to test the simple model in the workspace. // activate datastore itso.vap.simple.wsservices.SimpleDataStore.singleton().activate(); // begin transaction com.ibm.vap.Transactions.Transaction.begin(); // create a Customer itso.vap.simple.domain.Customer cust; cust = (itso.vap.simple.domain.Customer) itso.vap.simple.domain.CustomerHomeImpl.singleton().create(); cust.setCustomerId("101"); cust.setFirstName("Greg"); cust.setLastName("Behrend"); cust.setTitle("Mr."); // create another Customer cust = itso.vap.simple.domain.CustomerHomeImpl.singleton().create("102"); cust.setFirstName("Daniel"); cust.setLastName("Peter"); cust.setTitle("Mr."); // commit transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); // begin new read-only transaction com.ibm.vap.Transactions.Transaction.beginReadOnly(); System.out.println("All Customers:"); itso.vap.simple.domain.Customer c; java.util.Enumeration enumCustomers = itso.vap.simple.domain.CustomerHomeImpl.singleton().allInstances().elements(); while(enumCustomers.hasMoreElements()) { c = (itso.vap.simple.domain.Customer)enumCustomers.nextElement(); System.out.println(c.getTitle() + " " + c.getFirstName() + " " + c.getLastName()); } // commit transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); // save the memory datastore to disk com.ibm.vap.Persistence.DataStore.reset( itso.vap.simple.wsservices.SimpleDataStore.singleton() ); Figure 41. Scrapbook: Local Database Test (SimpleLocal.scrap) 70 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Load the SimpleLocal.scrap file into a scrapbook page, run it, and watch the output in the console. This script creates two Customer instances in two different ways: the first one anonymously, the second one using a primary key. After the commit we start a read-only transaction and iterate over all existing Customer objects and print out their attributes. We also provide a SimpleLocalFind.scrap file to retrieve a customer. Notices ❑ Each page in the scrapbook has its own execution context. You must activate a datastore in every scrapbook page separately. ❑ Read-only transactions must also be committed (or rolled back) when they are not used anymore, otherwise you will have multiple active read-only transactions floating around. ❑ The Persistence Builder lets you save the memory datastore data to a file so that you can use the data after the process ended: • In Version 3 you can specify the file name and the default is <modelname>LclImage.PB. Select Page -> Run in, type Customer and select the itso.vap.simple.domain package and click OK. This sets the context for the run and also the project resources directory where the file is stored. • In VisualAge for Java Version 2 the file was named lcl_db and was located in the ide\program subdirectory of your VisualAge for Java installation. By calling the following method the persistent objects of our sample get serialized and saved to disk: com.ibm.vap.Persistence.DataStore.reset(datastoreObject); By activating a memory datastore the file is read again and the objects get deserialized. Errors occur when data from one model is serialized and then deserialized during activation of a datastore for a different or changed model. Should you ever run into such problems just delete the file. ❑ Because each page in the scrapbook has its own execution context, it is necessary that after you have saved the data from within one scrapbook page you first activate the datastore in the other page to see the saved data of the first page. Chapter 5. Simple Object Model: Customer 71 Generate Database Schema This section demonstrates the use of a physical database. In our sample we use the IBM DB2 database. The Simple model created in the previous sections will not be changed. We just want to use IBM DB2 as datastore instead of the memory datastore. To create service classes for a specific database you need first the database schema and, of course, the mapping between that schema and the model. As a preparation make sure the DB2 JDBC drivers are loaded into your Workspace and DB2 is running. If the drivers are not yet loaded, see “DB2 Access from VisualAge for Java” on page 14 for instructions on how to load the drivers. To generate the database schema open the Model Browser and make sure the Simple model is selected. Select Models -> Generate and select the Generate schema from model radio button to generate the schema. For now we leave the defaults in the next dialog panel and click on Finish. Open the Schema Browser by selecting Persistence Builder Tools -> Browse Schemas from the Model Browser menu bar to look at the generated schema (Figure 42). Figure 42. Schema Browser with Generated Simple Schema Check and Edit the Generated Tables and Columns The next step is to check the tables and columns of the generated schema and if necessary to edit them. In the Schema Browser select the Simple schema, 72 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs the Customer table, and Tables -> Edit Table... to open the Table Editor (Figure 43). Figure 43. Table Editor for the Customer Table Change the qualifier to ITSO and the physical name to CUSTOMER00. If you leave the physical name entry field blank, then the name is also used as physical name. Notice Make sure the names of all elements in the schema conform to the naming rules of the database that is used, and, if necessary, specify explicitly a physical name. Note that in DB2 names must not contain blanks and the length is limited to 18 characters. This rule is often violated by the default names generated for foreign keys of associations. In the Table Editor select a column and click on Edit... to open the Column Editor (Figure 44) and change the column’s Physical name, Type, Converter, Length, Scale, and Allow nulls where necessary. You can also open the Column Editor for a column directly from the Schema Browser by just double-clicking on the column in the Column list or by selecting the column and Columns -> Edit Column. Chapter 5. Simple Object Model: Customer 73 Figure 44. Column Editor Use the VapTrimStringConverter for all CHAR columns, unless the data is sensitive to leading or trailing blanks. The VapTrimStringConverter best fits Java’s String behavior and GUIs, as it trims trailing blanks on read and pads them on write. Use the information listed in Table 4 to change the columns. Table 4. Columns for the Customer Table Column Name Type with Length Converter Allow nulls customerId CHAR(4) VapTrimStringConverter no firstName VARCHAR(30) VapConverter no lastName VARCHAR(30) VapConverter no title CHAR(3) VapTrimStringConverter no After making these changes save the schema. In the Schema Browser select the Simple schema and Schemas -> Save Schema. Enter your project name, itso.vap.simple.metadata as package name, and SimpleSchema as class name, then click on Finish. 74 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Review the Generated Map The map was generated together with the schema. Because type changes do not affect mappings and the mappings are OK, the only action is to save the map, after reviewing it. For the review, open the Map Browser (Figure 45) by selecting Persistence Builder Tools -> Browse Maps, for example in the Schema Browser. Select the map SimpleSimple and browse the textual summary of the map in the bottom pane of the browser. Select the Customer persistent class and the Customer table map to review the mappings in the list of property maps. The (a) marking indicates an attribute mapping, whereas the (r) marking would indicate a relationship mapping. Figure 45. Map Browser: SimpleSimple Map Note that the Persistence Builder concatenates the name of the model and the name of the schema to create the name of the map. If you create the map manually, you may prefer to use the shorter name Simple. In the Map Browser select the SimpleSimple map and DatastoreMaps -> Save Datastore Map to save the map. Enter your project name, itso.vap.simple.metadata as package name, and SimpleMap as class name, then click on Finish. Chapter 5. Simple Object Model: Customer 75 Generate the DDL and the Database Tables To implement DB2 based persistence we must have a database and the table for customer objects. Create Database First of all the empty database itself must be created. In our samples we use VAPSAMPL as database name. If DB2 is installed on your local machine, open a DB2 command window, start DB2 by entering db2start, and create the database with db2 create database VAPSAMPL. Otherwise ask your database administrator to create the database for you. Notice For the samples in the Persistence Builder Redbook we created the VAPSAMPL database, added the system defined user ITSO (with password itso) 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.2 and 6.1: Start the Command Center program, open a Control Center—with the “hierarchy” tool button, expand the hierarchy and work with the context menus on the hierarchy items. For example, on the DB2 item perform Start to start DB2, or on the Databases item perform Add... to add a database. If you need to start and stop a DB2 administration server, you can do this in a system command window with DB2ADMIN START or STOP. You can start and stop DB2 in a system command window with DB2START and DB2STOP. Create Table The next step is to create the physical database table. It is possible to view the DDL code that the Schema Browser would use to create the physical database tables. In the Schema Browser select the Simple Schema and Schemas -> Generate DDL Script for Schema Creation. This produces the DDL code and places it into the bottom pane of the Schema Browser (Figure 46). 76 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 46. Schema Browser: Generated DDL Script There are several ways to generate the tables in the physical database. For example, it would be possible to cut and paste the generated 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 select the Simple schema and Schemas -> Import/Export Schema -> Export Entire Schema to Database. This opens the Database Connection Info dialog (Figure 47). Enter the connection info using the relevant parameters given to you by your database administrator. As connection type use the appropriate DB2 JDBC driver, either COM.ibm.db2.jdbc.app.DB2Driver or COM.ibm.db2.jdbc.net.DB2Driver. Notice We assume that you are familiar with the basic concepts of JDBC and the existing different driver categories. A good start for beginners is the redbook Application Development with VisualAge for Java Enterprise, SG24-5081. If you use the COM.ibm.db2.jdbc.net.DB2Driver make sure that the DB2 JDBC Gateway is started. To start the gateway type DB2JSTRT port# from a command window. Chapter 5. Simple Object Model: Customer 77 Figure 47. Database Connection Information Once you click on the OK button, the Persistence Builder establishes a connection to the database, creates the table(s), and executes the necessary ALTER TABLE commands to create the primary and foreign key(s). Watch the output in the VisualAge for Java Console (Figure 48). Figure 48. Console Showing Results of Creating the Database Tables 78 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 48 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 the error message can safely be ignored. Generate Service Classes for Relational SQL Schema As soon as the database schema and the mapping between that schema and the model exist, the service classes for that particular database can be generated. Open the Model Browser and make sure the Simple model is selected. Select Models -> Generate... (or Generate... from the pop-up menu) to open the Generation Options SmartGuide. (You can also select Datastore Maps -> Generate Services from the menu bar in the Map Browser.) On the first page of the Generation Options SmartGuide select Data Service Classes and Interfaces and click on Next. On the next page select Relational SQL, enter itso.vap.simple.services as package name, and click on Next. Enter the database connection information by clicking on the Change button (Figure 49) and enter the required information as described in Figure 47. You must at least enter the connection type and the data source information. If you do not specify user ID and password the connection is attempted without a user ID and password. In Version 3 you can select a Connection retrieval preference option. The default is to use the specified user ID and password as in Version 2. The new options are: ❑ Supply the user ID and password at runtime. This option may be suitable for stand-alone applications. See “Database Connection with User ID and Password” on page 257 for more information. ❑ Use database connections from WebSphere connection pool. This option should be used for Web server applications implemented in servlets. See “WebSphere Connection Pools” on page 255 for more information. For now we will use the default and specify a user ID and password in the database connection information. Usually you select the Generate queries using parm marker bindings checkbox. The use of parm marker bindings is less dynamic than otherwise. The SQL prepare is done once, so there could be significant efficiency gains for often-used queries. We will always select this marker. Chapter 5. Simple Object Model: Customer 79 Select Casts for Join generation preference if your underlying SQL system can understand the CAST expression, which is usually the case. Now click on Finish to start code generation. new in Version 3 Figure 49. Generation Options for Service Classes Notice You will find the database connection information in the generated getConnectionSpec method of the SimpleSimpleDatastore class (see “Generated Service Classes for Relational SQL Schema” below). There are currently no setter methods which allow you to set the database connection information at runtime. If you need to set this information at runtime, then you must subclass the generated SimpleSimpleDatastore class and override the getConnectionSpec method. 80 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Generated Service Classes for Relational SQL Schema Several service classes for the relational SQL schema are generated (Figure 50): Figure 50. Generated Service Classes for the Relational SQL Schema In addition to the generated classes described in “Generated Service Classes for Workspace Schema” on page 68 you get an Extractor and an Injector class, both supporting the data transfer to and from the relational datastore, as well as a QueryPool class, in which all the SQL queries are stored. Activate a Datastore In Chapter 3, “Persistence Builder Concepts” on page 21 we introduced Application Layers and Persistence Layers. Both sections have a diagram (Figure 3 on page 24 and Figure 4 on page 25) with a similar pattern to successfully separate the model from the storage. Now, we want to make the same model code talk to and listen to various datastores. How does this happen? (See Figure 51.) The key to the answer is the dynamic connection at runtime by activating a datastore. But how do the homes know about the datastore and vice-versa? In the generation process, first, the model code is generated, with a special focus on the homes, the logical storage. After that, and with the knowledge of the homes, the services sets are generated, such as the services for the workspace schema or the schema for relational SQL schema and—most important—the datastore, the physical storage. Selecting and activating a datastore is like connecting the logical storage (the homes) with the physical storage (the datastore). At runtime, when a datastore is activated (when the datastore’s activate method is called), the Chapter 5. Simple Object Model: Customer 81 datastore and a corresponding service object are registered with each home singleton. From this behavior we can conclude: ❑ Many datastores can exist for one and the same model. ❑ Only one datastore can be active at a time. Home Collections of a Model Activate Datastore Services Sets Datastores Image Schema Relational Database Schema Stub/... Schema Figure 51. Conceptual View of Activating a Datastore Verify the Generated Classes for Relational SQL Schema Again, you can test the generated classes by running a script from within a scrapbook page. Load the SimpleDB2.scrap file into the scrapbook. It is nearly the same script as the workspace sample (Figure 41 on page 70); the only line that changed is the activation of the datastore: itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(); Run the script and watch again the output in the console. If you run the script twice you will—of course—get a database error, because you cannot insert a row twice with the same primary key. The scripts in Figure 52 and 82 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 53 show how to retrieve, update, and delete a single Customer object, while the script in Figure 54 shows how to retrieve all Customer objects. Now you have all the basic elements you need to be able to experiment with the generated code. // activate datastore itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(); // begin transaction com.ibm.vap.Transactions.Transaction.begin(); // update a Customer itso.vap.simple.domain.Customer cust; try { cust = (itso.vap.simple.domain.Customer) itso.vap.simple.domain.CustomerHomeImpl.singleton().find("101"); cust.setFirstName("Ueli"); cust.setLastName("Wahli"); } catch (Exception e) { System.out.println(e); } // commit transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 52. Scrapbook: Database Retrieve and Update (SimpleUpdate.scrap) // activate datastore itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(); // begin transaction com.ibm.vap.Transactions.Transaction.begin(); // delete a Customer itso.vap.simple.domain.Customer cust; try { cust = (itso.vap.simple.domain.Customer) itso.vap.simple.domain.CustomerHomeImpl.singleton().find("101"); cust.remove(); } catch (Exception e) { System.out.println(e); } // commit transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 53. Scrapbook: Database Retrieve and Delete (SimpleDelete.scrap) Chapter 5. Simple Object Model: Customer 83 // activate datastore itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(); // begin new read-only transaction com.ibm.vap.Transactions.Transaction.beginReadOnly(); System.out.println("All Customers:"); itso.vap.simple.domain.Customer c; java.util.Enumeration enumCustomers; enumCustomers = itso.vap.simple.domain.CustomerHomeImpl.singleton().allInstances().elements(); while(enumCustomers.hasMoreElements()) { c = (itso.vap.simple.domain.Customer)enumCustomers.nextElement(); System.out.println(c.getTitle() + " " + c.getFirstName() + " " + c.getLastName()); } // commit transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 54. Scrapbook:Retrieve All Customers (SimpleReadAll.scrap) Notice There exists a potential problem that you have to be aware of: If, after you changed an object, you call the allInstances method on the object’s home class within the same uncommitted transaction, your changes will be lost. The allInstances method always reads the data from the database. The SimpleDeleteFail.scrap file illustrates the case. SQL Query Tool Now is a good moment to introduce the SQL Query Tool. Start it by selecting Workspace -> Tools -> Persistence Builder Tools -> Launch SQL Query Tool. Make sure to select the correct datastore in the next dialog. If you built the sample shown in the previous sections, there should be two datastores for the customer sample; one for the local workspace schema (itso.vap.simple.wsservices.SimpleDataStore), and one for the DB2 schema (itso.vap.simple.services.SimpleSimpleDataStore). Once the SQL Query Tool window is opened you can switch to another datastore by selecting Database -> Switch Datastore if necessary. In the upper pane you can enter one or several SQL queries. Mark the portion you want to execute and select Execute Sql from the pop-up menu of the upper pane. The result of the execution will be displayed in the lower pane (Figure 55). 84 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 55. SQL Query Tool Notices ❑ The SQL Query Tool can only be used to query relational databases. ❑ The SQL Query Tool does not operate in auto commit mode. Therefore do not forget to execute the commit or rollback statement respectively at the end of updates in the SQL Query Tool. If you do not commit after an update statement, the row will stay locked on the database and cannot be accessed outside the SQL Query Tool. Persistence Status Browser The Persistence Status Browser offers numerous options and outputs that are very helpful during experimenting, testing, and debugging. Start it by selecting Workspace -> Tools -> Persistence Builder Tools -> Launch Status Tool and make sure to select the correct execution context in the next dialog. This tool can only be used while an application or scrapbook is active. The most important facilities are: ❑ Enable tracing in the Trace menu. This option displays output on database interactions to the Console window. ❑ Select View -> Transaction Statistics to see the status of your transactions and how they are related to each other. Chapter 5. Simple Object Model: Customer 85 ❑ Sometimes it is useful to discard all outstanding transactions or even to globally reset the Persistence Builder. These operations can be performed from within the Persistence Builder Status Browser by selecting Cleanup -> Release All Transactions and Cleanup -> Global Persistence Builder Reset respectively. ❑ Change the execution context by selecting View -> Change Execution context or by opening an additional Persistence Status Browser for the new context. In the chapters that follow we will show more details about the Persistence Status Browser after we introduce advanced topics such as concurrent and nested transactions. Backward Development Path The objective of this section is to show the bottom-up approach. After completing this section you will have a second set of model, schema, map, domain, and service classes for the simple customer sample in your workspace. However, in the chapters that follow we will build again upon the results of the top-down approach. For simplicity the VAPSAMPL database and the CUSTOMER00 table that were created in the previous sections are also used here. But now the starting point is the existing database table CUSTOMER00. In the bottom-up approach the following steps are performed: 1. 2. 3. 4. 5. “Import Schema from Relational Database” on page 86 “Generate Model Definition and Mapping” on page 88 “Generate Domain Classes and Interfaces” on page 89 “Generate Service Classes” on page 89 “Verify the Generated Classes” on page 90 As preparation make sure DB2 is running and you have access to the VAPSAMPL database and the CUSTOMER00 table. Import Schema from Relational Database Open the Schema Browser and select Schemas -> Import / Export Schema -> Import Schema from Database and enter SimpleBU as the schema name. When prompted, use the same database connection information as in the previous sections (Figure 47 on page 78). 86 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs In the Select Tables dialog (Figure 56) select the ITSO qualifier and click on the Build Table List button. Select the CUSTOMER00 table in the Tables list and click on OK. Figure 56. Select Tables Dialog Browse through the SimpleBU schema that was imported (Figure 57). Remember, two greater-than signs “>>” at the left of a column name indicate a primary key column. Whenever the Persistence Builder cannot detect a primary key column for a table (because no primary key definition exists in the database) you have to add it manually using the Table Editor, otherwise you will get an error during model and map generation. Figure 57. Schema Browser: Imported SimpleBU Schema Chapter 5. Simple Object Model: Customer 87 Change the name (not the physical name) of the CUSTOMER00 table to CUSTOMER using the table editor, otherwise you will end up later with a domain class called Customer00. In the Column Editor change the converter to VapTrimStringConverter where necessary. Save the schema in your project, itso.vap.simplebu.metadata package, with SimpleBUSchema as the class name. Generate Model Definition and Mapping In the Schema Browser select the SimpleBU schema and Schemas -> Generate Model from Schema. This generates the model and the map as well. The model has been given the same name as the schema, namely SimpleBU (Figure 58). Look at the attribute names in the textual summary created for the new Customer class. Note that the attribute names have been taken directly from the database table column names. You can rename the attributes (Rename Attribute action) if a project requires more meaningful names and the mapping is adjusted automatically. Figure 58. Model Browser: Generated SimpleBU Model Save the model in your project, itso.vap.simplebu.metadata package, with SimpleBUModel as class name. The Persistence Builder generated the mappings between the model and the schema. Open the Map Browser window and analyze its contents (Figure 59). 88 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 59. Map Browser: Generated SimpleBUSimplebu Map The Persistence Builder generated a SimpleBUSimpleBU map, the concatenation of the model and schema names. If you prefer a short name, delete the map and enter the mapping between model and schema by hand. Generate Classes for the Model and Services The code to support the backward development path sample is generated in two steps: ❑ Generate Domain Classes and Interfaces Open the Model Browser, select the SimpleBU model and Models -> Generate. In the Generation Options dialog select Domain Classes and Interfaces and click on Next. On the next page enter your project name, itso.vap.simplebu.domain as package name, and then click on Finish. ❑ Generate Service Classes Select the SimpleBU model and Models -> Generate. In the Generation Options dialog select Data Service Classes and Interfaces and click on Next. On the next page select Relational SQL, enter itso.vap.simplebu.services as package name. On the last page enter the database connection information by clicking on the Change button (see Figure 47 on page 78) and select Generate queries using parm marker binding. Finally click on Finish to start code generation. Chapter 5. Simple Object Model: Customer 89 Verify the Generated Classes You can test the generated classes by running a script from a scrapbook page. You can run all the scripts from the previous sections with minor modifications: ❑ The package names are different (itso.vap.simplebu.*) ❑ Change the line that activates the datastore (SimpleBUSimpleBUDataStore). ❑ The names of the getter and setter methods of the customer class are spelled slightly different, for example, getFirstname instead of getFirstName. We provide one sample script. Load the file SimpleBUReadAll.scrap into a scrapbook page and run it (Figure 60). // activate datastore itso.vap.simplebu.services.SimpleBUSimpleBUDataStore.singleton().activate(); // begin read-only transaction com.ibm.vap.Transactions.Transaction.beginReadOnly(); System.out.println("All Customers:"); itso.vap.simplebu.domain.Customer c; java.util.Enumeration enumCustomers; enumCustomers = itso.vap.simplebu.domain.CustomerHomeImpl.singleton().allInstances().elements(); while(enumCustomers.hasMoreElements()) { c = (itso.vap.simplebu.domain.Customer)enumCustomers.nextElement(); System.out.println(c.getTitle() + " " + c.getFirstname() + " " + c.getLastname()); } // commit transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 60. Scrapbook: Retrieve All Customers (SimpleBUReadAll.scrap) 90 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Visual Programming This section introduces visual programming using the Visual Composition Editor in connection with the Persistence Builder. Basic operations on single or collections of Customer objects are explored using both AWT and advanced Swing components such as JTable. Notice We assume that you are familiar with the basic concepts of visual programming using VisualAge for Java. A good start for beginners is the redbook Programming with VisualAge for Java Version 2, SG24-5264. Retrieve Customer This first GUI sample shows how to retrieve one customer from the memory datastore and display its properties. We use the classes generated in “Forward Development Path” on page 58 in this section. The code pieces that you would execute in the scrapbook to retrieve one Customer object are shown in Figure 61 and will be used accordingly in this GUI sample. // activate datastore itso.vap.simple.wsservices.SimpleDataStore.singleton().activate(); // begin read-only transaction com.ibm.vap.Transactions.Transaction.beginReadOnly(); System.out.println("Found customer:"); itso.vap.simple.domain.Customer c; try { c = itso.vap.simple.domain.CustomerHomeImpl.singleton().find("102"); System.out.println(c.getTitle() + " " + c.getFirstName() + " " + c.getLastName()); } catch(Exception e) { System.out.println(e); } // commit transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 61. Scrapbook: Find a Customer (SimpleLocalFind.scrap) First of all the GUI itself must be built. Create a new class called RetrieveCustomerView in a new itso.vap.simple.gui package in your project. Specify JFrame as superclass and add the GUI components as shown in Chapter 5. Simple Object Model: Customer 91 Figure 62. (This is a capture of the visual composition inside of the Visual Composition Editor.) 1 4 5 2 6 3 Figure 62. Visual Composition: RetrieveCustomerView Class Similar to the script you need a few additional non-visual beans: ❑ The CustomerHomeImpl singleton is needed later to call its find method. Therefore add a bean of type itso.vap.simple.domain.CustomerHomeImpl to the free-form surface (1). Save the view and browse the generated code; you will find a call to the CustomerHomeImpl.singleton method. ❑ Next add a variable of type itso.vap.simple.domain.Customer. This variable will be used to hold the result of the find method (2). (Do not use the transacted variable; we will discuss it later.) ❑ The third bean is a ReadOnlyTransaction bean (3). You can find this bean on the Composition Editor’s bean palette (Figure 63). Save the view again and browse the generated code; you will find a call to the Transaction.beginReadOnly method during initialization. Figure 63. Persistence Builder Palette: ReadOnlyTransaction Of course you can also use the memory datastore in applications or prototypes that have a GUI, and that is what we use for this example. As the script in “Verify the Generated Classes in the Workspace Schema” on page 69 92 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs showed, the datastore must be activated once per execution context (scrapbook page, application, applet, servlet). Insert the activation code in the user code begin {1} section of the initialize method. If the initialize method does not exist yet, just save your view and the method is generated. private void initialize() { // user code begin {1} itso.vap.simple.wsservices.SimpleDataStore.singleton().activate(); // user code end ... Now you have all the infrastructure; only the connections are missing: ❑ When the Find button is clicked, the find method of the CustomerHomeImpl bean should be triggered with the text property of the customerId text field as parameter (4). ❑ The return value of the find method call (normalResult) must be placed into the customer variable (5). ❑ The properties of the customer variable bean must be displayed in the corresponding text fields. Connect the properties accordingly with the Customer variable as the source of the connections (6). ❑ Uncomment the two code lines in the generated handleException method to see error messages in the Console window. Now save the view and run it. You can also run scripts in a scrapbook page to insert additional Customer objects into the memory database. When you try to find a Customer object that does not exist, an exception message is displayed in the Console window: com.ibm.vap.common.VapReadFailureException: Cannot read from the database java.lang.Throwable(java.lang.String) java.lang.Exception(java.lang.String) javax.ejb.FinderException(java.lang.String) javax.ejb.ObjectNotFoundException(java.lang.String) com.ibm.vap.common.VapReadFailureException ... In a real application you would—of course—show a message box to the user. You can safely ignore the NullPointerExceptions that are thrown during initialization. They are caused by the fact that during initialization the Customer variable still references null and its properties are propagated to the text fields. Browse the code again so that you get familiar with it. You should find all the code pieces from the script above also in the generated code. Chapter 5. Simple Object Model: Customer 93 Version your work done so far. Notices ❑ If you experience the “Class Not Found” message, check the class path. Select the class in the Workbench and Run -> Check Class Path from the pop-up menu. Check that these projects are in the list: IBM Persistence EJB Library JFC Class Libraries <== if you use Swing components VisualAge Persistence VisualAge Persistence Common Runtime VisualAge Persistence Extras You can click on the Compute Now button to have the class path determined by the system. ❑ If the application does not show the expected behavior, it is always a good idea to uncomment the two code lines in the generated handleException method. All uncaught exceptions will pass through this method and will be printed to System.out, that is the Console window in the VisualAge for Java environment. This often helps to locate the problem. ❑ In order to turn tracing on select Trace -> Basic Trace or Trace -> Detailed Trace in the Persistence Status Tool or by calling the static methods traceOn and traceLevelTwo respectively of the com.ibm.vap.RuntimeTools.Trace class. ❑ If this does not help use the VisualAge for Java debugger. ❑ Of course you can show—and you will certainly do that in real applications—the exceptions thrown by the Persistence Builder frameworks in an error message box to the user. For that purpose use the exceptionOccurred event of the corresponding connection to trigger your favorite exception handling mechanism. We omit these connections in our samples for the sake of clarity. Edit and Save a Customer We want to enhance the solution of the previous section and allow the user to change a Customer object and save the changes in the datastore. Open the RetrieveCustomerView to the Composition Editor and add two buttons, Commit and Rollback, to the GUI as shown in Figure 64. 94 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 1 3 4 2 Figure 64. Retrieve Customer View with Edit and Save Update the visual composition: ❑ The current Customer object must be notified of the changes made by the user in the text fields. Therefore open each of the three property-to-property connections and specify keyReleased as target event (for the text fields).(1) ❑ Because we want to change persistent objects we need a different transaction bean. Replace the ReadOnlyTransaction bean with a TopLevelTransaction bean (2), which you can also find on the Composition Editor palette (Figure 65). Save the view and you will find a call to Transaction.begin instead of Transaction.beginReadOnly in the generated code. Figure 65. Persistence Builder Palette: TopLevelTransaction Chapter 5. Simple Object Model: Customer 95 ❑ Connect the actionPerformed events of the Commit and Rollback buttons to the commit and rollback method of the TopLevelTransaction bean (3). ❑ Because this application connects to the memory datastore you must ensure that the datastore is saved to disk when the window is closed. Create an event-to-code connection from the windowClosing event of the window to a new method called resetDatastore with the following body (4): com.ibm.vap.Persistence.DataStore.reset( itso.vap.simple.wsservices.SimpleDataStore.singleton() ); That’s it. When testing the application you can observe somewhat surprising behavior: ❑ If you try to find another customer after a commit or rollback of the transaction you will get a VapTransactionRequiredException. Once a transaction is committed or rolled back it is dead and cannot be used again. In our GUI sample we consequently have to create a new transaction after each commit or rollback, but this will be shown later. For the time being just close the application after a commit or rollback and start it again. ❑ You can find and change several Customer objects within the same transaction. When you find a Customer object that you changed earlier within the same transaction you will see the changed version of that Customer object. Create and Delete a Customer This section shows how to enhance the GUI to allow creation and deletion of Customer objects. For that purpose add two buttons Create and Delete (Figure 66). Remember from the earlier code examples that the home implements the functionality to create new business objects, while the delete (remove) operation is the business object’s responsibility. ❑ Connect the actionPerformed event of the Delete button to the remove method of the Customer variable (1). ❑ Connect the actionPerformed method of the Create button to the create(String) method of the CustomerHomeImpl and pass the text attribute of the customerId text field (2). Connect the normalResult (return value) to the this property of the Customer variable. Note that the attributes of a new customer cannot be set with this implementation. More connections would be required to copy the text fields into the Customer variable after it has been created. 96 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 2 1 Figure 66. Retrieve Customer View with Create and Delete During the test you can observe this behavior: ❑ A newly created Customer object cannot be found by calling the find(String) method before the transaction is committed. For now, you have to commit the transaction, close the window, and start the application again. ❑ A deleted object can still be accessed as long the transaction has not been committed, because a call to the object’s remove method just marks the object for deletion. Customer List Views The previous sections showed visual programming focussed on dealing with single Customer objects, while this section explores the handling of several customer objects at a time. We will use AWT and Swing components as well as special helper beans provided by the Persistence Builder. Customer List Using AWT One possibility to fill an AWT list with customer objects is to trigger a method that fills the list. Create a new CustomerAWTListView class derived from java.awt.Frame in the itso.vap.simple.gui package and lay out the GUI using a java.awt.List as shown in Figure 67. Chapter 5. Simple Object Model: Customer 97 1 Figure 67. Customer List Using AWT ❑ Again, you need a Transaction (here you can use a ReadOnlyTransaction) ❑ Make sure the datastore gets activated. Call within user code begin {1} section of the initialize method: itso.vap.simple.wsservices.SimpleDataStore.singleton().activate(); ❑ Create an event-to-code method from the actionPerformed event of the Refresh button to a new refreshList method (1). Note that the generated getCustomerList1 method returns the AWT list component in this view. public void refreshList() { try { getCustomerList1().removeAll(); itso.vap.simple.domain.Customer c; java.util.Enumeration enum; String entry; enum = itso.vap.simple.domain.CustomerHomeImpl. singleton().allInstances().elements(); while(enum.hasMoreElements()) { c = (itso.vap.simple.domain.Customer)enum.nextElement(); entry = c.getTitle() + " " + c.getFirstName() + " " + c.getLastName(); getCustomerList1().add(entry); } } catch (Throwable t) {handleException(t);} } Now you can test the new view. Customer List Using AWT and AwtListModel Rather than to fill the AWT list manually, we exploit one of the Persistence Builder helper beans, the AwtListModel (Figure 68). 98 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 2 4 1 3 Figure 68. Customer List Using AWT and AwtListModel As in the previous sample you must activate the datastore and you must have a transaction. But here the filling of the list happens automatically. You do not need the refresh method anymore. Instead you can make use of the AwtListModel class (1), which you can find in the com.ibm.vap.awt.list package. ❑ Connect the list property of the AwtListModel bean to the this property of the CustomerList visual bean (2). ❑ Connect the transaction property of the AwtListModel bean to the this property of the ReadOnlyTransaction (3). ❑ Connect the home property of the AwtListModel bean to the this property of the CustomerHomeImpl (4). Now, how does the list get filled automatically? When the view is initialized the property-to-property connections are fired. As consequence the setHome(HomeCollection) method of the AwtListModel is called with CustomerHomeImpl as parameter. This automatically calls the allInstances method of the CustomerHomeImpl and fills the CustomerList. The Customer objects displayed in the list are not very readable at this stage. To see meaningful data in the list you have to override the toString method of the CustomerImpl class: public String toString() { try { return getCustomerId() + "," + getTitle() + "," + getLastName() + "," + getFirstName(); } catch (Throwable any) { return "could not print: " + any; } } Chapter 5. Simple Object Model: Customer 99 If the user requires to trigger a refresh from time to time when the view is already open, you could trigger the setHome(HomeCollection) method of the AwtListModel explicitly using an event-to-method connection and pass the CustomerHomeImpl bean as parameter. If you want to execute a different query you can change the queryName and queryArguments properties in the property editor of the AwtListModel bean. For further details see Chapter 12, “Custom Queries” on page 221. Customer List Using a Swing JList and VapDefaultListModel If you want a Swing GUI with a JList, you can make use of the VapDefaultListModel helper bean, which can be found in the com.ibm.vap.swing.list package. Build the sample in the same way as the AwtListModel. Customer List Using a JTable and VapDefaultTableModel If you want to use a Swing JTable (Figure 69), use the VapDefaultTableModel helper bean, which is in the com.ibm.vap.swing.table package. Figure 69. Customer List Using a Swing Table The development of this sample is very similar to the previous list examples, but because the JTable is more flexible you have to define some additional properties. Create a new CustomerSwingTableView class derived from com.sun.java.swing.JFrame in the itso.vap.simple.gui package and lay out the GUI using a com.sun.java.swing.JTable as shown in Figure 70. 100 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 1 3 2 Figure 70. Customer Table Using Swing and VapDefaultTableModel The connections are similar to the previous examples: ❑ Connect the table property of the VapDefaultTableModel bean to the this property of the CustomerTable visual bean (1). ❑ Connect the transaction property of the VapDefaultTableModel bean to the this property of the ReadOnlyTransaction (2). ❑ Connect the home property of the VapDefaultTableModel bean to the this property of the CustomerHomeImpl (3). In addition you must specify the type of objects you want to show in the table, which columns you want to show, and whether they should be editable or not. For that open the property editor of the VapDefaultTableModel bean. When you select the columnIdentifiers property a special ColumnIdentifiers Editor opens (Figure 71). After specification of the Table Element Class (make sure to specify the class including its complete package name: itso.vap.simple.domain.CustomerImpl) the Available Column Identifiers list appear in the upper pane. Select the columns you would like to show in the JTable and add them one by one to the Selected Column Identifiers list. For each selected column identifier specify whether it should be editable using the Editable checkbox. Chapter 5. Simple Object Model: Customer 101 Figure 71. ColumnIdentifier Editor for VapDefaultTableModel Do not forget to add the datastore activation code. You are ready to run this example. Version your work done so far. 102 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 6 Simple Associations This chapter introduces one-to-many associations, while many-to-many associations will be introduced in Chapter 9, “Many-to-Many Associations” on page 165. The concept of associations is fundamental in the Persistence Builder and is specially supported from the model side through the database schema side. In this chapter we describe both the forward and the backward development path before the visual programming issues are covered. The sections of this chapter build on the results of the chapter “Simple Object Model: Customer” on page 57. Figure 72 shows the class diagram which is the starting point for the examples. In addition to the Customer class there is another class, the BankAccount class, which only has two attributes, an accountId and a balance. The relationship between the two classes is modeled using a 0-to-many association with the role names accountOwner and ownedAccounts, respectively. Read the association as follows: ❑ Customer has zero or many ownedAccounts ❑ BankAccount has exactly one accountOwner Note that no attributes exist related to the association, neither in the Customer class nor in the BankAccount class. © Copyright IBM Corp. 2000 103 Customer customerId title firstName lastName BankAccount 1 account owner accountId 0..* balance owned accounts Figure 72. Class Diagram for the Simple Association Model Forward Development Path The objective of this section is to show the top-down approach for the Customer-BankAccount sample. We extend the results (model, map, schema, domain classes, service classes) from “Forward Development Path” on page 58. Define the Model Metadata You have to add the BankAccount class and the association. Add Class In the Model Browser select the existing Simple Model created in the previous chapter and add the new class BankAccount. For the BankAccount class define the attributes listed in Table 5 and specify the accountId attribute to be the object ID. Table 5. Attributes of BankAccount Class Name Type Required (comment) accountId String yes (object id) balance BigDecimal yes Define Associations Once you have entered the class attributes and set the object IDs for each class, use the Association Editor (Figure 73) of the Model Browser to set up the association between the two classes. 104 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Class2 of Class1 in Class2 Class1 of Class2 in Class1 Figure 73. Association Editor View Opens with Name Templates Because associations are listed separately from the classes in the Model Browser, and later on generated service classes and methods include the association name in their names, it is important to select the names for the associations carefully. This helps to avoid later confusion, especially if several associations exist between two single classes. The Association Editor suggests using the two role names separated by an underscore character or To to build the association name (for example, OwnedAccounts_AccountOwner or OwnedAccountsToAccountOwner). If there is only one association between two classes and no meaningful role names were defined, then you can substitute the role names with the class names (for example, OwnedAccounts_Customer, BankAccounts_AccountOwner, or BankAccounts_Customer). The consequence of checking the Navigable checkbox is that a pair of getter/setter and add/remove methods are generated. The resulting cardinality for each end of the association depends on the combination of the two checkboxes Many and Required as shown in Table 6. Chapter 6. Simple Associations 105 Table 6. Cardinalities Required Checkbox Many Checkbox Cardinality 0..1 X X 1 X 0..* X 1..* Now create the association for our sample. In the Model Browser select the existing Simple Model and add a new association between the Customer and the BankAccount class called OwnedAccountsToAccountOwner, navigable in both directions and with the cardinalities shown in Figure 74. Figure 74. Association Editor for OwnedAccountsToAccountOwner Association Browse the textual description in the Model Browser’s lower pane to verify the created association (Figure 75). The property setting Not an aggregate is default and cannot be changed in this version of the Persistence Builder. Note also that in the Customer and the BankAccount class a relation attribute (r) ownedAccounts and (r) accountOwner respectively were added. Save the changed model. Many-to-many relationships are discussed in Chapter 9, “Many-to-Many Associations” on page 165. 106 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 75. Model Browser with Defined Association Notice Each association in the model will be mapped to a foreign key in the schema. The direction of the associations is only important with one-to-one associations when generating the schema for a relational database. The foreign key will be placed in the table that is mapped from Class1 (see definition in Figure 73), that is the class on the right side in the Association Editor. If you later notice (while reviewing the schema and map) that the foreign key is in the wrong table, you have to come back to the map browser, delete the association and create it again, in reverse direction. Chapter 6. Simple Associations 107 Generate Domain Classes and Interfaces In the Model Browser select the Simple Model and Models -> Generate. In the SmartGuide Generation Options specify your project and the itso.vap.simple.domain package. This will overwrite the previously generated domain classes for the Simple model. When regenerating code always decide whether the previously generated classes should be deleted or not. Since in this sample only additions were made, the domain classes can just be regenerated. Click on Finish to start code generation. Notice Whenever you change something in the model you have to go through the entire generation chain: generate schema and map (or update them manually), generate model classes, and finally generate service classes. Generated Domain Classes and Interfaces Besides the Customer and BankAccount classes two helper classes are generated (Figure 76): ❑ CustomerToOwnedAccountsRelationship ❑ BankAccountToAccountOwnerRelationship Figure 76. Simple Association: Generated Domain Classes and Interfaces 108 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Notice the get/set and add/remove methods generated in the Customer and BankAccount classes for the association (Figure 77). Figure 77. Customer and BankAccount Interface Methods Generate Database Schema If you wanted to use the workspace schema again you would proceed as described earlier in “Generate Workspace Schema” on page 67. In this sample we directly generate the database schema. In the Model Browser select the Simple model and Models -> Generate Schema from Model. (We recommend to delete the schema if it already exists, otherwise you may experience errors in the generated schema.) Then review the generated schema and model using the Schema Browser and Map Browser respectively. If these browsers are already open, you may have to refresh their lists by selecting Schemas -> Load Available Schemas or Datastore Maps -> Load Available Maps. Changing the Database Schema You have to tailor the database implementation: ❑ In the Schema Browser open the Column Editor for the balance column and change the type to DECIMAL(8,2). ❑ For the accountId column change the type to CHAR(8) and select the VapTrimStringConverter. ❑ For the Customer_customerId column enter a physical name of Cust_customerId in order to adhere to the DB2 naming rules. Do not select the Allow nulls checkbox, because each BankAccount must be Chapter 6. Simple Associations 109 assigned to one Customer. Change the type to CHAR and the length to 4 and select the VapTrimStringConverter. ❑ In the Table Editor for the BankAccount table change the qualifier to ITSO and the physical name to ACCOUNT00. ❑ Open the Foreign Key Relationship Editor (Figure 78) for the generated relationship and change the physical name to OwnedAccToCust. Remember to adhere to the database naming rules. Make sure that the Constraint exists in database checkbox is selected, so that the corresponding foreign key constraint for the relation between Customer and BankAccount is defined in the database during table generation. ❑ Save the changed Simple schema. Figure 78. Foreign Key Relationship Editor Review the Generated Map The map was generated together with the schema. Because type changes do not affect mappings, just save the map, after reviewing it. Note the additional relationship mapping (r) ownedAccounts in the property map list for the Customer table map. Save the SimpleSimple map that was regenerated. 110 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Generate the DDL and the Database Tables In the Schema Browser select the Simple schema Schema -> Generate DDL Script for Schema Creation. Browse the output in the lower pane and notice the foreign key constraint: ALTER TABLE ITSO.ACCOUNT00 ADD CONSTRAINT OwnedAccToCust FOREIGN KEY (Customer_customerId) REFERENCES ITSO.CUSTOMER00; In order to generate the database tables proceed as in “Generate the DDL and the Database Tables” on page 76. Generate Service Classes for Relational SQL Schema The last step in generating the persistence support is to generate the service classes. In the Model Browser select the Simple model Models -> Generate. In the Generation Options SmartGuide select Data Service Classes and Interfaces and click on Next. On the next page select Relational SQL and specify itso.vap.simple.services as the package name an click on Next. Enter the database connection information as in Figure 47 on page 78, select Generate queries using parm marker binding, and click on Finish. Verify the Generated Classes for Relational SQL Schema The creation of persistent objects is the responsibility of the corresponding homes. For the purpose of linking two objects to each other there exist methods of the persistent objects themselves depending on the navigation settings you defined in the model. Here are the most important operations related to associations for the Customer-BankAccount sample; all operations can be performed from either side of the association: Create a Link aCustomer.addOwnedAccounts(aBankAccount); aBankAccount.setAccountOwner(aCustomer); Move a Link This, of course, only works for the to-one side of an association. anOtherCustomer.addOwnedAccounts(aBankAccount); aBankAccount.setAccountOwner(anOtherCustomer); Delete a Link aCustomer.removeOwnedAccounts(aBankAccount); aBankAccount.setAccountOwner(null); Chapter 6. Simple Associations 111 Notice Removing a link (without deleting an object on either end of the link) means that the foreign key field value of the affected row is set to null. As a prerequisite the respective column must be defined to allow nulls. From the model side of the Persistence Builder, aggregation and therefore delete cascade is not supported. However you can set up a foreign key constraint in the database with on delete cascade and that will do the job. Be careful with cascading deletes because you can get out of synchronization with the cache of the Persistence Builder. Test the generated classes by running code pieces from within a scrapbook page. Load the SimpleAssocInsert.scrap (Figure 79) and SimpleAssocReadAll.scrap (Figure 80) files into the scrapbook and run them. These scripts show how to create and associate persistent Customer and BankAccount objects and how to query the associations. itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(); com.ibm.vap.Transactions.Transaction.begin(); // create Customers itso.vap.simple.domain.Customer cust1 = itso.vap.simple.domain.CustomerHomeImpl.singleton().create("101"); cust1.setFirstName("Ueli"); cust1.setLastName("Wahli"); cust1.setTitle("Mr."); itso.vap.simple.domain.Customer cust2 = itso.vap.simple.domain.CustomerHomeImpl.singleton().create("102"); cust2.setFirstName("Daniel"); cust2.setLastName("Peter"); cust2.setTitle("Mr."); // create BankAccounts and associations itso.vap.simple.domain.BankAccount acc1 = itso.vap.simple.domain.BankAccountHomeImpl.singleton().create("101-1001"); acc1.setBalance(new java.math.BigDecimal("80")); cust1.addOwnedAccounts(acc1); // same as: acc1.setAccountOwner(cust1); itso.vap.simple.domain.BankAccount acc2 = itso.vap.simple.domain.BankAccountHomeImpl.singleton().create("101-1002"); acc2.setBalance(new java.math.BigDecimal("375.26")); acc2.setAccountOwner(cust1); // same as: cust1.addOwnedAccounts(acc2); itso.vap.simple.domain.BankAccount acc3 = itso.vap.simple.domain.BankAccountHomeImpl.singleton().create("102-2001"); acc3.setBalance(new java.math.BigDecimal("9375.26")); cust2.addOwnedAccounts(acc3); // same as: acc3.setAccountOwner(cust2); // commit transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 79. Scrapbook: Customers–BankAccounts (SimpleAssocInsert.scrap) 112 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(); com.ibm.vap.Transactions.Transaction.beginReadOnly(); itso.vap.simple.domain.Customer c; java.util.Enumeration enumCustomers; itso.vap.simple.domain.BankAccount a; java.util.Enumeration enumAccounts; enumCustomers = itso.vap.simple.domain.CustomerHomeImpl.singleton().allInstances().elements(); while(enumCustomers.hasMoreElements()) { c = (itso.vap.simple.domain.Customer)enumCustomers.nextElement(); System.out.println(c.getTitle() + " " + c.getFirstName() + " " + c.getLastName()); // get all accounts for current Customer enumAccounts = c.getOwnedAccounts().elements(); while(enumAccounts.hasMoreElements()) { a = (itso.vap.simple.domain.BankAccount)enumAccounts.nextElement(); System.out.println(a.getAccountId() + ": " + a.getBalance()); } } com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 80. Scrapbook: Customers–BankAccounts (SimpleAssocReadAll.scrap) A third scrapbook script (SimpleAssocDelete.scrap) shows how to delete an association between a customer and a bank account. Regenerate Service Classes for the Workspace Schema The service classes for the workspace schema generated for the Customer sample do not work anymore, because in the regenerated domain classes there are additional methods in the Customer interface because of the association. Regenerate the service classes as described in “Generate Service Classes for Workspace Schema” on page 67 to bring the workspace schema up-to-date. Chapter 6. Simple Associations 113 Backward Development Path The objective of this section is to show the bottom-up approach for the Customer-BankAccount sample. We will extend the results (model, map, schema, domain classes, service classes) from “Backward Development Path” on page 86. For simplicity we use the VAPSAMPL database with its CUSTOMER00 and ACCOUNT00 tables that were created in the forward development path. They are the starting point for the bottom-up approach. Alter Schema from Relational Database Open the Schema Browser and select the SimpleBU schema and Schemas -> Import / Export Schema -> Alter Schema from Database. Use the same database connection information as in the previous sections (Figure 47 on page 78). In the Select Tables dialog select the ITSO qualifier and click on the Build Table List button. Select only the ACCOUNT00 table from the Tables list, because the CUSTOMER00 table did not change, and click on OK. Browse through the changed SimpleBU schema and note that also the OWNEDACCTOCUST foreign key was imported. If the foreign key constraints in the database have no names, you would see imported foreign key names in the form of SQL135233131212243 and you would have analyze the details in the textual description or in the Foreign Key Relationship Editor and then change the name to something more readable. Note: We gave the relationship the name OwnedAccToCust in the forward development path, and the constraint was generated as: ALTER TABLE ITSO.ACCOUNT00 ADD CONSTRAINT OwnedAccToCust FOREIGN KEY .... DB2 changed the name to upper case and after importing the relationship is now named OWNEDACCTOCUST. You can rename the relationship. To preserve mixed-case names during DDL generation, you can put the physical name in quotes, for example, “OwnedAccToCust”. Change the name (not the physical name) of the ACCOUNT00 table to BankAccount using the table editor, otherwise the domain class will be given the name Account00. In the Column Editor note that in Version 3 the VapTrimStringConverter converter is used for the CHAR fields by default. Save the schema. 114 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Generate Model Definition and Mapping In the Schema Browser select the SimpleBU schema and Schemas -> Generate Model from Schema. This overwrites the SimpleBU model and the SimpleBUSimpleBU map as well. Open the Model Browser and look at the changed SimpleBU model. The association between Customer and Bankaccount was generated technically correct (Figure 81), but because there are no role names on foreign keys in the relational database, the class names were also used as role names. The role name bankaccount would generate a getBankaccount method in the domain class Customer. This method name could be misleading because the cardinality on that side is 0..m and, therefore, the return value is a LinkCollectionShell rather than a single Bankaccount object. ownedAccounts accountOwner Figure 81. Association Editor: Generated Association Change the role names in the Association Editor to ownedAccounts and accountOwner respectively. Open the Map Browser and select the SimpleBUSimpleBU map, and then the Customer or Bankaccount class. Broken Maps In rare circumstances, you may see errors such as <<broken: a VapClusterMap>> and <<broken: a VapRelationshipMap>> (Figure 82). Chapter 6. Simple Associations 115 Figure 82. Map Browser: Broken Mapping To fix the broken map, first delete the two broken property maps for the Customer and the Bankaccount class one by one by selecting them and selecting Delete Property Map from the pop-up menu. Then select the CUSTOMER table map and Table Maps -> Edit Property Maps. Switch to the Associations page in the Property Map Editor (Figure 83) and change the foreign key relationship from <Not mapped> to OWNEDACCTOCUST for the ownedAccounts association. Do the same for the accountOwner association in the Bankaccount class. Figure 83. Property Map Editor: Association Not Mapped Save the changed SimpleBUSimplebu map. 116 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Generate Code to Support Model and Services The code to support the backward development example is generated in two steps. Generate Domain Classes and Interfaces Open the Model Browser, select the SimpleBU model and Models -> Generate. In the Generation Options SmartGuide select Domain Classes and Interfaces and click on Next. On the next page enter your project name, itso.vap.simplebu.domain as package name, and then click on Finish. This overwrites the domain classes generated during the previous bottom-up example. Generate Service Classes In the Model Browser select the SimpleBU model and Models -> Generate. In the Generation Options dialog select Data Service Classes and Interfaces and click on Next. On the next page select Relational SQL, enter itso.vap.simplebu.services as package name, and click on Next. On the last page enter the database connection info by clicking on the Change button (Figure 47 on page 78). Select parm marker binding and click on Finish to start code generation. This overwrites the service classes generated during the previous bottom-up example. Verify the Generated Classes You can test the generated classes by running code pieces from a scrapbook page. You can run the scripts from the forward path (Figures 79 and 80 on page 113) with minor modifications: ❑ ❑ ❑ ❑ The package names are different. Change BankAccount to Bankaccount. Change the line that activates the datastore. The getter and setter methods of the customer class are spelled slightly different. We provide one sample script. Load the SimpleAssocBU.scrap file (Figure 84) into the scrapbook and run it. Chapter 6. Simple Associations 117 itso.vap.simplebu.services.SimpleBUSimpleBUDataStore.singleton().activate(); com.ibm.vap.Transactions.Transaction.beginReadOnly(); itso.vap.simplebu.domain.Customer c; java.util.Enumeration enumCustomers; itso.vap.simplebu.domain.Bankaccount a; java.util.Enumeration enumAccounts; enumCustomers = itso.vap.simplebu.domain.CustomerHomeImpl.singleton().allInstances().elements(); while(enumCustomers.hasMoreElements()) { c = (itso.vap.simplebu.domain.Customer)enumCustomers.nextElement(); System.out.println(c.getTitle() + " " + c.getFirstname() + " " + c.getLastname()); // get all accounts for current Customer enumAccounts = c.getOwnedAccounts().elements(); while(enumAccounts.hasMoreElements()) { a = (itso.vap.simplebu.domain.Bankaccount)enumAccounts.nextElement(); System.out.println(a.getAccountid() + ": " + a.getBalance()); } } com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 84. Scrapbook: Customers–BankAccounts (SimpleAssocBU.scrap) Visual Programming In this section we describe how to deal with one-to-many associations in visual programming. Enhance the GUI Edit the RetrieveCustomerView that you last edited in “Create and Delete a Customer” on page 96, and add a JTable that lists the ownedAccounts of the displayed Customer object (Figure 85). Change the Type of the Customer Variable In the previous samples the Customer interface was used as type of the variable on the free-form surface. This worked fine so far, because only connections to a Customer’s attributes were required. However, the actual runtime return type of the find method of the CustomerHomeImpl is CustomerImpl, which implements the Customer interface. It is generally recommended to use the XYImpl class rather than the XY interface in visual programming because the XYImpl class fires property change events. Change the type of the Customer variable to CustomerImpl (in the itso.vap.simple.domain package). 118 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 2 3 1 4 Figure 85. Retrieve Customer View with Owned Accounts Add Additional Model Bean and Connections A table model bean is required because we use a JTable for the accounts. In this example you do not want to fill the table with the result of a query executed by a home. You want to show all the BankAccount objects that are linked to a particular Customer object. The solution is to use the VapDefaultRelationshipTableModel (1) rather than the VapDefaultTableModel. For list boxes, the Persistence Builder also provides the AwtRelationshipListModel and the VapDefaultRelationshipListModel (Swing). Drop a VapDefaultRelationshipTableModel bean to the free form surface and add three connections from this bean: ❑ Connect the relationship property (of the table model) to the ownedAccounts property of the CustomerImpl bean (2). ❑ Connect the table property to the this property of the ownedAccounts table visual bean (the Swing JTable) (3). ❑ Connect the transaction property to the this property of the TopLevelTransaction bean (4). Chapter 6. Simple Associations 119 Specify Column Identifiers ❑ Open the properties editor for the VapDefaultRelationshipTableModel and select the columnIdentifiers property. In the dialog, specify itso.vap.simple.domain.BankAccountImpl as Table Element Class and add both the AccountId and the Balance column identifiers to the Selected Column Identifiers list. Make sure that both are set to not editable. Change the Code to Access the Relational Datastore This sample accesses the VAPSAMPL relational database. To prepare the interaction with the datastore: ❑ Change the database activation code in the initialize method. ❑ Delete the event-to-code connection that calls the resetDatastore method, or at least change the method to reset the relational datastore instead of the memory datastore. Notices ❑ It is not really necessary to call the Datastore.reset(aDatastore) method at the end of an application, if you want to disconnect from a relational datastore, because this happens automatically during finalization of the database connection. But as mentioned earlier, it is important if you work with the memory datastore; the reset method also saves the data to the hard drive. ❑ If you still want to access the memory datastore, make sure to first delete the workspace file (see “Verify the Generated Classes in the Workspace Schema” on page 69), because the Customer objects that were last serialized to that file do not match the current implementation. Check the Class Path Before running the sample make sure that all the required projects are included in the class path. Test and version your work. Extend the Application for Updates of Accounts A more complete sample that also supports creation of new BankAccounts as well as insertion and removal of BankAccounts to and from a Customer will be shown in Chapter 8, “Transactions” on page 129. 120 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Restriction for Associations The Persistence Builder only supports the definition of foreign keys where the parent side is a primary key. In Figure 86 only the first configuration is supported. In the second configuration the foreign key points to an attribute that is not the primary key. Customer customerId (PK) title firstName lastName Customer customerId (PK) title firstName lastName driversLicense BankAccount accountId balance customerId (FK) OK XYZ xyzId attribute2 driversLicense (FK) not supported Figure 86. Association Restriction Chapter 6. Simple Associations 121 122 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 7 Enhance Business Object Model Chapter 5, “Simple Object Model: Customer” and Chapter 6, “Simple Associations” explained how to define and generate persistent classes, their persistent data, and associations between persistent classes. In this chapter we describe how an application developer can define additional methods, business rule validation, transient data, events, and how to initialize persistent objects. Initialize Beans You can override the ejbCreate method in the bean class (subclass of VapEntityBeanImpl) to initialize properties of the bean. Make sure to call super.ejbCreate in the changed method. /** Custom initialization for my bean */ public void ejbCreate() { super.ejbCreate(); //custom initialization //... } © Copyright IBM Corp. 2000 123 Do not worry, the changed method is not lost when you regenerate the code. Initialization Example After initialization of a BankAccountBean we would like the balance to hold a BigDecimal instance with value 0.00 rather than to hold null. We overwrite the ejbCreate method in the BankAccountBean class to initialize the balance: /** * Custom initialization for BankAccountBean */ public void ejbCreate() { super.ejbCreate(); setBalance(new java.math.BigDecimal("0")); } Add Methods and Business Rule Validation New behavior should be added to the generated XYBean class. Resist the temptation to add behavior to the XYImpl (EJBObject) as this is meant to act as a passive forwarder (according to the EJB specification) to the bean. The new method(s) must be defined in the XY interface and a forwarding method must be added to the XYImpl (EJBObject). The throwing of Exceptions and the signaling of events should also be done in the bean. Business Rule Example We want to add withdraw and deposit methods to the BankAccount. The withdraw method should check whether there are enough funds, and if not throw an OverdrawException. Exception Class Add a new OverdrawException exception class to the itso.vap.simple.domain package (Figure 87). public class OverdrawException extends Exception { public OverdrawException(String s) { super(s); } } Figure 87. OverdrawException Class 124 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Extend the Interface Add the two methods to the BankAccount interface (Figure 88): public interface BankAccount extends javax.ejb.EJBObject { ... public void deposit(java.math.BigDecimal anAmount) throws java.rmi.RemoteException; public void withdraw(java.math.BigDecimal anAmount) throws OverdrawException, java.rmi.RemoteException; ... } Figure 88. Enhancements in the BankAccount Interface Extend the Bean Add the methods to the BankAccountBean class. The last three lines of code in the two methods make sure that the firePropertyChange method of the right version is called (Figure 89). public class BankAccountBean extends VapEntityBeanImpl { ... public void deposit(java.math.BigDecimal anAmount) throws java.rmi.RemoteException { java.math.BigDecimal oldValue = this.getBalance(); java.math.BigDecimal newValue = oldValue.add(anAmount); this.setBalance(newValue); VapEJBObject impl = this.getEJBObject(); com.ibm.vap.Transactions.Version version = impl.getBom().getVersionForUpdate(); version.firePropertyChange("balance" , oldValue , newValue ); } public void withdraw(java.math.BigDecimal anAmount) throws OverdrawException, java.rmi.RemoteException { java.math.BigDecimal oldValue = this.getBalance(); java.math.BigDecimal newValue = oldValue.subtract(anAmount); if (newValue.signum() == -1) { throw new OverdrawException ("Withdraw not performed. Account must not be overdrawn."); } this.setBalance(newValue); VapEJBObject impl = this.getEJBObject(); com.ibm.vap.Transactions.Version version = impl.getBom().getVersionForUpdate(); version.firePropertyChange("balance" , oldValue , newValue ); } ... } Figure 89. Enhancements in the BankAccountBean Class At the end of the methods a PropertyChangeEvent is fired in order to notify interested listeners of the change. In the withdraw method the balance of the Chapter 7. Enhance Business Object Model 125 version is only changed if the resulting balance will not be negative, otherwise an OverdrawException is thrown. Extend the Implementation Add the methods to the BankAccountImpl class (Figure 90): public class BankAccountImpl extends com.ibm.vap.Transactions.VapEJBObjectImpl implements itso.vap.simple.domain.BankAccount { ... public void deposit(java.math.BigDecimal anAmount) throws java.rmi.RemoteException { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForUpdate(); BankAccountBean bean = (BankAccountBean)version.getBean(); bean.deposit(anAmount); } public void withdraw(java.math.BigDecimal anAmount) throws OverdrawException, java.rmi.RemoteException { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForUpdate(); BankAccountBean bean = (BankAccountBean)version.getBean(); bean.withdraw(anAmount); } ... } Figure 90. Enhancements in the BankAccountImpl Class The deposit and withdraw methods in the BankAccountImpl class are built in the same way as the generated methods. The first two lines get the version bean related to the current transaction. The rest of the code operates on this version bean. Add Transient Data For some applications new state must also be added to the bean. It is assumed that the addition of new state would be for transitory (non-persistent) fields because persistent state would be added using the Model Browser and support for the new field would be generated. You must add the instance variables in the XYBean class including getter and setter methods, enhance the XYImpl class with the forwarding methods and define them also in the XY interface, as described in “Add Methods and Business Rule Validation” on page 124. 126 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Notice There is currently no framework support for transitory fields and so they do not participate in the transaction framework; support for this may be added in the future. Therefore, at the moment the use of additional data is very limited: once you commit the (child-)transaction, in which the transient data was set and changed, the transient data is lost! To add the transient data to the EJBObject (XYImpl ) is not a good idea, because then the data would not be version dependent, that is all (concurrent) transactions would work on the same variables. This might be acceptable for non-volatile temporary data. Chapter 7. Enhance Business Object Model 127 128 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 8 Transactions Chapter 3, “Persistence Builder Concepts” gave a good overview on how transactions are supported by the Persistence Builder. In this chapter we explore the details of nested and concurrent transactions. Furthermore, we describe the BusinessTransaction bean, and a more extensive example shows how to implement these techniques in visual programming. Nested Transactions “Nested Transactions” on page 31 introduced the concept of and the demand for nested transactions. Nested transactions are a means to split a bigger unit of work into smaller subparts, called subtransactions or child transactions. This allows you to rollback single child transactions without having to rollback the over-all top-level transaction. In other words it enables you to undo subparts. The code example in Figure 91 implements the following scenario: 1. A new account is created for a specific customer who already has two existing accounts. 2. An amount of money is transferred from one existing account to the new account. © Copyright IBM Corp. 2000 129 3. The user realizes that the money was taken from the wrong account. Step 2 must be undone. 4. The money is transferred from the other existing account to the new account. 5. Now the whole unit of work is finished and all the changes are committed to the database. If you want to run the sample in the scrapbook, make sure that you run the page in the com.ibm.vap.Transactions.Transaction context (select Page -> Run in... from the menu bar), and the VAPSAMPL database is in the same state as it was after executing the code in SimpleAssocInsert.scrap. // Use Page -> Run in and select Transaction in com.ibm.vap.Transactions itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(); itso.vap.simple.domain.CustomerHomeImpl custHome = itso.vap.simple.domain.CustomerHomeImpl.singleton(); itso.vap.simple.domain.BankAccountHomeImpl acctHome = itso.vap.simple.domain.BankAccountHomeImpl.singleton(); itso.vap.simple.domain.Customer c; itso.vap.simple.domain.BankAccount a1, a2; Transaction t1, t1_1, t1_2, t1_3; //---------------------------------------------------------------------------------// // explanation ref# -// the current transaction:-| // | | t1 = Transaction.begin("T1"); // T1 1 t1_1 = t1.beginChild("T1_1"); c = custHome.find("101"); a1 = acctHome.create("101-1003"); a1.setAccountOwner(c); t1_1.commit(); // // // // // T1_1 T1_1 T1_1 T1_1 T1 2 3 4 t1_2 = t1.beginChild("T1_2"); a2 = acctHome.find("101-1001"); a2.withdraw(new java.math.BigDecimal("50")); a1.deposit(new java.math.BigDecimal("50")); t1_2.rollback(); // // // // // T1_2 T1_2 T1_2 T1_2 T1 6 7 8 t1_3 = t1.beginChild("T1_3"); a2 = acctHome.find("101-1002"); a2.withdraw(new java.math.BigDecimal("50")); a1.deposit(new java.math.BigDecimal("50")); t1_3.commit(); // // // // // T1_3 T1_3 T1_3 T1_3 T1 10 t1.commit(); // - Figure 91. Scrapbook: Nested Transactions (Nested.scrap) Remarks by reference number: 1. Create a new top-level transaction T1. 130 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 5 9 11 12 2. Create a new child transaction T1_1 with T1 as parent transaction. Now two transactions T1 and T1_1 are active and T1_1 automatically becomes the current transaction. 3. Find specific customer. 4. Create a new BankAccount 101-1003 in the context of T1_1, the current transaction. The new BankAccount is not yet visible in T1. The account is assigned to the customer. 5. Commit child transaction T1_1. The new BankAccount is merged to the parent transaction T1. Nothing is committed yet to the database. 6. Create another child transaction T1_2 with T1 as parent transaction. 7. Find an existing account. 8. An amount of money is withdrawn from BankAccount 101-1001 and deposited to the new BankAccount 101-1003. The BankAccount 101-1003 is accessible in T1_2 although it does not yet exist on the database. The reason for that is that T1_2 is a child transaction of T1. In T1_2 there now exists a version for both the BankAccount 101-1001 and 101-1003 containing the changed values. These changes are only visible within T1_2. Nothing is changed yet, whether in the context of the parent transaction T1 nor on the database itself. 9. Rollback transaction T1_2. That is, all work recorded within T1_2 is undone, or more precisely: all object versions within T1_2 are simply dropped. T1 becomes the current transaction. 10.Transfer the amount from the correct account. 11.Commit T1_3. The changed object versions are merged to the parent transaction T1, but again, nothing is changed on the database yet. T1 now contains the object versions for the changed BankAccounts. T1 becomes the current transaction. At this point the whole unit of work recorded in T1 still could be undone (dropped respectively) by calling T1’s rollback method. 12.Since T1 is a top-level transaction all the changes are finally committed to the database, or more precisely: All changes that were recorded within T1 are now sent to the database and then committed to the database. It was not really necessary to call the commit method of child transaction T1_3 (11), because when you commit a parent transaction, all its child transactions automatically get committed first. As stated before, when you commit (rollback) a transaction, all child transactions are committed (rolled-back) first and they are therefore no longer active. This can lead to undesired situations, especially when it comes to GUI applications where each window has its own (child) transaction. If not developed properly a transaction connected to a window can suddenly become Chapter 8. Transactions 131 committed (rolled-back), that is the window does not have an active transaction. Therefore it is important to make sure that all windows containing their own child transaction are closed when the window containing the parent transaction is closed. Concurrent Transactions In the previous section we covered nested transactions in detail. But there is another dimension to transactions, namely concurrence. A transaction can have many parallel child transactions, there can be several parallel top-level transactions in the same process, 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 the Persistence Builder. Isolation Policies A transaction is essentially the management of resources over a period of time. Within a transaction, the Persistence Builder 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. The Persistence Builder supports optimistic (non-locking) and pessimistic (locking) strategies on the back-end data store. The service code will be built differently, depending on whether you chose optimistic or pessimistic locking. As a consequence, it is not possible to change the locking strategy dynamically at run time. Non-locking Optimistic locking means that usually no conflicts are anticipated, but any that occur will be dealt with. Locking and updating is deferred until the Persistence Builder 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. Conflicts can be detected on two levels: either within the Persistence Builder or on the back-end datastore. Optimistic locking is the default in the Persistence Builder. Locking 132 Pessimistic locking means that because you do not want to deal with conflicts, 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. VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs What you can set at run time is the isolation policy for each transaction: Repeatable Read The repeatable read isolation policy guarantees that if the same object is fetched multiple times within the same transaction, the attribute values will never be affected by (committed) changes in other transactions. Repeatable read is the default for each transaction. You could set this policy explicitly by calling the transaction’s supportRepeatableReads method. Unrepeatable Read With the unrepeatable read policy the attribute values of business objects 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. You can set this policy by calling the transaction’s supportUnrepeatableReads method. Therefore there are four different possible combinations: ❑ ❑ ❑ ❑ Copy-on-read - Non-locking implementation of repeatable read Copy-on-write - Non-locking implementation of unrepeatable read Lock-on-read - Locking implementation of repeatable read Lock-on-write -Locking implementation of unrepeatable read Non-Locking Implementation of Repeatable Read If you do not specify any particular isolation policy—as we have done so far—you get a non-locking implementation of repeatable read. This is also called copy-on-read, because a copy of the data of a business object is made the first time it is read within a transaction. Within this transaction subsequent read or write access to the object, or navigation to the object, or query returning the object will use the data from this copy. Non-Locking Implementation of Unrepeatable Read Read-only access prior to the first write will use the parent transaction’s version of the business object. (Remember that even a top-level transaction has a parent: the shared transaction.) When you reread the object (or reuse a reference to the object) before it is updated within this transaction, it is possible to see committed changes from other (sibling) transactions. But note that it is not guaranteed that you see those changes, it depends on the context. Chapter 8. Transactions 133 The first time a business object is updated within a transaction, a copy of the data is made. That’s why it is also called copy-on-write. Within this transaction subsequent read or write access to the object, or navigation to the object, or query returning the object will use the data from this copy. Figure 92 shows the behavior of both copy-on-read and copy-on-write in one example. // Use Page -> Run in and select Transaction in com.ibm.vap.Transactions itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(); itso.vap.simple.domain.CustomerHomeImpl home; itso.vap.simple.domain.Customer c; String n = "Peter"; Transaction tr, t1, t1_1, t1_2; t1 = Transaction.begin("TopLevel/Reset"); home = itso.vap.simple.domain.CustomerHomeImpl.singleton(); c = home.find("101"); c.setLastName(n); t1.commit(); //--------------------------------------------------------------------------------// // explanation ref# -// c's lastname in that context -| // the current transaction:-| | // | | | com.ibm.uvm.tools.DebugSupport.halt(); // 1 t1 = Transaction.begin("T1"); // T1 2 t1_1 = t1.beginChild("T1_1"); // T1_1 t1_2 = t1.beginChild("T1_2"); // T1_2 t1_2.supportUnrepeatableReads(); // T1_2 3 t1.resume(); // T1 4 c = home.find("101"); n=c.getLastName(); // T1 Peter 5 c.setLastName("Peter_T1"); n=c.getLastName(); // T1 Peter_T1 t1_1.resume(); n=c.getLastName(); // T1_1 Peter_T1 6 t1_2.resume(); n=c.getLastName(); // T1_2 Peter_T1 7 t1.resume(); n=c.getLastName(); // T1 Peter_T1 8 c.setLastName("Peter_T1A"); n=c.getLastName(); // T1 Peter_T1A t1_1.resume(); n=c.getLastName(); // T1_1 Peter_T1 9 c.setLastName("Peter_T1_1"); n=c.getLastName(); // T1_1 Peter_T1_1 10 t1_2.resume(); n=c.getLastName(); // T1_2 Peter_T1A 11 c.setLastName("Peter_T1_2"); n=c.getLastName(); // T1_2 Peter_T1_2 12 t1.resume(); n=c.getLastName(); // T1 Peter_T1A 13 c.setLastName("Peter_T1B"); n=c.getLastName(); // T1 Peter_T1B t1_2.resume(); n=c.getLastName(); // T1_2 Peter_T1_2 14 t1.rollback(); // 15 Figure 92. Scrapbook: Transaction Isolation Policies (IsolationPolicies.scrap) The first lines are datastore activation and variable declaration, followed by a short transaction block with the intent to reset the data to a known state. After that the actual sample code starts. A customer object is fetched from the database and then read and changed from within different 134 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs (child)transactions. The comments display which transaction is the current one (after executing the line) and the customer’s last name in that context. The following explanations refer to the explanation ref# at the far right in Figure 92. You can load the IsolationPolicies.scrap file into a scrapbook page and verify the behavior using the debugger. 1. You can use this statement to bring up the debugger and halt execution. In the debugger’s Visible Variables list select the String variable n and watch its value during the execution of the code pieces. 2. Start a top-level T1 and two child transactions T1_1 and T1_2. From this point there are three transactions active. 3. Set unrepeatable read isolation policy for T1_2. T1 and T1_1 have still the default of repeatable read isolation policy. 4. Make T1 the current transaction. 5. Find customer 101 and change its last name to Peter_T1. 6. Make T1_1 the current transaction and read the customer’s last name in that context. The customer is accessed for the first time in T1_1 and now a copy from the actual parent’s version is made (-> Copy-on-read). 7. Make T1_2 the current transaction and read the customer’s last name in that context. The customer is also accessed for the first time in T1_2, but only for reading. Because T1_2 supports unrepeatable reads, no copy of the parent’s customer version is made at this time. The parent’s version of the customer is accessed. 8. Make T1 the current transaction and change the customer’s last name to Peter_T1A in that context. 9. Make T1_1 the current transaction and read the customer’s last name in that context. Because there already exists a version of that customer in this context, the last name is still Peter_T1, as in step 6. 10.Change the customer’s last name in the context of T1_1 to Peter_T1_1. Remember, T1_1 already has its own customer version, so this change is not seen in T1 or T1_2. 11.Make T1_2 the current transaction and read the customer’s last name in that context. Because there is no version of that customer in this context, the parent’s version of the customer is accessed again. So here the changed value Peter_T1A is seen. 12.Change the customer’s last name in the context of T1_2 to Peter_T1_2. The customer is changed for the first time in T1_2, and now a copy of the actual parent’s version is made (-> Copy-on-write). Then this version is changed. Chapter 8. Transactions 135 13.Make T1 the current transaction and change the customer’s last name again. 14.Make T1_2 the current transaction and read the customer’s last name again. Now the changes from T1 are not seen, because in step 12 T1_2 got its own customer version. 15.Rollback T1. This first performs a rollback on T1_1 and T1_2 implicitly. No rollback is executed on the database, because all changes we made were only recorded in customer versions within the Persistence Builder. Conflict Detection 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 originally read. A sibling transaction or another top-level transaction might have committed its changes in the meantime. This kind of situation is called conflict. There are two levels where conflicts can be detected: either within the Persistence Builder or on the back-end datastore. Note that conflicts are only possible with non-locking implementations. Conflict Detection within the Persistence Builder Figure 93 shows a conflict between updates in a parent and child transaction when the child transaction attempts to commit. // Use Page -> Run in and select Transaction in com.ibm.vap.Transactions itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(); itso.vap.simple.domain.CustomerHomeImpl home = itso.vap.simple.domain.CustomerHomeImpl.singleton(); itso.vap.simple.domain.Customer c; Transaction t1, t1_1; com.ibm.uvm.tools.DebugSupport.halt(); t1 = Transaction.begin("T1"); c = home.find("101"); t1_1 = t1.beginChild("T1_1"); c.setLastName("Peter_T1_1"); t1.resume(); c.setLastName("Peter_T1"); try { t1_1.commit(); } catch(VapMergeFailureException e) { System.out.println(e); } t1.rollback(); Figure 93. Scrapbook: Conflict Detection (VapMergeFailure.scrap) 136 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs When the commit method of transaction T1_1 is called a VapMergeFailureException is thrown. The VapMergeFailureException tells you in this context, that the object version (let us call it V1_1) of transaction T1_1 cannot be merged to the parent transaction T1’s object version (V1). The reason is that V1_1 was copied on read from V1, and in the mean time V1 has changed. It does not matter how the changes got to V1, whether it was directly changed in transaction T1 (like in our sample) or whether another sibling child transaction merged its version first. This is what is called conflict detection. In a real application, you would either display an instructional message to the user or implement your own conflict resolution. To implement your own strategy, you can use the catch block. Another possibility is to override the methods canMerge(Version aParentVersion, Version aChildVersion) and merge(Version aParentVersion, Version aChildVersion) in the shell object (XYImpl). In your implementation of canMerge(Version aParentVersion, Version aChildVersion), 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(Version aParentVersion, Version aChildVersion) 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(Version aParentVersion, Version aChildVersion). Conflict Detection on the Back-End Data Store Now what happens if two top-level transactions that may even run in two different processes change the same object and commit their changes? Well, nothing special. As soon as you commit one top-level transaction, the changes cause an update to the database. The same happens when you commit the second top-level transaction. So normally the last transaction that commits its data ‘wins’. The Persistence Builder provides two ways of collison detection: ❑ Optimistic Predicate ❑ Pessimistic Locking Chapter 8. Transactions 137 Optimistic Predicate If you want the same behavior as with concurrent nested transactions for concurrent top-level transactions, let the data store do the conflict detection. Normally, the SQL update statements generated by the Persistence Builder 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 (in the pop-up menu for an attribute). That means that the WHERE clause is enhanced by additional expressions to identify the row. When a top-level transaction is committed, Persistence Builder 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 attribute = newAttributeValue, otherattr = newOtherAttrValue, ... 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, then the database finds the correct row to be updated. If, however, the row—to be precise, the optimistic predicate attribute(s)—was changed in the meantime, the database can no longer find a row to update and reports an SQL warning: SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a query is an empty table. SQLSTATE=02000 Thus, if you want conflict detection between top-level transactions, whether they run in the same process or not, you must specify an optimistic predicate. It can either be 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. Activating an Exception for Optimistic Locking Failure By default a commit fails when the optimistic predicate does not match the actual database content. To get an exception in the application code you can call the following static method: com.ibm.vap.RelationalPersistence.SqlQuery.setThrowNoRowFoundException(true); Optimistic Predicate for the Account Balance For our model we can set Be part of optimistic predicate for the balance attribute of the BankAccount. We regenerate the service classes into the itso.vap.simple.opservices package. 138 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs In Figure 94 we provide a scrapbook script to experience the effect of optimistic locking. In this example we retrieve an account, deposit some funds, and commit the updated balance. // Use Page -> Run in and select Transaction in com.ibm.vap.Transactions itso.vap.simple.opservices.SimpleSimpleDataStore.singleton().activate(); itso.vap.simple.domain.BankAccountHomeImpl home = itso.vap.simple.domain.BankAccountHomeImpl.singleton(); itso.vap.simple.domain.BankAccount a; Transaction t1; com.ibm.vap.RelationalPersistence.SqlQuery.setThrowNoRowFoundException(true); t1 = Transaction.begin("T1"); a = home.find("101-1001"); System.out.println("start balance in t1: "+a.getBalance()); a.deposit( new java.math.BigDecimal("20") ); System.out.println("changed balance in t1: "+a.getBalance()); com.ibm.uvm.tools.DebugSupport.halt(); try { t1.commit(); } catch (Exception e) { System.out.println("commit t1 failed: "+e); t1.rollback(); } t1 = Transaction.beginReadOnly("T1"); a = home.find("101-1001"); System.out.println("final balance in t1: "+a.getBalance()); t1.commit(); Figure 94. Scrapbook: Optimistic Locking (OptimisticLocking1.scrap) Before committing the transaction the program stops in the debugger. At this point you can setup tracing and simulate another process changing the database: ❑ Start the Status Tool for this context and activate tracing ❑ Open a DB2 Command Window and change the balance manually: update itso.account00 set balance = 200.00 where accountid = '101-1001' Continue to run the scrapbook in the debugger and watch the Console window when the transaction is committed. The SQL update statement fails and an exception is raised. Instead of using a DB2 command to change the database you can also run a second scrapbook (OptimisticLocking2.scrap) in parallel and when both are stopped in the debugger you decide which one to continue first. The first update is successful, but the second one fails with the exception. Chapter 8. Transactions 139 Optimistic Predicate with Timestamps You have to do a bit more to introduce nonbusiness attributes, such as a version number or a timestamp. 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 timestamp just before you commit the transaction. Setting the timestamp from Java bears the risk (admittedly slight) that you could have the same exact timestamp on different machines. Another way to do it is by using database timestamps. This requires that on each insert and update the timestamp is set to the current value, which is expressed as CURRENT TIMESTAMP in SQL. It is possible to modify the generated service code for insert and update but it involves changes in the XYQueryPool and XYDataObject classes. Even this does not guarantee that all applications update the timestamp in the same way. We therefore do not suggest to modify the generated service code. Using Database Triggers Suppose we have a model and schema named Timestamp with a Customer class with attributes customerId, firstName, lastName, and title, mapping to a table with matching columns (not null). We add a timestamp attribute (type: Timestamp) to the model class and a timestamp column (type: TIMESTAMP, converter: VapConverter, nulls allowed) to the corresponding table in the schema (the standard timestamp size is 26 characters). In the Map Browser we mark the timestamp as Be part of the optimistic predicate. We generate the model classes into the itso.vap.timestamp.domain package and the service classes into the itso.vap.timestamp.service package, and we export the schema into the VAPSAMPL database as ITSO.CUSTOMERTS table. In a DB2 Command Window we define two triggers to automatically set the timestamp for insert and update statements: db2 create trigger ITSO.INSTIMESTAMP no cascade before insert on ITSO.CUSTOMERTS referencing new as CUST for each row mode db2sql set CUST.TIMESTAMP = CURRENT TIMESTAMP db2 create trigger ITSO.UPDTIMESTAMP no cascade before update on ITSO.CUSTOMERTS referencing new as CUST for each row mode db2sql set CUST.TIMESTAMP = CURRENT TIMESTAMP 140 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs We can test the triggers with a few SQL statements to verify that the timestamp is updated after each insert and update: db2 insert into itso.customerts (customerid,firstname,lastname,title) values('104','Pat','McCarthy','Mr') db2 select * from itso.customerts db2 update itso.customerts set title = 'XX' where customerid = '104' db2 select * from itso.customerts The scrapbook script in Figure 95 can be used to test the optimistic predicate using timestamps. Before committing the update, change the customer data in a DB2 Command Window to make the update from the scrapbook fail: db2 update itso.customerts set title = 'XX' where customerid = '101' // Use Page -> Run in and select Transaction in com.ibm.vap.Transactions itso.vap.timestamp.services.TimestampTimestampDataStore.singleton().activate(); itso.vap.timestamp.domain.CustomerHomeImpl home = itso.vap.timestamp.domain.CustomerHomeImpl.singleton(); itso.vap.timestamp.domain.Customer c; Transaction t; com.ibm.vap.RelationalPersistence.SqlQuery.setThrowNoRowFoundException(true); com.ibm.uvm.tools.DebugSupport.halt(); // insert a customer without a timestamp - it is generated t = Transaction.begin("T1"); c = home.create("101"); c.setLastName("Wahli"); c.setFirstName("Ueli"); c.setTitle("Mr"); t.commit(); // update the customer data t = Transaction.begin("T2"); c = home.find("101"); System.out.println("Found: "+c.getLastName()+" "+c.getTimestamp()); c.setFirstName( c.getFirstName()+"X" ); // stop execution: update the customer from a DB2 Command Window com.ibm.uvm.tools.DebugSupport.halt(); try { t.commit(); } catch (Exception e) { System.out.println("commit failed: "+e); t.rollback(); } Figure 95. Scrapbook: Optimistic Predicate with Timestamp (Timestamp.scrap) The nice thing about this solution with triggers is that every application that updates this table is affected by the triggers and therefore protection is system wide. It does require, however, that all applications use SQL update statements with the optimistic predicate to check the old timestamp value: UPDATE ITSO.CUSTOMERTS SET atribute1 = newvalue1, attr2 = newvalue2 WHERE customerid = keyvalue AND timestamp = oldtimestampvalue Chapter 8. Transactions 141 Pessimistic Locking With pessimistic locking table rows can be locked when read. Pessimistic locking is activated in the Map Browser for individual tables. In the table pop-up menu set the Enable pessimistic locking flag. Of course you have to regenerate the service classes. Locking Implementation of Repeatable Read If you tell the Persistence Builder to generate locking implementations for the service code for (some of) your classes and your transaction is set to support repeatable read, then the Persistence Builder implicitly acquires a lock on a business object the first time it is read. This is why it is also called lock-on-read. What happens if you try to access the locked object depends on whether you try to access the object from a different process or from the same process. Access from other process All subsequent attempts to read or write the object are blocked. The process waits or a timeout by the DBMS causes an exception (see “Database Lock Timeout” on page 144). Access from same process Read access from other transactions is still possible. The first transaction that changes the object acquires an internal update lock. All subsequent attempts to acquire an update lock on this object in other transactions (except in child transactions) cause a VapLocalObjectLockedException. If you want to experiment with pessimistic locking, open the Map Browser and select the SimpleSimple map, then select the Customer persistent class and in the pop-up menu check Enable pessimistic locking. Then generate the service code (select Datastore Maps -> Generate Services from the menu bar) and use a different service package itso.vap.simple.lockingservices. This allows you to decide on datastore activation whether you want to use the locking or non-locking service classes. You can try to run some code pieces from two different scrapbook pages to verify the behavior described above. Use the debugger (com.ibm.uvm.tools.DebugSupport.halt();) to run the code from the two pages step by step. Locking Implementation of Unrepeatable Read With this transaction isolation policy no lock is acquired, if the object is only read. An attempt to acquire a lock is automatic when an object is changed. This is why it is called lock-on-write. What happens if you try to access the 142 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs locked object depends on whether you try to access the object from a different or from the same process. Access from other process All subsequent attempts to read or write the object are blocked. The process waits or a timeout by the DBMS causes an exception (see “Database Lock Timeout” on page 144). Access from same process Subsequent attempts to acquire an update lock on this object in other transactions (except from child transactions) cause a VapLocalObjectLockedException. Note that with this policy there is still a small chance to have a conflict. Look at this scenario: Two processes P1 and P2 read the same object. P1 acquires the update lock and changes the object. P2 acquires also an update lock, caused by calling a setXY method, and is blocked. P1 commits. P2 continues the setXY method, gets the actual data from the datastore and overwrites the data right away (remember we are continuing the setXY method). VapLocalObjectLockedException 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 you may get an exception (VapLocalObjectLockedException) when trying to acquire a lock and a lock is held by another transaction in the same process. When the lock is held by another process, then the thread of execution is blocked. Locking of database rows for a long time, such as a user interaction, should be avoided because other applications accessing that data will wait for the resource to become available. Pessimistic locking with repeatable read should be avoided in general because database rows are already locked when objects are retrieved from the database. How are Table Rows Locked? The service code generated in the XYQueryPool class for pessimistic locking includes extra methods that lock table rows. When retrieving an object by key, the SQL statement is the singleLockSqlString method is used: UPDATE itso.customer00 SET customerId = customerId WHERE customerId = ? Chapter 8. Transactions 143 Database Lock Timeout In DB2 you can set a lock timeout value for the database to cause an exception if a process waits for a locked resource. By default no lock timeout value is set and a process waits indefinitively. You can set the lock timeout value using the DB2 Control Center. Select the database and Configure from the context menu or from the Selected pull-down. Set the value on the Applications page. Alternatively you can set the timeout value in a DB2 Command Window: db2 update db cfg for databasename using locktimeout xx db2 get db cfg for databasename <== seconds <== list values Facts About Transactions Let us revisit what we learned about transactions: ❑ Access and manipulation of data in persistent business objects is only possible within a transaction. ❑ A call to the static begin method on the Transaction class creates a new read-write transaction. The returned object is of type TopLevelTransaction and its parent is the global shared transaction. ❑ A call to the static beginReadOnly method on the Transaction class creates a new read-only transaction. The returned object is of type ReadOnlyTransaction and its parent is the global shared transaction. ❑ A call to the beginChild method on an existing transaction T1 creates a new read-write child transaction. The returned object’s parent is the transaction T1, its type depends on the existing transaction hierarchy: if the parent transaction is a ReadOnlyTransaction and a direct child of the global shared transaction, then the type is TopLevelTransaction, else the type is Transaction. ❑ A call to the beginReadOnlyChild method on an existing transaction T1 creates a new read-only child transaction. The returned object is of type ReadOnlyTransaction and it’s parent is the transaction T1. ❑ Several parent and child transactions can be active at the same time. ❑ Only one transaction is the current transaction in a thread of execution. ❑ In order to make a transaction the current transaction, its resume method is called. ❑ Whenever a transaction is created, it becomes the current transaction. 144 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs ❑ Whenever a transaction is committed or rolled back, its parent transaction becomes the current transaction. ❑ When a transaction is committed, all child transactions are committed. ❑ When a transaction is rolled back, all child transactions are rolled back. ❑ Changes to objects in nested transactions are committed to the parent transaction. ❑ Changes to objects in a TopLevelTransaction are committed to the data store, even if it is a child of a ReadOnlyTransaction. ❑ Once a transaction is committed or rolled back it cannot be used again. ❑ Also ReadOnlyTransactions should be committed once they are not used anymore. ❑ Changes made within a transaction will not be seen outside the scope of the transaction until the transaction commits. ❑ A transacted variable part will ensure that its contents is always accessed in the context of its specified transaction, but will not change the current transaction. In fact, the current transaction may be changed temporarily before the actual access and then switched back after the access. ❑ If there is no doubt about which transaction is the current one at every moment (either given by the design of the application flow, or there is only one transaction active at a time), then you do not require transacted variables. ❑ In some situations also homes need to be encapsulated by a transacted variable: when the home’s create method is called, it might be important in which transaction context the new object should be created. ❑ The isolation policy implemented in the home collections can be either optimistic (nonlocking) or pessimistic (locking). This 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. ❑ Conflict detection between sibling nested transactions is done automatically by the Persistence Builder. ❑ Conflict detection between TopLevelTransactions (or between multiple processes) is done by the database system through optimistic predicates in the Persistence Builder. If you do not use optimistic predicates, then the last update wins. Chapter 8. Transactions 145 BusinessTransaction Bean As stated before a transaction cannot be used again once it is committed or rolled back. This is particularly inconvenient when it comes to GUI programming and you have one (child) transaction per view. For example, you want to provide a reset button which should roll back the changes but keep the window open and allow further changes, so after the rollback you have to start a new transaction again. If it is a child transaction you have to make sure that it is created with the correct valid parent. The BusinessTransaction is a bean that helps in those situations. From the outside it acts like a transaction, that is, it provides among others commit and rollback methods. It encapsulates a transaction object and makes sure that once the encapsulated transaction is committed or rolled back a new transaction is begun automatically. The encapsulated transaction can be lazy initialized (only when needed) when the getTransaction method is called. The features of the BusinessTransaction bean are its properties and methods. Properties Note the type of the property in parenthesis and that the superscript RWB letters indicate Readable, Writable, and Bound respectively. createTransactionOnInitialize RWB (boolean) Indicate here whether the contained transaction should be immediately created or only when needed. The default is true. generateReadOnlyParent RW (boolean) Indicate here whether you want to insert a read-only transaction between the parent transaction and the encapsulated transaction. We suggest to set this property to false (the default is true). nameRW (String) This string is used to name the encapsulated transaction. parentTransactionW (Transaction) The encapsulated transaction will be a child of this parent transaction. If you want it to be a top-level transaction you do not set this property, that is leave it null. When the parent transaction changes, then the encapsulated transaction is replaced with a new one. If the Business 146 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Transaction is used in a child window, then the parent should be set to the transaction of the parent window. rollbackTransactionWhenParentChangesRWB (boolean) Indicate here whether the encapsulated transaction should be rolled back or stay active when another transaction is assigned to the parent property. Usually you do not change the default, which is true. transactionRB (Transaction) This holds the encapsulated transaction object. Methods commit Commits the encapsulated transaction. A new transaction is started automatically. commitAndDestroy Commits the encapsulated transaction. No new transaction is started. rollback Rolls back the encapsulated transaction. A new transaction is started automatically. destroy Rolls back the encapsulated transaction. No new transaction is started. The BusinessTransaction bean can be found in the Composition Editor in the Persistence Builder category (Figure 96). Figure 96. Persistence Builder Palette: BusinessTransaction. Chapter 8. Transactions 147 Visual Programming This section continues with visual programming and focuses on the business transaction bean as well as nested and concurrent transactions. Therefore we will extend the Customer–BankAccount sample with a customer details view, that shows a customer’s assigned BankAccounts on a second notebook page, and with an account details view. Concurrent Top-Level Transactions In this section we will create the customer details view and enhance the existing customer table view to open the customer details view. We will allow multiple details views to be open at the same time. Since each details view has its own TopLevelTransaction you can experiment with concurrent TopLevelTransactions. Create Customer Details View We will implement a customer details view that can be opened from the CustomerSwingTableView (“Customer List Using a JTable and VapDefaultTableModel” on page 100) for a selected Customer object. First create a new visual CustomerDetailsView class derived from com.sun.java.swing.JFrame in the itso.vap.simple.gui package and lay out the GUI as shown in Figure 97. First we have to add some additional nonvisual beans. ❑ The user should be able to have multiple CustomerDetailViews open simultaneously and commit or rollback the changes in those views individually. So the CustomerDetailsView must have its own transaction. The transaction in the CustomerDetailsView must be a child transaction of the read-only transaction in the CustomerSwingTableView, so that when the CustomerDetailsView’s transaction is committed, the changes can be seen in the CustomerSwingTableView. This parent-child relationship between the transactions is not sufficient to refresh the table after the changes in the details view are committed. In addition, it is required that the shouldQueryAfterMergedIntoEvent property of the VapDefaultTableModel bean is set to true (which is the default). This triggers the query again after the child transaction in the CustomerDetailsView has merged its changes into the read-only transaction of the CustomerSwingTableView, which is connected to the VapDefaultTableModel bean. 148 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 11 3 6 2 4 10 10 7 8 1 9 5 Figure 97. Customer Details View ❑ Add a BusinessTransaction bean to the free-form surface and call it BusinessTransaction ( 1). Change its name property to CustomerDetailsView and its generateReadOnlyParent property to false. Promote the BusinessTransaction bean’s parentTransaction property (by selecting Promote Bean Feature... from its pop-up menu) and name it businessTransactionParentTransaction so that later the transaction of the CustomerSwingTableView can be connected to it. (For more information on the BusinessTransaction bean see “BusinessTransaction Bean” on page 146.) ❑ With our GUI design it is possible that several transactions are active at the same time. Now it is important to use a transacted variable for the customer variable, in order to make sure that a customer object is changed in the correct transaction context. Add a transacted variable bean of type CustomerImpl to the free-form surface and call it CustomerImpl (2). ❑ It is necessary to isolate the transacted Customer variable from the selected customer object in the CustomerSwingTableView, because our GUI design allows—once a CustomerDetailsView is open—to select another Customer object (and open its CustomerDetailsView). But the Customer object attached to one CustomerDetailsView should of course Chapter 8. Transactions 149 not be exchanged. Add another (non-transacted) variable bean of type CustomerImpl to the free-form surface and call it CustomerImplTemp (3). Promote the this property of the CustomerImplTemp bean and name it customerImplTempThis, so that you can later connect the selected Customer object from the CustomerSwingTableView to it. Next we need to connect the beans. ❑ Connect the corresponding properties of the CustomerImpl transacted variable bean to the text attributes of the JTextFields and specify keyReleased as target event for each of those connections (4). ❑ Connect the transaction property of the BusinessTransaction bean to the transaction property of the CustomerImpl transacted variable bean (5). ❑ When the window opens we want to assign the Customer object referenced in the CustomerImplTemp variable to the CustomerImpl transacted variable. Therefore connect the windowOpened event of the CustomerDetailsView to the this property of the CustomerImpl transacted variable bean and pass the this property of the CustomerImplTemp variable bean as parameter (6). ❑ Connect the actionPerformed event of the Save button to the commitAndDestroy method of the BusinessTransaction bean. Do not connect to the commit method because after the commit we want to close the window rather than to start a new transaction. Then connect the normalResult of the connection you just made to the dispose method of the CustomerDetailsView (7). ❑ Connect the actionPerformed event of the Cancel button to the destroy method of the BusinessTransaction bean. Do not connect to the rollback method because after the rollback we want to close the window rather than to start a new transaction. Then connect the normalResult of the previous connection to the dispose method of the view (8). ❑ Closing the window should have the same behavior as if the Cancel button were pressed. First change the defaultCloseOperation property of the CustomerDetailsView to DO_NOTHING_ON_CLOSE. Then connect the windowClosing event of the CustomerDetailsView to the destroy method of the BusinessTransaction bean and finally connect the normalResult to the dispose method of the view (9). ❑ Connect the actionPerformed event of the Delete button to the remove method of the CustomerImpl transacted variable bean. Also connect the actionPerformed event of the Delete button to the commitAndDestroy method of the BusinessTransaction bean and connect the normalResult to the dispose method of the CustomerDetailsView (10). ❑ We want the CustomerId text field only to be enabled if the Customer object was newly created and was not yet committed to the database. Once 150 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs we edit an existing customer object, the customerId should no longer be editable. First make sure the name of the CustomerId text field is TFCustomerId. Then create an event to code connection from the windowOpened event of the CustomerDetailsView to a new enableOrDisableField method (11) with the following method body: public void enableOrDisableIdField() { getTFCustomerId().setEnabled(!getCustomerImpl().isPersistent()); return; } Now save the CustomerDetailsView and generate the code. As always uncomment the two lines of code in the generated handleException method. Before you can test your work you have to enhance the CustomerSwingTableView. Enhance the CustomerSwingTableView Enhance the GUI of the CustomerSwingTableView as shown in Figure 98. 6 5 1 4 2 3 Figure 98. Enhanced CustomerSwingTableView First add a Factory bean of type CustomerDetailsView to the free-form surface and call it CustomerDetailsViewFactory (1). Then add these two property-to-property connections: Chapter 8. Transactions 151 ❑ Connect the this property of the ReadOnlyTransaction to the businessTransactionParentTransaction property of the CustomerDetailsViewFactory. (2) ❑ Connect the selectedRowObject property of the VapDefaultTableModel to the customerImplTempThis property of the CustomerDetailsViewFactory (3). Add the event-to-method connections from the Details button in this order: ❑ Connect the actionPerformed event to the CustomerDetailsView constructor of the factory bean (4). ❑ Connect the actionPerformed event to the show method of the factory bean. The event-to-method connections from the Create button in this order: ❑ Connect the actionPerformed event to the CustomerDetailsView constructor of the factory bean (5). ❑ Next we want to create a new persistent Customer object and pass it to the new CustomerDetailsView. Connect the actionPerformed event to the CustomerImplTempThis property of the factory bean and connect its parameter to the create method of the CustomerHomeImpl bean. ❑ Connect the actionPerformed event to the show method of the factory bean. Add this additional event-to-method connection: ❑ Connect the initialize event of the CustomerSwingTableView to the selectionMode property of the ScrollPaneTable bean and set 0 as parameter (6). This sets the single selection mode. Change the name property of the ReadOnlyTransaction bean to CustomerTableView. This enables better debugging and tracing, by showing the name of the transaction. Check in the CustomerSwingTableView’s initialize method (user code section one) that the correct datastore is activated and that the read-only transaction is initialized. // user code begin {1} itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(); getReadOnlyTransaction(); // user code end {1} Define the Class Path Before running the sample make sure the correct projects are included in the class path of the CustomerSwingTableView. You can always click on the Compute Now button. 152 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Test During the test you can observe this behavior: ❑ When you open twice a CustomerDetailsView for the same Customer object, you can change it in both views without an error. The last view that you save wins, and the conflict is not detected. Why? In this sample optimistic locking is used, copy-on-read to be precise, and no optimistic predicate was defined. That is we allow collisions but we do not detect them. You could try to avoid that several CustomerDetailsViews can be opened for the same Customer object within the same application, but this still would not solve the potential risk of collisions, because another user still could change the same Customer object in the meantime. So the best solution when you use copy-on-read is to define optimistic predicates as explained in “Conflict Detection on the Back-End Data Store” on page 137 and report collisions to the user. ❑ The CustomerId text field is only enabled when a new persistent Customer object is created. ❑ How many transactions are active if you open the CustomerDetailsView for two Customer objects? Four transactions: • The global SharedTransaction • The CustomerTableView transaction, a ReadOnlyTransaction in the CustomerSwingTableView • Two CustomerDetailsView transactions, both TopLevelTransactions with the CustomerTableView transaction as parent. Those transactions are concurrent transactions. • When the user clicks on the Save button, the TopLevelTransaction in the CustomerDetailsView is committed to its parent, a ReadOnlyTransaction on level 1, and therefore to the database. ❑ Which transaction is the current one when the CustomerHomeImpl’s create method is called? The create method is triggered by clicking on the New button, but just before this a new CustomerDetailsView is instantiated. Part of the construction of a CustomerDetailsView is also the starting of a new TopLevelTransaction (within the BusinessTransaction), and this is the current transaction at that moment. Well this is exactly what we want, but the mechanism may not be obvious if you look at the beans and connections in the Visual Composition Editor. This transaction hierarchy can be easily verified: Select Workspace -> Tools -> Persistence Builder Tools -> Launch Status Tool from the Workbench’s menu bar to open the Persistence Status Browser, and select itso.vap.simple.gui.CustomerSwingTableView.main() as execution context in Chapter 8. Transactions 153 the Selection Required dialog. Once you have two CustomerDetailsViews open, select View -> Transaction Statistics to display the list (Figure 99). The Persistence Status Browser shows—among other things—the number and type of transactions in your running application. You see the transaction hierarchy tree including level numbers, the name of each transaction, the transaction type (for example, ReadOnlyTransaction), and the transaction state (for example, ActiveTransactionState). You can also use the Persistence Status Browser in your applications to verify that no unused active transactions are floating around. Figure 99. Status Browser: Transaction Statistics Nested Transactions In this section you will first enhance the customer details view to include a second notebook page that shows all accounts of the selected customer. Then you create an account details view that can be opened from the list of accounts. The account details view will have its own child transaction, with the transaction of the customer details view as the parent. Several account details views can be open at the same time, so that you can experiment with several nested transactions. The account list that is shown on a customer details view notebook page will be developed as separate visual bean, derived from JPanel. Create Account Details View This is very similar to the creation of the customer details view. Create a new visual AccountDetailsView class derived from JDialog in the itso.vap.simple.gui package and lay out the GUI as shown in Figure 100. Note that there is no Delete button, because we will implement different delete approach: the Delete button will be part of the account list panel. VisualAge for Java adds a call to the initialize method only in the null-argument constructor of the AccountDetailsView. Because we will later call the AccountDetailsView(java.awt.Frame) constructor you have to include the call to the initialize method by hand: 154 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs public AccountDetailsView(java.awt.Frame owner) { super(owner); initialize(); } 6 3 4 2 12 7 11 8 13 9 10 5 1 Figure 100. Account Details View First we have to add some additional nonvisual beans. ❑ Add a BusinessTransaction bean to the free-form surface and call it BusinessTransaction ( 1). Change its name property to AccountDetailsView and its generateReadOnlyParent property to false. Promote the BusinessTransaction bean’s parentTransaction property (by selecting Promote Bean Feature... from its pop-up menu) and name it businessTransactionParentTransaction so that later the transaction of the CustomerDetailsView can be connected to it. (For more information on the BusinessTransaction bean see “BusinessTransaction Bean” on page 146.) ❑ Again it is important to use a transacted variable for the BankAccount variable to make sure that a BankAccount object is changed in the correct transaction context. Add a transacted variable bean of type BankAccountImpl to the free-form surface and call it BankAccountImpl (2). ❑ It is necessary to isolate the transacted BankAccount variable from the selected BankAccount object in the accounts list. Add another (non-transacted) variable bean of type BankAccountImpl to the free-form surface and call it BankAccountImplTemp. Promote the this property of the BankAccountImplTemp bean as bankAccountImplTempThis, so that you later can connect the selected BankAccount object from the account list to it (3). Chapter 8. Transactions 155 Next we need to connect the beans. ❑ Connect the corresponding properties of the BankAccountImpl transacted variable bean to the text attributes of the JTextFields and specify keyReleased as target event for the accountId connection. Change the enabled property of the balance JTextField to false, because we only want to change the balance using the deposit and withdraw methods (4). ❑ Connect the transaction property of the BusinessTransaction bean to the transaction property of the BankAccountImpl transacted variable bean (5). ❑ When the window opens we want to assign the BankAccount object referenced in the BankAccountImplTemp variable to the BankAccountImpl transacted variable. Therefore connect the windowOpened event of the AccountDetailsView to the this property of the BankAccountImpl transacted variable bean and pass the this property of the BankAccountImplTemp variable bean as parameter (6). ❑ Connect the actionPerformed event of the Ok button to the commitAndDestroy method of the BusinessTransaction bean. Then connect the normalResult of the connection you just made to the dispose method of the CustomerDetailsView (7). ❑ Connect the actionPerformed event of the Apply button to the commit method of the BusinessTransaction bean. After a commit a new transaction will be created and the dialog stays open, while the changes will already be reflected in the account list ( 8). ❑ Connect the actionPerformed event of the Reset button to the rollback method of the BusinessTransaction bean. After a rollback a new transaction will be created and the dialog stays open, but the values in the view will be reset to the values visible in the account list (9). ❑ Connect the actionPerformed event of the Cancel button to the destroy method of the BusinessTransaction bean. Then connect the normalResult of the previous connection to the dispose method of the view (10). ❑ Closing the window should have the same behaviour as if the Cancel button were pressed. First change the defaultCloseOperation property of the AccountDetailsView to DO_NOTHING_ON_CLOSE. Then connect the windowClosing event of the AccountDetailsView to the destroy method of the BusinessTransaction bean and finally connect the normalResult to the dispose method of the view (11). ❑ Connect the actionPerformed event of the Withdraw button to the withdraw method of the BankAccountImpl transacted variable bean and pass the text property of the related amount text field as parameter (12). 156 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs ❑ Connect the actionPerformed event of the Deposit button to the deposit method of the BankAccountImpl transacted variable bean and pass the text property of the related amount text field as parameter (same as 12). ❑ We want the AccountId text field only to be enabled if the BankAccount object was newly created and was not yet committed to the database. First make sure the name of the AccountId text field is TFAccountId. Then create an event to code connection from the windowOpened event of the AccountDetailsView to a new enableOrDisableField method ( 13) with the following method body: public void enableOrDisableIdField() { getTFAccountId().setEnabled(!getBankAccountImpl().isPersistent()); return; } Now save the AccountDetailsView and generate the code. As always uncomment the two lines of code in the generated handleException method. Create AccountsOfOneCustomer Panel Next we create the AccountsOfOneCustomer panel that will be used on a second notebook page in the customer details view and shows all BankAccounts that are assigned to a selected Customer. The panel will be created similar to the CustomerSwingTableView. Create a new visual class AccountsOfOneCustomerPanel derived from com.sun.java.swing.JPanel in the itso.vap.simple.gui package and lay out the GUI as shown in Figure 101. 16 5 15 14 6 13 9 2 10 12 7 1 11 8 3 4 Figure 101. Accounts of One Customer Panel Chapter 8. Transactions 157 Add these additional non-visual beans: ❑ A variable bean of type CustomerImpl (1). This will hold a selected Customer object, whose BankAccounts should be displayed in the table. Promote the this property (use the name CustomerImplThis) so that it is possible to connect to it from outside. ❑ We do not want an additional transaction in this panel just for displaying a list of accounts. We rather want to use a transaction that must be set from outside. So add a variable bean of type Transaction and promote its this property using the name transactionThis (2). ❑ A class bean of type VapDefaultRelationshipTableModel (3). Open the Property Editor and then the ColumnIdentifiers Dialog. There specify itso.vap.simple.domain.BankAccountImpl as table element class and add both available column identifiers (AccountId and Balance) as not editable. ❑ A factory bean of type AccountDetailsView (4). ❑ We subclassed AccountsDetailsView from JDialog, so that we can specify a parent frame during construction, which causes also the dialog to be closed when the frame itself is closed. Add a variable bean of type java.awt.Frame, name it ParentFame, and promote its this feature (use the name parentFrameThis), so that we later can specify the parent frame from outside (5). ❑ A class bean of type BankAccountHomeImpl that will be used for creation of new BankAccount objects (6). ❑ A variable bean of type BankAccountImpl that will hold a newly created BankAccount object (7). Add the property-to-property connections: ❑ Connect the ownedAccounts property of the CustomerImpl bean to the relationship property of the VapDefaultRelationshipTableModel bean (8). ❑ Connect the this property of the ScrollPaneTable bean to the table property of the table model bean (9). ❑ Connect the transaction property of the table model bean to the this property of the Transaction bean (10). ❑ Connect the selectedRowObject property of the table model bean to the bankAccountImplTempThis property of the AccountDetailsViewFactory bean (11). ❑ Connect the this property of the Transaction bean to the businessTransactionParentTransaction property of the factory bean (12). 158 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Add the event-to-method connection from the Delete button (13): ❑ Connect the actionPerformed event to the removeAndDeleteSelectedRow method of the VapDefaultRelationshipTableModel bean. Add the event-to-method connections from the actionPerformed event of the Details button in this order (14): ❑ To the AccountDetailsView(java.awt.Frame) constructor of the factory bean and pass the this property of the ParentFrame bean as parameter. ❑ To the show method of the AccountDetailsView(Factory) bean. Add the event-to-method connections from the actionPerformed event of the New button in this order (15): ❑ To the AccountDetailsView(java.awt.Frame) constructor of the factory bean and pass the this property of the ParentFrame bean as parameter. ❑ To the create method of the BankAccountHomeImpl bean and pass the normalResult to the this property of the CreatedBankAccountImpl bean. ❑ To the setAccountOwner method of the CreatedBankAccountImpl bean and pass the this property of the CustomerImpl bean as parameter. ❑ To the bankAccountImplTempThis property of the factory bean and pass the this property of the CreatedBankAccountImpl bean as parameter. ❑ To the show method of the AccountDetailsView(Factory) bean. Add this additional event-to-method connection: ❑ Connect the initialize event of the AccountsOfOneCustomerPanel to the selectionMode property of the ScrollPaneTable bean and set 0 as parameter. This sets the single selection mode (16). Save the AccountsOfOneCustomerPanel and generate the code. Uncomment the two lines of code in the generated handleException method. Enhance the Customer Details View Open the CustomerDetailsView and temporarily move the panels containing the buttons and text fields to the free-form surface, so that you do not lose the connections. Then place a JTabbedPane into the CustomerDetailsView and move the other components back onto the first page. Add another page to the tabbed pane, name it Accounts, and place a visual bean of type AccountsOfOneCustomerPanel on it (Figure 102). Chapter 8. Transactions 159 1 3 2 Figure 102. CustomerDetailsView Enhanced Three additional property-to-property connections are needed: ❑ Connect the this property of the CustomerImpl bean to the customerImplThis property of the AccountsOfOneCustomerPanel (1). ❑ Connect the transaction property of the BusinessTransaction bean to the transactionThis property of the AccountsOfOneCustomerPanel bean (2). ❑ Connect the this property of the CustomerDetailsView to the parentFrameThis property of the AccountsOfOneCustomerPanel bean (3). Save the changed CustomerDetailsView. Test During the test you can observe this behavior: ❑ You can create new BankAccounts in a new Customer before the new Customer is committed to the database. ❑ Because we used a nested transaction in the AccountDetailsView it is now possible to roll back or commit changes made for a single BankAccount, without affecting the changes you may have made in the Customer details or in other BankAccounts of the same Customer. 160 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs ❑ When you open twice an AccountDetailsView for the same BankAccount object and change it in both views, then a VapMergeFailureException is thrown when you want to commit the second one. Unfortunately you do not get the exception because the BusinessTransaction’s commit method calls the Transaction’s commitOrRollback method which catches the exception but does not rethrow it. This will be changed in a future release, so that the Exception will be rethrown after a rollback. You could fix the problem in the meantime by changing the Transaction’s commitOrRollback method yourself: public void commitOrRollback() throws RemoteException, VapTransactionRequiredException { try { this.getState().commit(this);} catch (VapTransactionRequiredException aFailureException) { throw aFailureException;} catch (RemoteException aFailureException) { this.rollback(); throw aFailureException; } } ❑ Open a Customer and some of its associated BankAccounts (Figure 103). Figure 103. Customer–BankAccount Sample GUI Scenario ❑ Look at the transaction statistics in the Persistence Status Browser. You will see a transaction tree similar to the one shown in Figure 104. The two Chapter 8. Transactions 161 AccountDetailsView transactions are so-called sibling nested transactions. Figure 104. Transaction Statistics: Sibling Nested Transactions ❑ If the user closes a CustomerDetailsView then all associated AccountDetailsViews are also closed. This is important, because otherwise an AccountDetailsView’s transaction may have an invalid parent transaction. ❑ One potential problem with our GUI design is, that the user can save a CustomerDetailsView, that is commit its transaction, while an associated AccountDetailsView is open. What happens is that during the commit of the CustomerDetailsView’s transaction, first the AccountDetailsView’s transaction is committed, because it is a child transaction. And that may not be what you want, because the user may not be finished editing the BankAccount. One possibility to solve this problem would be to make the AccountDetailsView modal. Experiments with Locking With this application we can now experiment with optimistic predicate and with pessimistic locking. Optimistic Predicate Change the initialize method of the CustomerSwingTableView to use the services generated with optimistic predicate: // user code begin {1} com.ibm.vap.RelationalPersistence.SqlQuery.setThrowNoRowFoundException(true); itso.vap.simple.opservices.SimpleSimpleDataStore.singleton().activate(); getReadOnlyTransaction(); // user code end If you work with the same account in two windows, there is no change in the way the application works. A merge failure exception occurs when the second account details window is closed. 162 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs If you change the table content after you open an account, then the optimistic predicate triggers an exception when the customer details window is closed and the database is trying to update the account balance in the database. Pessimistic Locking Change the initialize method of the CustomerSwingTableView to use the services generated with pessimistic locking of the customer table: // user code begin {1} com.ibm.vap.RelationalPersistence.SqlQuery.setThrowNoRowFoundException(true); itso.vap.simple.lockingservices.SimpleSimpleDataStore.singleton().activate(); getReadOnlyTransaction(); // user code end Again there is no change in the application because the customer object is passed to the details view from the table view and is not retrieved with the find method of the home. The locking of customer objects can be forced by using the find method. For a simple test modify the connEtoM1 method that sets the CustomerImpl object from the CustomerImplTemp object: // user code begin {1} setCustomerImplTemp( (itso.vap.simple.domain.CustomerImpl) itso.vap.simple.domain.CustomerHomeImpl.singleton(). find( getCustomerImplTemp().getCustomerId() ) ); // user code end setCustomerImpl(getCustomerImplTemp()); Note that you can implement this change also in the visual composition with a CustomerHomeImpl bean and getting the parameter for the windowOpened connection from the find method (of the home bean), passing the customerId property of the CustomerImplTemp bean as parameter for the find method. Use the Status Tool to setup tracing and you can watch in the Console window that the customer object and table row is locked when the first detail window is opened, and when a second detail window for the same customer is opened, an exception occurs and the window is empty. (first detail window) >>>>executing find by key with lock >>>executing: UPDATE ITSO.CUSTOMER00 SET customerId = customerId WHERE customerId = ? >>>>input values: [102] ..... (second detail window) EXCEPTION OCCURRED => InternalObjectLock>>attemptToLockWith(InternalObjectLock) The object is locked internally --------- UNCAUGHT EXCEPTION --------com.ibm.vap.common.VapLocalObjectLockedException: The object is locked internally Chapter 8. Transactions 163 You can run another test scenario by running two applications in parallel: ❑ Open two CustomerSwingTableView applications. ❑ Open a detail window in one application and the customer object and row is locked. ❑ Open a detail window for the same customer in the second application. The window opens grey and the application waits because the customer row is locked. ❑ Change the customer data in the first application and save the changes. The transaction is committed to the read-only parent, the database is updated, and the customer is unlocked. The second application detail window opens now with the new customer data. However, the first application now waits. It tries to execute the allInstances method, but one of the customer objects is locked now by the second application. ❑ Change the customer data in the second application and save the changes. The database is updated, and both applications continue and display the updated list of customers. Why does the application rerun the allInstances method when the detail window is closed? In “Create Customer Details View” on page 148 we specified that the shouldQueryAfterMergedIntoEvent property of the VapDefaultTableModel bean in the CustomerSwingTableView is set to true. This triggers the allInstances method when changes from a child transaction are merged into the parent transaction. We can change the shouldQueryAfterMergedIntoEvent property to false. In this case the allInstances method is not executed after a detail window is closed. However, we want the table to display the updated values: ❑ Add a connection from the windowClosed event of the CustomerDetailsViewFactory to the repaint method of the Swing table. This change lets the application continue when customer data is locked, but, over time, the data displayed in the window might not reflect the actual data in the database table. Note that good application design would prohibit locking of data over a user interaction. The window should be opened without locking, and the customer object should be locked only to apply the updates of the user. 164 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 9 Many-to-Many Associations In this chapter we extend the discussion on associations with a look at many-to-many associations. In the banking example (see Chapter 15, “ATM Application Requirements and Database” on page 267), the two classes BankAccount and Card share a many-to-many association (Figure 105): ❑ A card can be associated to zero or more bank accounts. ❑ A bank account can be associated to zero or more cards. This is quite typical in the real world; with your ATM card you can access multiple accounts, and one account may be accessed from multiple ATM cards, as in the example of joint bank accounts. © Copyright IBM Corp. 2000 165 Card cardNumber pinCard BankAccount accessedAccounts accountId 0..* balance 0..* validCards Figure 105. Banking Example: Card and BankAccount Analysis View Intuitively, implementing an object model to reflect this relationship between the two classes is straightforward: define the classes and define the association between them. With the current implementation of the Persistence Builder, however, the construction of a many-to-many association requires an alternate model. To accommodate a many-to-many relationship in a relational database, for example, an intermediate table must be used to map keys of one table to keys of another table. In defining an object model that contains a many-to-many association, we need an intermediate class called an association class that has a one-to-many relationship to each of the business classes. In our example, we have called it CardAccount (Figure 106). CardAccount Card cardNumber pinCard 1 0..* BankAccount 0..* 1 accountId balance Figure 106. Banking Example: Card and BankAccount Design View Each instance of the CardAccount association class represents a connection from exactly one Card object to exactly one BankAccount object. Accessing the BankAccounts for a given Card, for example, requires collecting all instances of CardAccount that are equivalent to the given Card, and resolving the BankAccounts from these instances. 166 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Define the Model We now have enough information to define our ManytoMany model in the Model Browser. Define the Card and BankAccount classes The first step is to define the Card and BankAccount classes with the attributes specified in Table 7. Table 7. Attributes of Card and BankAccount Class Name Attribute Name Attribute Type Required Card cardNumber String Yes (Object ID) pin String No accountId String Yes (Object ID) balance BigDecimal No BankAccount Create the CardAccount Association Class The next step is to create the association class CardAccount, without entering any attributes. As soon as this is defined, the one-to-many associations can be specified. Define the Associations To accommodate the many-to-many relationship as previously described, we will create two one-to-many associations. The first of the two associations is between the Card and CardAccount class. To define this association, open the Association Editor for a new association (Associations -> New Association) and complete the dialog as shown in Figure 107. Chapter 9. Many-to-Many Associations 167 Figure 107. Association between Card and CardAccount At first glance, the settings may be somewhat confusing. The association we are defining here is that one Card object can be related to zero or more instances of the CardAccount class. We consider this association from the perspective of each class to better understand the settings. From the perspective of the Card class, the role of CardAccount is to provide account associations. CardAccount can provide zero or many account associations. Therefore we select the Many checkbox, but not the Required checkbox. From the perspective of the CardAccount class, the role of Card is to provide a valid card to create an association with. Card must provide only one card to CardAccount. Therefore, we select the Required checkbox, but not the Many checkbox. In both classes, the Navigable checkbox is selected, as we wish to have methods generated that return account associations for a Card and the valid card for an instance of CardAccount. In the same manner, we define the second one-to-many association between BankAccount and CardAccount (Figure 108). 168 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 108. Association between BankAccount and CardAccount Define Object IDs in the CardAccount Class Since no object ID attributes have been defined for the CardAccount class yet, the last step in the definition of the model is to use the association roles as object IDs. This is because the table corresponding to CardAccount in the relational database will have two columns, both defined as primary keys, and this must be reflected in the object model. By choosing associations as object IDs instead of defining attributes, we assert the fact that the information in this class is stored elsewhere in other classes. We use the Class Editor to define the object IDs (Figure 109). We select each association and add it to the object ID list by using the arrow buttons in the center. Chapter 9. Many-to-Many Associations 169 Figure 109. Class Editor: CardAccount Save the Model and Generate the Schema Once the object model is complete, save it in the workspace in an appropriate package and project. Select Models->Generate schema from model to generate the schema and datastore mapping. We used the itso.many.metadata package and saved the model as ManyToManyModel class. Define the Schema and Mapping The schema generation process generates the schema and mapping. Both are available for examination in the Schema Browser and Map Browser, respectively. In the Schema Browser, change the cardNumber, accountId, Card_cardNumber, and BankAccount_accountId columns to CHAR(8), and the Pin column to CHAR(4). 170 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Define Physical Names for Schema Identifiers As shown in other examples, some of the schema identifiers violate DB2 naming restrictions. The identifiers and their physical name definitions are specified in Table 8. Table 8. Physical Name Definitions Identifier Name Identifier Type Physical Name Card_cardNumber Column cardNumber BankAccount_accountId Column accountId CardToCardAccount Foreign Key Relationship CARDTOACCOUNT BankAccountToCardAccount Foreign Key Relationship ACCOUNTTOCARD Each entity and association in the schema has an editor dialog. In the dialog is a field to define a physical name. For example, we define a physical name for the foreign key relationship between Card and CardAccount in the Foreign Key Relationship Editor (Figure 110). Figure 110. Foreign Key Relationship Editor: Defining a Physical Name For each of the identifiers in Table 8, we define a physical name in this manner, using the proper editor for the identifier type. Chapter 9. Many-to-Many Associations 171 Exporting the Schema to the Database To export the schema to the database select Schemas->Import/Export Schemas->Export Entire Schema to Database... and configure the parameters to export the schema to a database of your liking. Also be sure to save the schema in the workspace (ManyToManySchema class in itso.many.metadata package). We used the VAPSAMPL database from the previous chapters and generate the tables: CREATE TABLE ITSO.BankAccount ( balance DECIMAL(20,2), accountId CHAR(8) NOT NULL) CREATE TABLE ITSO.Card ( cardNumber CHAR(8) NOT NULL, pin CHAR(4)) CREATE TABLE ITSO.CardAccount ( cardNumber CHAR(8) NOT NULL, accountId CHAR(8) NOT NULL) ALTER TABLE ITSO.Card ADD PRIMARY KEY (cardNumber) ALTER TABLE ITSO.BankAccount ADD PRIMARY KEY (accountId) ALTER TABLE ITSO.CardAccount ADD PRIMARY KEY (cardNumber, accountId) ALTER TABLE ITSO.CardAccount ADD CONSTRAINT ACCOUNTTOCARD FOREIGN KEY (accountId) REFERENCES ITSO.BankAccount ALTER TABLE ITSO.CardAccount ADD CONSTRAINT CARDTOACCOUNT FOREIGN KEY (cardNumber) REFERENCES ITSO.Card Generate the Domain and Service Classes Once the export of the schema is complete, generate both the domain classes and service classes for the model in appropriate packages in your project, and save the Datastore mapping in the workspace (ManyToManyManyToManyMap class in itso.many.metadata package). We used the itso.many.domain package for the domain classes, and the itso.many.services package for the service classes. 172 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Implement Association Methods As we mentioned previously, the present implementation of the Persistence Builder does not directly accommodate many-to-many associations. As a consequence, we must implement methods that will allow us to manage the associations between a Card and its BankAccounts, and between a BankAccount and its Cards. Mainly, these methods will be add, delete, and retrieve methods. This allows us to act as though the association class, CardAccount, does not exist when developing applications. We will consider the modifications to be made to the Card classes. Modifications to the BankAccount classes will be similar. Implement Custom Methods When implementing custom methods in the business classes generated by the Persistence Builder, they must be defined in three areas of a business object. If we consider the Card, for example, methods must be added to: ❑ Card Interface. This is the public interface to the Card business object. Any methods we add to the Card that we want to be publicly available should be added here. ❑ CardImpl Class. This is the class that participates at the transactional level of an application. Anything we ask of a business object can only be done by first determining the current version of the business object. It is in that version where the instructions will be executed. Methods in this class are responsible for obtaining the current version of the business object. ❑ CardBean Class. This class contains the actual implementation of our method. At first glance, this hierarchy might seem redundant, but it has many advantages. It allows for the development of straightforward methods without having to consider transactional versions at the implementation level. In addition, many of the helper methods that you might use along with the implementation of the primary method can be hidden in the “Bean” class (here, CardBean). Implement the Card Methods Card Interface We are going to add three methods to the Card public interface: Chapter 9. Many-to-Many Associations 173 ❑ addBankAccount(BankAccount). Adding a BankAccount to a Card involves adding the association of the BankAccount to the Card. An entry is then created in the CardAccountHome as a result of creating the association. ❑ removeBankAccount(BankAccount). Removing a BankAccount from a Card involves deleting the association of the BankAccount to the Card. The corresponding entry is then deleted from the CardAccountHome as a result. ❑ getBankAccounts(). Retrieving the BankAccounts requires the retrieval of a Vector of BankAccount associations. To be useful, we would like a Vector of BankAccounts. The final step is to resolve these associations into BankAccount objects. It is important to note here that we are not adding or deleting actual business objects to home collections, but adding and deleting associations into the association class (in this case the CardAccount class). The code for these methods in the interface is straightforward (Figure 111). // Methods to add to the Card Interface // addBankAccount public void addBankAccount(BankAccount aBankAccount) throws java.rmi.RemoteException, javax.ejb.FinderException; // removeBankAccount public void removeBankAccount(BankAccount aBankAccount) throws java.rmi.RemoteException, javax.ejb.FinderException, javax.ejb.RemoveException; // getBankAccounts public java.util.Vector getBankAccounts() throws java.rmi.RemoteException, javax.ejb.FinderException; Figure 111. Card Interface Method Definitions The exceptions thrown here are transaction-related, and would be thrown in such instances. CardImpl Class The methods in this class act as ‘supervisors’ to the entire activity of changing or querying the state of a business object. The simple objective here is to obtain the current version for a business object, and forward the instruction on the implementation class, CardBean. If any results or exceptions are thrown, the methods in this class handle those situations. 174 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs The code for the methods to be added to this class is shown in Figure 112. // Methods to add to the CardImpl Class // addBankAccount public void addBankAccount(BankAccount aBankAccount) throws java.rmi.RemoteException, javax.ejb.FinderException { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForRead(); CardBean bean = (CardBean)version.getBean(); bean.addBankAccount(this, aBankAccount); } // removeBankAccount public void removeBankAccount(BankAccount aBankAccount) throws java.rmi.RemoteException, javax.ejb.FinderException, javax.ejb.RemoveException { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForRead(); CardBean bean = (CardBean)version.getBean(); bean.removeBankAccount(this, aBankAccount); } // getBankAccounts public java.util.Vector getBankAccounts() throws java.rmi.RemoteException, javax.ejb.FinderException { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForRead(); CardBean bean = (CardBean)version.getBean(); return bean.getBankAccounts(); } Figure 112. CardImpl Methods In the addBankAccount and removeBankAccount methods, we pass the this parameter along to the implementation method in the CardBean class. This ensures that the current instance version of card has the given instance of BankAccount added or removed to itself. The CardBean is not aware of the current version or instance of the Card business object that is to be modified. The getBankAccounts method returns the result from the forwarded request to the CardBean class. CardBean Class In this class, methods are implemented to simply execute Java instructions to change or inspect the state of a given Card. It is here where we define the actual instructions to add, remove, and retrieve the BankAccounts for a card (Figure 113). Chapter 9. Many-to-Many Associations 175 // Methods to add to the CardBean Class // addBankAccount public void addBankAccount(Card aCard, BankAccount aBankAccount) throws java.rmi.RemoteException, javax.ejb.FinderException { this.addAccountAssociations(CardAccountHomeImpl.singleton().create (aCard, aBankAccount)); } // removeBankAccount public void removeBankAccount(Card aCard, BankAccount aBankAccount) throws java.rmi.RemoteException, javax.ejb.FinderException, javax.ejb.RemoveException { CardAccount anAssociation = CardAccountHomeImpl.singleton().find (aCard, aBankAccount); this.removeAccountAssociations(anAssociation); anAssociation.remove(); } // getBankAccounts public java.util.Vector getBankAccounts() throws java.rmi.RemoteException, javax.ejb.FinderException { java.util.Vector result = new java.util.Vector(); java.util.Enumeration accountAssociations = this.getAccountAssociations().elements(); while (accountAssociations.hasMoreElements()) { itso.many.domain.CardAccount anAssociation = (itso.many.domain.CardAccount)accountAssociations.nextElement(); result.addElement (anAssociation.getAccessedAccount()); } return result; } Figure 113. CardBean Methods At this level, the add and remove methods simply add or remove the BankAccount association. In the case of the remove method, we also must physically remove the association from the database to avoid inconsistent versions of data in the home collections. The getBankAccounts method iterates through the Vector of BankAccount associations it obtains from the CardAccount home collection and resolves them into a new Vector of BankAccount objects. As a performance consideration, a pre-load path (described later) for the BankAccountHome could be used to reduce the amount of database activity that would be involved in resolving the BankAccount associations to BankAccount objects. Overriding the toString Method The default toString generated by the Persistence Builder for the business objects may not be adequate for your application needs. To change how a 176 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs business object represents itself as text, you may override the toString method in any “Impl” class. For our example, we created a toString method in the BankAccountImpl class to simply return the accountId for a BankAccount. Using the Association Methods Now that the method implementations are complete, they are available for use in visual and nonvisual applications. Implement the BankAccount Methods We use the same technique to extend the BankAccount class with the following three methods: ❑ getCards: retrieve all Cards that can access the BankAccount ❑ addCard: add an association to a Card that can access the BankAccount ❑ removeCard: remove the association to a Card Test Examples Once all of the necessary code is generated, and the database is populated, we can test the model with a sample applications. We will do this in two steps. First, we test with simple scrapbook samples, and then describe some considerations when implementing an applet. You can use the manydata.sql file to load sample data into the tables: db2 -f manydata.sql Scrapbook Examples In each of our samples, we must first activate the datastore we are accessing, initialize and begin a transaction, execute our instructions, and either commit or rollback the transaction. The last step is necessary to terminate the transaction and avoid “ghost” transactions that would otherwise occupy system resources. These samples are easily executed in the VisualAge for Java Scrapbook window. Retrieve Accounts of a Card In our first sample (Figure 114) we supply the database a key from the Card class to access the account associations. Chapter 9. Many-to-Many Associations 177 // Activate the DataStore itso.many.services.ManyToManyManyToManyDataStore.singleton().activate(); // Begin a Transaction com.ibm.vap.Transactions.Transaction.begin(); // Get the associations for card '1111111' and // display the accountIds itso.many.domain.Card aCard = itso.many.domain.CardHomeImpl.singleton().find("1111111"); java.util.Enumeration enum = aCard.getBankAccounts().elements(); System.out.println("Accounts linked to card 1111111:\n"); while (enum.hasMoreElements()) { System.out.println((itso.many.domain.BankAccount)enum.nextElement()); } // Commit the Transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 114. Scrapbook: Display BankAccounts of a Card (FindAccounts.scrap) This example finds the card with cardNumber 1111111 and then retrieves all of the accounts for that card using the getBankAccounts method. It then iterates through the BankAccounts, by way of an Enumeration, to display the account numbers to the console. Add Card and Associate Accounts In our second example (Figure 115), we create a new Card object, and add it to the database. BankAccount objects could be added to the database in a similar manner. In addition to adding the card, we also want to associate it to some existing accounts by using the addBankAccount or addCard method. Note that we can use either method to create the association. We create a card with cardNumber 8888888 in the CardHome, and then set its pin to 8888. After creating it, we commit the change to the database and begin a new transaction. We commit at this point because the card needs to be in the database to associate it to BankAccounts. To associate this card to some BankAccounts, we use the addCard method to add the card to two of the BankAccounts, and the addBankAccount method of the Card for one BankAccount. To complete this example, we display all accounts associated to Card 8888888. 178 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs // Activate the DataStore itso.many.services.ManyToManyManyToManyDataStore.singleton().activate(); // Begin a Transaction com.ibm.vap.Transactions.Transaction.begin(); // Create a new card with cardId '8888888' and pin '8888' itso.many.domain.Card aCard = itso.many.domain.CardHomeImpl.singleton().create("8888888"); aCard.setPin("8888"); // Commit the Transaction and begin a new one com.ibm.vap.Transactions.Transaction.getCurrent().commit(); com.ibm.vap.Transactions.Transaction.begin(); // Add three BankAccounts to the card (itso.many.domain.BankAccountHomeImpl.singleton().find("101-1003")).addCard(aCard); (itso.many.domain.BankAccountHomeImpl.singleton().find("103-3002")).addCard(aCard); aCard.addBankAccount( itso.many.domain.BankAccountHomeImpl.singleton().find("106-6004") ); // Commit the Transaction and begin a new one com.ibm.vap.Transactions.Transaction.getCurrent().commit(); com.ibm.vap.Transactions.Transaction.begin(); // Display all accounts for cardId '8888888' java.util.Enumeration enum = aCard.getAccountAssociations().elements(); System.out.println("Accounts linked to card 8888888:\n"); while (enum.hasMoreElements()) { System.out.println((itso.many.domain.BankAccount)enum.nextElement()); } // Commit the Transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 115. Add a Card and Associate BankAccounts (AddCard.scrap) Remove Card and Associated Accounts In our third example (Figure 116), we address the issue of deleting Cards and BankAccounts in the database. To remove a card from the datastore we first obtain a handle to the Card object with cardNumber 8888888. We then iterate through the Vector of BankAccounts and call removeBankAccount for each BankAccount. To finish up, we commit our transaction, begin a new one, verify that Card 8888888 no longer as any accounts associated to it, and remove the Card. Chapter 9. Many-to-Many Associations 179 // Activate the DataStore itso.many.services.ManyToManyManyToManyDataStore.singleton().activate(); // Begin a Transaction com.ibm.vap.Transactions.Transaction.begin(); // Get cardNumber '8888888' itso.many.domain.Card aCard = itso.many.domain.CardHomeImpl.singleton().find("8888888"); // Get BankAccounts for cardNumber '8888888' java.util.Enumeration enum = aCard.getBankAccounts().elements(); System.out.println("Card "+aCard); // For each BankAccount, remove it from the Card while (enum.hasMoreElements()) { aCard.removeBankAccount((itso.many.domain.BankAccount)enum.nextElement()); System.out.println("...removed an account from the card"); } // Commit the Transaction and begin a new one com.ibm.vap.Transactions.Transaction.getCurrent().commit(); com.ibm.vap.Transactions.Transaction.begin(); // Display all accounts for cardId '8888888' to verify enum = aCard.getAccountAssociations().elements(); System.out.println("Accounts linked to card 8888888:\n"); while (enum.hasMoreElements()) { System.out.println((itso.many.domain.BankAccount)enum.nextElement()); } // Remove the cardNumber '8888888' aCard.remove(); System.out.println("Card deleted"); // Commit the Transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 116. Deleting a Card and Associations (RemoveCard.scrap) Visual Programming: Applet Example Because we have added functionality to add, remove, and retrieve the BankAccounts for a given card, implementing a visual application to handle the actions is straightforward. One difference exists, however, between implementing a visual application for a one-to-many association, and a many-to-many association. Consider for an instant if only one Card could be associated to zero or many BankAccounts. 180 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs The code generated by the Persistence Builder returns a LinkCollectionShell when following a one-to-many association. A LinkCollectionShell contains all the related objects, and it provides a method that returns an Enumeration to iterate over the related objects. The LinkCollectionShell can be directly connected to either a VapDefaultRelationshipTableModel, VapDefaultRelationshipListModel, or AwtListRelationshipModel, as shown in Figure 101 on page 157. In fact, the Persistence Builder visual model beans, VapDefaultRelationshipTableModel and VapDefaultRelationshipListModel, can be substituted for a DefaultTableModel or DefaultListModel. In our case of the many-to-many association, the getBankAccounts method returns a Vector of BankAccounts, because creating a LinkCollectionShell is not possible at the business object level with the Persistence Framework. To display the Vector of BankAccounts in a list or table, we have to write a method to display the contents of the Vector in these models. Figure 117 shows the layout of an applet that enables the user to associate BankAccounts to a Card. 1 6 4 8 5 7 3 2 9 Figure 117. Applet Design for Many-To-Many Association Chapter 9. Many-to-Many Associations 181 The basic logic of this applet is to fill two lists with all the Cards and BankAccounts that are available in the database. We use a VapDefaultTableModel for the list of Cards (1), and a VapDefaultListModel for the list of BankAccounts (2). They are connected to the appropriate home classes, and as we learned such a connection automatically fills the lists at startup of the applet. The third list (3) displays the BankAccounts associated with the selected Card. The selectedCard is the teared-off selectedRowObject attribute of the table model (4). The list is filled by a user-written method named displayAccounts: public void displayAccounts() { itso.many.domain.Card card = getSelectedCard(); com.sun.java.swing.DefaultListModel listModel = this.getAssociatedAccountsListModel(); listModel.removeAllElements(); if (card == null) return; // after commit this is null try { java.util.Enumeration enum = card.getBankAccounts().elements(); while (enum.hasMoreElements()) { listModel.addElement(enum.nextElement()); } } catch (Exception e) {} } Most actions call the displayAccounts method that uses the selected card to retrieve and display all the bank accounts (5). This method is used when selecting a card (selectedRowObject event in the table model), when using the push button to add an account, and for commit and rollback. The add button (>>) invokes a user-written method addUniqueBankAcount (6), passing the card and the selected account, before calling the getBankAccounts method: public void addUniqueBankAccount(itso.many.domain.Card aCard, itso.many.domain.BankAccount aBankAccount) throws java.rmi.RemoteException, javax.ejb.FinderException { if ( !this.getAssociatedAccountsListModel().contains(aBankAccount) ) aCard.addBankAccount(aBankAccount); } The remove button (<<) invokes the removeBankAccount method of the selected card with the selectedValue of the list as parameter (7). The account is then removed from the list (8). (The displayAccounts method would still list the removed account.) A BusinessTransaction is used for transaction management (9). It is connected to the two models and is used by the Commit and Rollback buttons. 182 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs In the initConnections method the datastore is activated and the BusinessTransaction is initialized so that the allInstances methods can run to fill the lists: itso.many.services.ManyToManyManyToManyDataStore.singleton().activate(); getBusinessTransaction().getTransaction(); Figure 118 shows a sample run of the applet. Figure 118. Applet Run for Many-To-Many Association Chapter 9. Many-to-Many Associations 183 184 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 10 Inheritance In the examples so far, we have looked at traditional object models and mappings. One of the strengths of Persistence Builder lies in its ability to bridge the object world to the relational world. We would like to be able to take full advantage of the techniques and methods of object modeling and use those to persist our object models into relational databases. One of these techniques is inheritance. In this chapter we explore the ways that the Persistence Builder accommodates the idea of modeling inherited objects. We will concentrate our efforts on the BankAccount class, and consider an object model where there is more than one type of BankAccount, mainly checking and savings accounts (Figure 119). A customer’s bank account is always represented by one of the different subclasses of BankAccount. © Copyright IBM Corp. 2000 185 BankAccount accountId balance Checking overdraft Savings minAmount Figure 119. BankAccount with Inheritance: Checking and Savings There are two ways to match an inheritance tree to a relational database: either all of the attributes of the class hierarchy are combined in one table (single table inheritance mapping) or a table is used for each class and the necessary foreign key relationships between the tables are defined (multiple table inheritance mapping). Single Table Inheritance Mapping Mapping the BankAccount class and its subclasses into one table means that we put all attributes, whether from the BankAccount class or from any of its subclasses, into one table. In addition, a discriminator column is needed that holds an indicator for the subclass that the row belongs to. This is required to ensure that the Persistence Builder constructs the proper object when a row is read from the database. The discriminator column contains constant values that indicate the class represented by a particalur row in the table. 186 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Table CUSTOMER customerId 101 102 Discriminator column 103 unused space in table accountId customerId balance accountType minAmount overdraft 101-1001 101 1495.00 102-2003 102 223.57 CHECKING 50.00 103-3001 103 675.82 CHECKING 50.00 SAVINGS 100.00 Table BANKACCOUNT Figure 120. Single Table Inheritance Mapping As Figure 120 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. Although this example is small, the problem is amplified when the number of columns increases. Support of null values and views of today’s relational database systems eases the problem somewhat, but mapping a class hierarchy to a single table is 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. Define the Model For this example, we will model the Customer and BankAccount classes, and the Checking and Savings subclasses. Create a new model called Inheritance in the Model Browser. Chapter 10. Inheritance 187 Define the Customer and BankAccount Classes The first step is to define the Customer and BankAccount classes with the attributes specified in Table 9. Table 9. Attributes of Customer and BankAccount Class Name Attribute Name Attribute Type Required Customer customerId String Yes (Object ID) title String No firstName String No lastName String No accountId String Yes (Object ID) balance BigDecimal No BankAccount Define the Checking and Savings Subclasses Once the BankAccount class is defined, we can use it to create the two subclasses: Checking and Savings. The attributes in these classes are specified in Table 10. Table 10. Attributes of Checking and Savings Class Name Attribute Name Attribute Type Required Checking overdraft BigDecimal No Savings minAmount BigDecimal No Note that only the attributes specific to each class are defined. The accountId and balance attributes are inherited from the BankAccount class. When defining these classes we must indicate to the Persistence Builder that both the Checking and Savings classes have the BankAccount class as their superclass. This is done in the Superclass pull down list in the top right corner of the Class Editor dialog (Figure 121). 188 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 121. Class Editor: Definition of a Subclass We also define a superclass for the Savings class in the same way. In the Model Browser, the Checking and Savings classes may not appear in the Model Classes pane. If they are not visible, then double-click on the BankAccount class in the list. Because they are subclasses of BankAccount, they are arranged as such in the browser. Any class that has subclasses in the Model Browser or Map Browser is denoted with three dots after the class name (in this case, it is BankAccount...). Double-clicking on the superclass will expand or collapse the subclasses in the pane (Figure 122). Figure 122. Model Browser: Collapse and Expand of Subclasses Chapter 10. Inheritance 189 Define the Customer to BankAccount Association In our object model, there is a one-to-many relationship between Customer and BankAccount. We define this association to complete our object model definition (Figure 123). Figure 123. Customer to BankAccount Association Generate the Schema and Datastore Mapping Once the object model is complete, save it in a package called itso.inheritance.metadata, and generate the schema from the model (Models -> Generate schema from model). Define the Schema and Mapping The schema generation process creates the datastore schema and mapping. Both are available for examination in the Schema Browser and Map Browser, respectively. Note that we used ITSO as the table prefix. 190 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Define Physical Names for Schema Identifiers As shown in other examples, some of the schema identifiers violate DB2 naming restrictions. The identifiers and their physical name definitions are specified in Table 11. Table 11. Physical Name Definitions Identifier Name Identifier Type Physical Name Customer Table CustomerInh BankAccount Table BankAccountInh Checking_overdraft Table Column overdraft Customer_customerId Table Column customerId Savings_minAmount Table Column minAmount Inheritance ==> accountType (see Notice) Table Column (accountType) CustomerToAccount Foreign Key Relationship CustomerAccount One identifier worthy of note is the Inheritance column in the BankAccount table. The name Inheritance was given as a default from the schema generation process. This is the discriminator column that will contain either SAVINGS or CHECKING. It is the information from this column that Persistence Builder will use to construct the appropriate object when reading information from the database. We have called it accountType to be more descriptive of its function. Notice We changed the name of the Inheritance column to accountType using the Rename Column function. The service code that was generated did not work if the column name was different from the physical name. Note that we changed the physical names of the tables to avoid conflicts with other sample tables. AccountType Column and Customer_customerId Column When we create a new entry in the BankAccount table, we will require that the account type and the customer who owns the account be specified. To assert this information in the Schema, we deselect the Allow Nulls checkbox in the Column Editor for both the accountType column (Figure 124) and the Customer_customerId column (is actually generated with nulls not allowed). Chapter 10. Inheritance 191 Figure 124. Column Editor: Account Type Note that we have not optimized the table in any way. The schema uses a default length of 30 characters for strings. In most real world cases we would tailor the length of the database columns, as we did for the accountType column. Export the Schema to the Database To export the schema to the database select Schemas -> Import/Export Schemas -> Export Entire Schema to Database... and configure the parameters to export the schema to a database (for example, VAPSAMPL). Also be sure to save the schema in the workspace. 192 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Explore the Datastore Mapping Because we have defined an object model with inheritance, our datastore mapping will be somewhat different than the simple mappings we have seen earlier. The Checking and Savings classes inherit from the BankAccount class. Therefore, a Single Table Inheritance Map has been used to map the objects to the BankAccount table, with BankAccount at the root of the model hierarchy. To see this, open the BankAccount table map for the Persistent Class BankAccount in the Map Browser (Figure 125). Figure 125. Single TableInheritance Map for BankAccount Note that because BankAccount is at the root of the model hierarchy, this box is checked. When this box is checked, we must select a Discriminator column. In this example it is the accountType column in the BankAccount table. Modify the Discriminator Values The generation process set the discriminator value to the default value BankAccount for the BankAccount table map (in the Checking table map it is set to Checking). In our database, we have chosen the following discriminating scheme: for Checking accounts, the discriminator column will contain the value CHECKING, and for Savings accounts, the discriminator column will contain the value SAVINGS. We will not be requiring a discriminator value for the BankAccount class, because if an object is a BankAccount it is either a checking account or a savings account. Nevertheless, the Persistence Builder requires a discriminator value for BankAccount, so we have set it to the value BANKACCOUNT to be consistent with our discriminating scheme. Chapter 10. Inheritance 193 We could of course use any discriminating scheme we wish. For example, we could have used S for savings and C for checking. Or we could have used integers and defined 0 for savings and 1 for checking. No matter what scheme is chosen, it must be accurately specified in the model design, and correctly implemented in the database, otherwise queries to the database will produce incorrect or no results at all. To implement our discriminating scheme, we must also rename the discriminator value for the Checking table map from Checking to CHECKING and the Savings table map from Savings to SAVINGS. We also note that these table maps have the Root of the model hierarchy checkbox deselected, because they are subclasses of the BankAccount class in the object model (Figure 126). Figure 126. Tailored Table Maps for Inheritance Generate the Domain and Service Classes The model definition is now complete. Save the datastore mapping, and generate both the domain classes and service classes into the packages itso.inheritance.singletable.domain and itso.inheritance.singletable.services in your project. 194 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Test Examples Once all of the necessary code is generated, and the database is populated (you can use the inheritancedata.sql file to load sample data), you can test the model with sample code. Scrapbook Examples In each of our samples, we must first activate the datastore we are accessing, initialize and begin a transaction, execute our instructions, and either commit or rollback the transaction we began. The latter step is necessary to terminate the transaction and avoid “ghost” transactions that would otherwise occupy system resources. These samples are easily executed in the VisualAge for Java Scrapbook window. The samples provided may have different package names than what you have used, but the concept remains the same. In our first sample (Figure 127) we will demonstrate the differences between the account home classes that are generated: BankAccountHome, CheckingHome and SavingsHome. Executing the allInstances method on the CheckingHome or SavingsHome returns a vector of checking accounts or savings accounts, respectively. Executing the allInstances method on the BankAccount home returns a vector of all bank accounts, whether they are checking accounts or savings accounts. You can check what kind of object it is before printing the appropriate properties. Chapter 10. Inheritance 195 // Page -> Run in BankAccount in itso.inheritance.singletable.domain // Activate the Datastore itso.inheritance.singletable.services.InheritanceInheritanceDataStore.singleton(). activate(); // Begin a Transaction com.ibm.vap.Transactions.Transaction.begin(); // List all BankAccounts System.out.println("All BankAccounts:\n"); java.util.Enumeration enum = BankAccountHomeImpl.singleton().allInstances().elements(); while (enum.hasMoreElements()) { BankAccount acct = (BankAccount)enum.nextElement(); if (acct instanceof Checking) { Checking aChecking = (Checking)acct; System.out.println("Checking "+acct.getAccountId() +" "+aChecking.getBalance()+" "+aChecking.getOverdraft()); } else if (acct instanceof Savings) { Savings aSavings = (Savings)acct; System.out.println("Savings "+acct.getAccountId() +" "+aSavings.getBalance()+" "+aSavings.getMinAmount()); } } // List all Checking Accounts System.out.println("\nChecking Accounts:\n"); enum = CheckingHomeImpl.singleton().allInstances().elements(); while (enum.hasMoreElements()) { Checking aChecking = (Checking)enum.nextElement(); System.out.println(aChecking.getAccountId()+" "+aChecking.getOverdraft()); } // List all Savings Accounts System.out.println("\nSavings Accounts:\n"); enum = SavingsHomeImpl.singleton().allInstances().elements(); while (enum.hasMoreElements()) { Savings aSavings = (Savings)enum.nextElement(); System.out.println(aSavings.getAccountId()+" "+aSavings.getMinAmount()); } // Commit the Transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 127. Scrapbook: Inheritance Home Classes (InheritHomes.scrap) To verify these results use the SQL Query Tool to query the BankAccountInh table in the database. 196 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs In our second example (Figure 128), we show that creating a new checking account in CheckingHomeImpl causes a record to be added to the BankAccount table with CHECKING as the discriminator value. // Page -> Run in BankAccount in itso.inheritance.singletable.domain // Activate the datastore itso.inheritance.singletable.services.InheritanceInheritanceDataStore.singleton(). activate(); // Begin a Transaction com.ibm.vap.Transactions.Transaction.begin(); // Retrieve a Customer Customer cust = CustomerHomeImpl.singleton().find("103"); System.out.println( cust.getLastName() ); // Create a new Checking Account Checking newAccount = CheckingHomeImpl.singleton().create("103-3003"); newAccount.setAccountOwner( cust ); newAccount.setBalance(new java.math.BigDecimal(200.00)); newAccount.setOverdraft(new java.math.BigDecimal(50.00)); // Commit the Transaction and Begin a new one com.ibm.vap.Transactions.Transaction.getCurrent().commit(); com.ibm.vap.Transactions.Transaction.begin(); // Display all BankAccounts of the Customer java.util.Enumeration enum = cust.getOwnedAccounts().elements(); while (enum.hasMoreElements()) { BankAccount acct = (BankAccount).enum.nextElement(); System.out.println(acct.getAccountId()+" "+acct.getBalance()); } // Commit the Transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); com.ibm.vap.Transactions.Transaction.begin(); // Delete the new CheckingAccount newAccount = CheckingHomeImpl.singleton().find("103-3003"); newAccount.remove(); com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 128. Scrapbook: Inheritance Create Checking (CreateChecking.scrap) We begin by creating a new Checking object in the CheckingHome class, and setting the customer, balance, and overdraft values. Then to verify, we display all of the BankAccounts that the customer owns. To see that the discriminator column is correctly set, use the SQL Query Tool to query the BankAccount table. Remove the delete of the new account at the end of the Scrapbook. Chapter 10. Inheritance 197 Applet Example Since the most complex relationship in the model is a one-to-many relationship, an applet can be built using any of the techniques described in this book. We leave it up to the reader to explore examples of interest. Multiple Table Inheritance Mapping Mapping an inheritance hierarchy to multiple tables means putting all of the common attributes of the superclass into one table, and placing 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 129). Table CUSTOMER Table BANKACCOUNT accountId customerId balance accountType customerId 101 101-1001 101 1495.00 SAVINGS 102 102-2003 102 223.57 SAVINGS 103 103-3001 103 675.82 CHECKING Discriminator column accountId minAmount accountId overdraft 101-1001 50.00 103-3001 102-2003 100.00 Table SAVINGS 50.00 Table CHECKING Figure 129. Multiple Table Inheritance Mapping This results in an effective use of space in the database tables. Nothing is wasted as it was with single table inheritance mapping. However, 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 and writing objects in the database now requires two statements: one for the superclass object, and one for the subclass object. With single table inheritance mapping, only one statement was required. As a result, the use of multiple table inheritance mapping may be appropriate when subclasses differ considerably in their data. Mapping very different subclasses to a single table would result in a lot of unused space. 198 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Define the Model To demonstrate multiple table mapping and to contrast it with the single table inheritance mapping, we will use the BankAccount inheritance example again. Fortunately, we can use the same object model again. This demonstrates one of the benefits of separating object modeling considerations from implementation issues. Since we have the object model defined, we will manually define a new schema and mapping. Define the Schema Create a new schema in the Schema Browser with an appropriate name, for example, MultipleInheritance. Define the Tables and columns The table and column definitions appear in Table 12. Table 12. Schema Table and Column Definitions Table Name Column Name Column Type Allow Nulls Customer customerId VARCHAR(3) No (Primary Key) physical: CustomerMult qualifier: ITSO title VARCHAR(5) Yes firstName VARCHAR(20) Yes lastName VARCHAR(20) Yes BankAccount accountId VARCHAR(8) No (Primary Key) physical: BankAccountMult qualifier: ITSO customerId VARCHAR(3) No (Foreign Key) balance DECIMAL(20,2) No accountType VARCHAR(8) No Checking accountId VARCHAR(8) No (Primary Key) (Foreign Key) physical: CheckingMult, qualifier: ITSO overdraft DECIMAL(20,2) Yes Savings accountId VARCHAR(8) No (Primary Key) (Foreign Key) physical: SavingsMult, qualifier: ITSO minAmount DECIMAL(20,2) Yes Chapter 10. Inheritance 199 Define the Foreign Key Relationships We will define three foreign key relationships. One between Customer and BankAccount, one between BankAccount and Checking, and the third between BankAccount and Savings. Figure 130 illustrates two of the relationships. Figure 130. Foreign Key Relationship Editor: Customer and BankAccount Export the Schema to the Database As before, export the schema to the database select (Schemas -> Import/Export Schemas -> Export Entire Schema to Database...) and configure the parameters to export the schema to the VAPSAMPL database. Also be sure to save the schema in the workspace in the itso.inheritance.metadata package. Define the Mapping We now define a datastore mapping to map the entities of the object model to the tables in the schema. To begin, create a new mapping in the Map Browser with the name MultipleInheritance. Be sure to select the correct object model (Inheritance) and correct schema (MultipleInheritance) in the New Datastore Map dialog. 200 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Define the Customer Table Mapping The definition of the Customer table map uses a cluster map with no inheritance and the property map is a straightforward simple mapping from Customer attributes to Customer columns. This map is identical to the Customer map in the single table inheritance example. Be sure to map the Customer–BankAccount association to the proper foreign key relationship (Associations tab in the Property Map Editor dialog for the Customer table). Define the BankAccount Mapping We will use the Root/Leaf Inheritance table map for the BankAccount table (Figure 131). Figure 131. Root/Leaf Inheritance Table Map: BankAccount As you can see, this is somewhat similar to the single table inheritance map for BankAccount. BankAccount is at the root of the inheritance hierarchy, and therefore the Root of the model hierarchy checkbox is selected. The foreign key relationship box is disabled for objects at the root of the hierarchy. Note that we are also using the same discriminator scheme as before. We set the discriminator column to the accountType column and the value to BANKACCOUNT. Define the Mapping for Checking and Savings The Root/Leaf Inheritance table map is also used to create the table mappings for both the Checking and Savings classes (Figure 132). Chapter 10. Inheritance 201 Figure 132. Root/Leaf Inheritance Table Map: Checking Checking is a subclass of BankAccount, so the Root of the model hierarchy checkbox is deselected, disabling the discriminator column pull-down list, and enabling the foreign key relationship pull-down list. For the foreign key, we have selected the relationship we defined between the BankAccount and Checking tables. This relationship is specified so that the Persistence Builder is aware of the table and relationship for the subclass. When writing to the database occurs, the primary key of the row in the BankAccount table will be placed in the Checking table accountId column, and the accountType column in the BankAccount table will have the value CHECKING. The overdraft value is specified in the Checking table. Any reading that takes place then, relies on the relationship between the BankAccount and Checking tables to obtain the additional information for the subclass. Figure 133 shows the property map for the Checking table. Figure 133. Root/Leaf Inheritance Table Map: Checking 202 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Only the overdraft column is mapped, the other columns are mapped in the superclass and cannot be modified here. The Savings table map is defined in a similar way, with the discriminator value set to SAVINGS and the foreign key relationship set to the BankAccount and Savings relationship. Generate the Domain and Service Classes After saving the datastore mapping, generate the domain and service classes into an appropriate project and package. Because we did not modify the model there is no need to regenerated the domain classes. However, you must generate the service classes, for example into the itso.inheritance.multipletable.services package. The model definition is now complete, and is ready for testing. Test Example Since we have only changed the structure of our schema and mapping, we can reuse the sample code from the single table inheritance mapping example, with modifications to some class and package names. We invite you to make the modifications to verify that the new model does work, and, as always, encourage you to try other examples to satisfy your curiosity. We provide a sample file to load data (Multiple.sql), a scrapbook to list the bank accounts by type (MultipleHomes.scrap), and a scrapbook to add an account to a customer (MultipleCreateChecking.scrap). The only changes in the scrapbooks compared to the single inheritance table model is the activation of the datastore: itso.inheritance.multipletable.services.MultipleInheritanceDataStore. singleton().activate(); Chapter 10. Inheritance 203 204 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 11 Complex Mappings The Persistence Builder provides maximum flexibility when defining a mapping between an object model and database schema. We have already seen examples of single table inheritance and multiple table inheritance, as well as straightforward mapping of one object to one table. In this chapter we explore the use of secondary table maps, composers, and converters to round out the complement of mapping tools available in the Persistence Builder. Secondary Table Mappings A secondary table mapping is used when a persistent model class is mapped to two or more database tables. Consider an example where we separate the personal data of a customer (such as birthdate and address) from the identity of a customer (title, firstName, lastName) in the database. We might have a database table model similar to that of Figure 134. © Copyright IBM Corp. 2000 205 Table CUSTOMER customerId title firstName lastName 101 Mr. John Smith 102 Mrs. Mary Jones 103 Dr. Pat Harris Table CUSTOMERDATA customerId birthdate street city state zip 101 1955-01-10 113 Kent St. Albany NY 12208-3507 102 1947-06-11 650 Harry Rd. San Jose CA 95120-6099 103 1960-03-04 200 Garrison Dr. Dallas TX 75253-5744 Figure 134. Secondary Table Mapping: Database Table Model When Customer objects are written to the database, the title, firstName, and lastName attributes are written to the Customer table, while the remaining attributes are written to the CustomerData table. The advantage to this mapping method relates to the familiar 80-20 rule. We access 20% of the Customer attributes (stored in the Customer table) 80% of the time, and 80% of the Customer attributes (stored in the CustomerData table) 20% of the time. We can justify this by claiming that most often, searches occur on name or customer number, and not birthdate or city. One minor disadvantage to this method, however is the introduction of a join query to retrieve the 80% of the customer attributes when instantiating objects. Unless there are a large number of columns or a lot of data, this performance decrease is manageable. Note: A secondary table mapping is only valid if the foreign key in the secondary table is also the primary key of that table. In our example, customerId is the primary key in both tables. We will now explore the model, mapping, and schema using this Customer example. 206 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Define the Model Create a model with an appropriate name (ComplexMapping) in the Model Browser. The attributes of the Customer class are described in Table 13. Table 13. Complex Mapping: Attributes of Customer Class Name Attribute Name Attribute Type Required Customer customerId String Yes (Object ID) title String No firstName String No lastName String No birthdate Date No street String No city String No state String No zip String No Save the model in an appropriate package (itso.complex.metadata) and project. Define the Schema Create a schema with the same name in the Schema Browser. The table and column definitions are described in Table 14 (primary table) and Table 15 (secondary table). Table 14. Complex Mapping: Primary Table Table Name Column Name Column Type Allow Nulls Customer customerId VARCHAR(3) No (Primary Key) Physical: CustomerComp title VARCHAR(5) Yes firstName VARCHAR(20) Yes Qualifier: ITSO lastName VARCHAR(20) Yes Chapter 11. Complex Mappings 207 Table 15. Complex Mapping: Secondary Table Table Name Column Name Column Type Allow Nulls CustomerData customerId VARCHAR(3) No (Primary Key) (Foreign Key) Physical: CustomerDataComp birthdate DATE Yes street VARCHAR(20) Yes city VARCHAR(20) Yes state VARCHAR(2) Yes zip VARCHAR(10) Yes Qualifier: ITSO We also need a foreign key relationship between the Customer and CustomerData Tables (Figure 135). Figure 135. Customer and CustomerData Foreign Key Relationship The schema definition is complete. Be sure to save the schema in the appropriate project and package and export the entire schema to the database. 208 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Define the Mapping The object model and schema were straightforward to define. We will be using the secondary table map when we map attributes of the Customer class to the CustomerData table. Create a new mapping with an appropriate name (Complex) in the Map Browser, ensuring the proper object model and schema are selected. For the customerId, title, firstName, and lastName attributes of the Customer class, we will use a Cluster Table Map with no inheritance defined on the Customer table (Figure 136). Figure 136. Complex Mapping: Primary Table Map Properties We now define a Secondary Table Map (Figure 137) to map the remaining attributes to the CustomerData table (Table_Maps -> New Table Map -> Add Secondary Table Map...). Chapter 11. Complex Mappings 209 Figure 137. Complex Mapping: Secondary Table Map Definition The remaining attributes in the Customer class are mapped to the CustomerData table (Figure 138). Figure 138. Complex Mapping: Secondary Table Map Properties Our mapping definition is complete. Be sure to save the datastore mapping, and generate the domain and service classes into the packages itso.complex.domain and itso.complex.services. Test Examples We provide a file to load sample data (Complexdata.sql), a scrapbook to list all customers (ComplexList.scrap), and a scrapbook to insert a new customer (Complexinsert.scrap). 210 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs We noticed the following behavior: ❑ For the customer list, a join is performed to retrieve all attributes for a customer object. >>>executing: SELECT T2.customerId, T2.title, T2.firstName, T2.lastName, T1.birthdate, T1.street, T1.city, T1.state, T1.zip FROM ITSO.CustomerDataComp T1, ITSO.CustomerComp T2 WHERE T2.customerId = T1.customerId ❑ For a new customer, two inserts are executed in the order required by the foreign key to add the data into the two tables: >>>executing: INSERT INTO ITSO.CustomerComp ( customerId, title, firstName, lastName ) VALUES ( ? , ? , ? , ? ) >>>executing: INSERT INTO ITSO.CustomerDataComp ( customerId, birthdate, street, city, state, zip ) VALUES ( ? , ? , ? , ? , ? , ? ) Figure 139 shows the scrapbook files to list the customers. // Activate the Datastore itso.complex.services.ComplexDataStore.singleton().activate(); // Begin a Transaction com.ibm.vap.Transactions.Transaction.begin(); // List all Customers System.out.println("All Customers:\n"); java.util.Enumeration enum = itso.complex.domain.CustomerHomeImpl.singleton().allInstances().elements(); while (enum.hasMoreElements()) { itso.complex.domain.Customer aCust = (itso.complex.domain.Customer)enum.nextElement(); System.out.println(aCust.getCustomerId()+" "+aCust.getLastName() +" "+aCust.getBirthdate()+" "+aCust.getCity()); } // Commit the Transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 139. Scrapbook: Complex Mapping (Complexlist.scrap) Chapter 11. Complex Mappings 211 Composers Certain situations might arise where one attribute of a class must be mapped to more than one column of table in a database. To accommodate this situation, the Persistence Builder supports the notion of a composer. Consider the Customer example again. Our database table has three columns for a customer name: title, firstName, and lastName. In its present state, the Customer class has three attributes: title, firstName, and lastName. Instead we would like to have only one attribute for a greeting in the Customer class. To map this greeting to the columns in the Customer table, we use a composer. Defining a composer allows us to take the three columns in the Customer table (title, firstName, and lastName), and compose them into an object (CustomerGreeting), which in turn will be used to define an attribute in the Customer class. To implement the transformation of multiple database columns into a single attribute we need two classes: ❑ An attribute class: CustomerGreeting ❑ A composer class: CustomerGreetingComposer A composer is a class that extends from VapAttributeComposer, and implements the methods to transform between the attribute class and an array of objects, the values from the database columns. Define the Attribute Class The attribute class holds properties that will be used in the business model, and methods that are used by the composer class. Let us define the CustomerGreeting class (Figure 140): ❑ Constructors to create the object from a greeting or from three strings ❑ Methods (getText and setText) for the business model ❑ Methods (getTitle, getFirstname, getLastname) for the composer 212 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs package itso.complex.composer.domain; import java.util.StringTokenizer; public class CustomerGreeting implements java.io.Serializable { String title, firstname, lastname; public CustomerGreeting() { super(); } public CustomerGreeting(String text) { // construct with a greeting super(); setText(text); } public CustomerGreeting(String t, String f, String l) { // construct with strings title = t.trim(); firstname = f.trim(); lastname = l.trim(); } public String getFirstname () { return firstname; } public String getLastname () { return lastname; } public String getTitle () { return title; } public String getText () { return title + " " + firstname + " " + lastname; } public void setText(String text) { // extract 3 tokens from greeting title = null; firstname = null; lastname = null; StringTokenizer st = new StringTokenizer(text, " "); int tokens = st.countTokens(); if (tokens == 0) return; if (tokens == 1) { lastname = st.nextToken(); return; } if (tokens == 2) { title = st.nextToken(); lastname = st.nextToken(); return; } title = st.nextToken(); firstname = st.nextToken(); lastname = st.nextToken(); return; } public String toString() { return getText(); } } Figure 140. Composer Attribute Class: CustomerGreeting Define the Composer Class The composer class, called CustomerGreetingComposer, is a subclass of com.ibm.vap.composers.VapAttributeComposer. It has only one singleton object that performs the transformations (Figure 141). The methods listed here must be implemented: ❑ The singleton method creates the composer object ❑ The getTargetClassName returns the name of the attribute class ❑ The getAttributesMethod returns the names of the internal attributes that map to the database columns ❑ The getSourceDatatype method returns the types of these attributes ❑ The reset method removes the singleton object Chapter 11. Complex Mappings 213 package itso.complex.composer.domain; public class CustomerGreetingComposer extends com.ibm.vap.composers.VapAttributeComposer { private static CustomerGreetingComposer singleton; public static CustomerGreetingComposer singleton ( ) { if (singleton == null) singleton = new CustomerGreetingComposer(); return singleton; } public static String getTargetClassName() { return "itso.complex.composer.domain.CustomerGreeting"; } public static String[] getAttributeNames() { String[] attributes = { "title", "firstname", "lastname" }; return attributes; } public static String[] getSourceDatatype() { String[] types = { "String", "String", "String" }; return types; } public Object[] dataFrom (Object anObject) { CustomerGreeting gr = (CustomerGreeting) anObject; Object[] anArray = new Object[3]; anArray[0] = null; anArray[1] = null; anArray[2] = null; if (anObject == null) {return anArray; } anArray[0] = gr.getTitle(); anArray[1] = gr.getFirstname(); anArray[2] = gr.getLastname(); return anArray; } public Object objectFrom (Object[] anArray) { String title, first, last; title = (String) anArray[0]; first = (String) anArray[1]; last = (String) anArray[2]; return new CustomerGreeting(title,first,last); } public static void reset() { singleton = null; return; } } Figure 141. Composer Class: CustomerGreetingComposer The methods that perform the actual transformation between the database columns and the attribute class are dataFrom and objectFrom: ❑ The dataFrom method transforms an attribute object into an array of three objects for the database columns by accessing the getter methods of the attribute object. ❑ The objectFrom method transforms an array of three database column values into an attribute object by using one of the constructors. 214 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Define Schema, Model, and Mapping To use the customer greeting we define a small mode, schema, and mapping. We use the itso.complex.metadata package for the definitions, the itso.complex.composer.domain package for the business model classes, and the itso.complex.composer.services package for the service classes. Schema We use the CUSTOMERCOMP table from the previous example (“Define the Schema” on page 207). Import the table into a new schema called ComplexComposer. (We do not use the secondary CUSTOMERDATACOMP table for this example.) Model Define a new model called ComplexComposer with a Customer class. Add two attributes: customerId of type String (object ID), and greeting of type CustomerGreeting. Note that the type drop-down list in the Attribute Editor contains all the types defined in composer classes (Figure 142). Figure 142. Complex Mapping: Greeting Attribute Map Define a new map called ComplexComposer and map the model to the schema. Create a new table map (cluster map with no inheritance) and open the Property Map Editor. Map the greeting attribute as a complex map type, and click on the button to define the mapping in the complex attribute editor. Select the CustomerGreetingComposer class and then map the three composer attributes to the appropriate table columns (Figure 143). Chapter 11. Complex Mappings 215 Figure 143. Complex Mapping: Greeting Attribute Generate the Model and Service Classes Save the model, schema, and map, and then generate the model classes (into the itso.complex.composer.domain package) and the service classes into the itso.complex.composer.services package). 216 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Test the Example Figure 144 shows a scrapbook to add a customer to the database using a greeting as input. After committing the update, all the customer are listed. The composer transforms between the greeting text and the three database columns. // Activate the Datastore itso.complex.composer.services.ComplexComposerDataStore.singleton().activate(); // Begin a Transaction com.ibm.vap.Transactions.Transaction.begin(); // New Customer itso.complex.composer.domain.Customer newCust = itso.complex.composer.domain.CustomerHomeImpl.singleton().create("120"); newCust.setGreeting( new itso.complex.composer.domain.CustomerGreeting ("Ms. Shania Twain") ); System.out.println(newCust.getCustomerId()+" "+newCust.getGreeting().getText()); // Commit the Transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); com.ibm.vap.Transactions.Transaction.begin(); // List all Customers System.out.println("All Customers:\n"); java.util.Enumeration enum = itso.complex.composer.domain.CustomerHomeImpl.singleton().allInstances().elements(); while (enum.hasMoreElements()) { itso.complex.composer.domain.Customer aCust = (itso.complex.composer.domain.Customer)enum.nextElement(); System.out.println(aCust.getCustomerId()+" "+aCust.getGreeting()); } // Commit the Transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 144. Scrapbook: Composer Test (Composerlist.scrap) Converters Logically, converters are a special case of composers; they work on one item only. However, converters are specified in the Schema Browser, as opposed to composers that are specified in the Map Browser. The Persistence Builder provides many typical conversions between datatypes in database columns and datatypes in the Java language (Tables 16 and 17). Chapter 11. Complex Mappings 217 Table 16. Converters Converter From Type To Type VapConverter Default, no conversion VapCharToBoolean CHAR Y,y,T,t,1 CHAR N,n,F,f,0; other true false VapCharToString CHAR String VapStringVarChar VARCHAR (*) String (*) = VARCHAR, CHAR, LONG VARCHAR VapTrimStringConverter VapNumberToXxxxxx Converter 218 VARCHAR (*) String with leading and trailing blanks removed Number (**) java.lang.Xxxxxx (Character, Integer, Byte, Short, Long, Boolean) (**) = INTEGER, BIGINT, TINYINT, SMALLINT VapStringToXxxxxx Converter VARCHAR (*) java.lang.Xxxxxx (Boolean, Byte, Character, Integer, Short, Long, Float, Double) java.math.BigDecimal VapXxxxxxToString Converter Integer, Float, Double, Short (SMALLINT) , Long (BIGINT), Byte (CHAR(1)), BigDecimal (DECIMAL) String VapDateToCalendar Converter DATE java.util.Calendar VapTimeStampTo CalendarConverter TIMESTAMP java.util.Calendar VapTimeToCalendar TIME java.util.Calendar VapStringCalendar Converter VARCHAR (*) representing short dates: yyyy.mm.dd java.util.Calendar VapLongStringCalendar Converter (new in V3.02) VARCHAR(*) representing long dates: java.util.Calendar yyyy.mm.dd at mm:hh:ss tz VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Table 17. Stream Converters Converter From Type To Type VapAsciiStreamToString Converter ASCII byte stream String VapBinaryStreamTo ByteArrayConverter Binary byte stream byte[] VapBinaryStreamTo SerializableObject Converter Binary byte stream java.io.Serializable VapUnicodeStreamTo StringConverter Unicode byte stream String Building Your Own Converter You can build your own converter as well. You would create a subclass of VapConverter and implement the same methods as for a composer (Figure 141 on page 214), with the exception that you deal with one object instead of an array. The best approach for building your own converter would be to study the implementation of the converters provided with the Persistence Builder in the com.ibm.vap.converters package. Chapter 11. Complex Mappings 219 220 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 12 Custom Queries When generating the service classes for an object model, the Persistence Builder creates a query pool class for each home collection class. This class contains methods defined for specific queries on the database. The needs of an application, however, may dictate the need for a query not available in the query pool. In this chapter, we show how to create custom queries for a home collection in the model. Custom queries are useful to improve the performance of an application, or replace awkward Java code with a simple SQL query. Custom queries can be created to represent any SQL query on a database. Just the structure of the returned data is given for select queries and cannot be changed. Note that custom queries always retrieve data from the database even if some of the objects may already exist in the memory cache. This is the same behavior as with the allInstances method. The Query Pool Class The queries for a home collection are stored as method pairs in a query pool class. The query pool classes are generated along with all of the service classes. To locate the query pool for the BankAccount home, for example, we © Copyright IBM Corp. 2000 221 look for a class name with the suffix BankAccountQueryPool in the itso.vap.simple.services package. The prefix to the class name is the name of the datastore map used in the model definition. Therefore if we were using SimpleSimple as our map name, the query pool class name is SimpleSimpleBankAccountQueryPool. If you explore the service classes for any of the examples created in this book, you will find query pools for each of the home collections. In each query pool is a set of method pairs for a query. Each query is identified by a unique name that is incorporated into the method names. The naming scheme for these pairs for a query named queryName is as follows: ❑ queryNameQuery is the method that defines the query specification possibly including the input shape and the output shape. We will refer to it as the query method. ❑ queryNameSqlString is the method that returns a string version of the SQL command that will be executed for the query. We will refer to it as the SQL string method. If you use service classes with pessimistic locking then two additional methods for each query are generated: queryNameQueryForLocking and queryNameSqlStringForLocking. See “Pessimistic Locking” on page 230 for more details. When the service classes are generated, a set of default queries are created with the following names: ❑ allInstances: returns all entries for the respective type from the database. ❑ delete: provides the functionality to delete information from the database. ❑ insert: allows for information to be inserted into the database. ❑ update: provides the capability to update information in the database. The generated methods look different, depending on whether you specified Generate queries using parm marker bindings during service code generation or not (see “Generate Service Classes for Relational SQL Schema” on page 79). Moreover the query methods for the queries mentioned above are not generated at all if the Generate queries using parm marker bindings option was not turned on; instead their respective default implementations in the QueryPool superclass are used. In addition to these default queries, other queries to retrieve linked objects are generated for the associations that were defined in the model. 222 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Important These query methods are provided in the services layer of the Persistence Builder so it can manipulate the database, and are not intended for public use. Instead the methods of the home collections should be used to create, retrieve, update, and delete information. The methods that are created in the query pool class depend largely on the type of query you require. In this chapter, we consider two types of queries: ❑ Fixed Queries. These are queries with no variable arguments. Consider a classic SELECT query: SELECT * FROM TABLEXYZ. In this query there is no part that may vary each time the query is used. ❑ Variable Queries. These are queries that contain variable arguments. We might SELECT from a table based on a primary key in an ID column: SELECT * FROM TABLEXYZ WHERE ID = 123. In an application, we would like the ability to substitute 123 with another value the next time we execute the query. Therefore, the variable argument in this example is contained within the WHERE clause. SQL String Methods Creating an SQL string method for your own custom queries follow one of the two patterns, fixed or variable. Note that for every select query the shape of the result set (which database columns and in which order) is not arbitrary. The shape must always be the same as defined in the allInstances method of the respective query pool class. Fixed Queries: allInstancesSqlString Parameter Markers Option Turned On The allInstancesSqlString method provides us with a straightforward example of a fixed query SQL string method (Figure 145). // // allInstancesSQLString Method for BankAccount // public java.lang.String allInstancesSqlString() { return "SELECT T1.balance, T1.accountId, T1.Cust_customerId FROM ITSO.ACCOUNT00 T1"; } Figure 145. SQL String Method: allInstancesSqlString Chapter 12. Custom Queries 223 The SQL string method simply returns a string containing the appropriate SQL query statement. The Persistence Builder generates SQL string methods that take advantage of table aliasing (T1 is an alias for ITSO.BankAccount in this example). When defining an SQL string method for a custom fixed query, all that is required is determining the proper syntax of the SQL, and implementing similar to the above. Parameter Markers Option Turned Off There is no difference in this simple case whether parameter markers are used or not. Variable Queries: findByKeySqlString For a variable query, the findByKeySqlString method provides an example of how to define the string to accommodate arguments (Figures 146 and 147). Parameter Markers Option Turned On // // findByKeySQLString Method for BankAccount (Parameter Markers Option Turned On) // public java.lang.String findByKeySqlString() { return "SELECT T1.balance, T1.accountId, T1.Cust_customerId FROM ITSO.ACCOUNT00 T1 WHERE T1.accountId = ? "; } Figure 146. SQL String Method with Parm-Markers: findByKeySqlString The argument in this query occurs in the WHERE clause and is denoted by a question mark (?). When the query is invoked in an application, an array of arguments is passed to the query method. The order of elements in this array corresponds to the order of the question marks in the SQL string statement. The question mark(s) will not be replaced at runtime, the string will be used as is for the database prepare statement. Parameter Markers Option Turned Off // // findByKeySQLString Method for BankAccount (Parameter Markers Option Turned Off) // public java.lang.String findByKeySqlString() { return "SELECT T1.balance, T1.accountId, T1.Cust_customerId FROM ITSO.ACCOUNT00 T1 WHERE T1.accountId = :V1"; } Figure 147. SQL String Method without Parm-Markers: findByKeySqlString 224 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Here the argument is denoted by the :V1 variable. When the query is invoked in an application, an array of arguments is passed to the query method. The order of elements in this array corresponds to the order of the variables in the SQL string statement. At runtime these variables are simply replaced with the actual values, that is, the statement is modified before it is submitted to the database system. Query Methods At first glance, defining a query method for your own custom query might seem like a daunting task. As described in this section, query methods are simple structures in the services level of the Persistence Framework. Fixed Queries: allInstancesQuery The allInstancesQuery method provides us with a template that we can work with when defining our own custom queries based on a fixed query. Parameter Markers Option Turned On Figure 148 shows an annotated version of an the allInstancesQuery method with the parameter markers option turned on, for descriptive purposes. There are five components to this query method: 1. Declare Result and Output Shape Variables. These lines simply declare a Vector to return to the calling method in the services layer, and a DatabaseCompoundType variable, used to set the output shape (output parameters) of our query. 2. Define the Query Specification Source. This line declares the source for our SQL query. The argument to the constructor is the SQL string method we have defined for the query. That is, the String value of our SQL query statement. 3. Define the Output Shape of the Query. This section of the method defines the output parameters for our query. We add one DatabaseStringField (accountId), and one DatabaseDecimalField (balance). The setAttributes method has four arguments defining the length, scale, data type, and nullable flag for the field. Note that this is the information you entered in the Schema Browser’s Column Editor for each attribute. The information is used by the framework to construct output that is usable in applications. To define your own output shapes it suffices to copy the section from the allInstancesQuery method that is contained within the same query pool class as the query you are defining. Chapter 12. Custom Queries 225 // // allInstancesQuery Method for BankAccount (Parameter Markers Option Turned On) // public java.util.Vector allInstancesQuery() { // 1) Declare Result and Output Shape Variables Vector aSpecArray = new Vector(); DatabaseCompoundType aCompoundType; // 2) Define the Select Query Specification Source DatabaseQuerySpec spec = new DatabaseSelectQuerySpec(allInstancesSqlString()); // 3) Define the Output Shape of the Query aCompoundType = new DatabaseCompoundType(); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base.DatabaseDecimalField("balance")).setAttributes(8,2,3,false)); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base.DatabaseStringField("accountId")).setAttributes(8,0,1,false)); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base.DatabaseStringField("Cust_customerId")).setAttributes(4,0,1, false)); // 4) Set the Output Shape of the Query Specification ((DatabaseSelectQuerySpec)spec).setOutputShape(aCompoundType); // 5) Return the Query Specification aSpecArray.addElement(spec); return aSpecArray; } Figure 148. Query Method with Parm-Markers: allInstancesQuery 4. Set the Output Shape of the Query Specification. Once the output shape has been defined, we set the output shape for the query specification to our definition. 5. Return the Query Specification. The query specification is added to a Vector and returned to the caller where it is executed. The execution takes place in the services layer of the Persistence Framework. Most of the time, when implementing custom fixed query methods, it is sufficient to copy the allInstancesQuery method and change the name of the SQL string method that is sourced for the query. Parameter Markers Option Turned Off If parameter markers are not used, then the allInstancesQuery method is the same for all classes and is therefore only defined in the QueryPool superclass (Figure 149). 226 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs // // The allInstancesQuery Method of the QueryPool Class // public java.util.Vector allInstancesQuery() { // Declare Result Variable Vector aSpecArray = new Vector(); // Define and Add the Select Query Specification Source aSpecArray.addElement(new DatabaseSelectQuerySpec(allInstancesSqlString())); // Return the Query Specification return aSpecArray; } Figure 149. Query Method of the QueryPool Class: allInstancesQuery This is an annotated version of an the allInstancesQuery method of the QueryPool class (parameter markers option turned off), for descriptive purposes. The difference to the version with the parameter markers option turned on is that no output shape needs to be defined and set for the query specification. You can use this method as template for custom fixed queries. Variable Queries: findByKeyQuery A variable query method is similar in structure to a fixed query method with some additions. We look to the findByKeyQuery method as a template for a custom variable query. You will note that this method takes two parameters: args (a Vector), and anInjector (a BOInjector). The args parameters contains the arguments to the query in the order that the question marks (?) are used in the SQL query string. The BOInjector is a parameter used at the services layer of the Persistence Framework. The method definition of a variable query method requires these two parameters. Parameter Markers Option Turned On Figure 150 shows an annotated version of the findByKeyQuery method with the parameter markers option turned on, for descriptive purposes. The components of this query method are identical to the allInstancesQuery method with the exception of three new components. Because we are using a variable query, these components deal with the definition of the structure of the arguments that are passed to the query. Chapter 12. Custom Queries 227 // // findByKeyQuery Method for BankAccount (Parameter Markers Option Turned On) // public java.util.Vector findByKeyQuery(java.util.Vector args, com.ibm.vap.Persistence.BOInjector anInjector) { // Declare Result and Output Shape variables Vector aSpecArray = new Vector(); DatabaseCompoundType aCompoundType; // Define the Select Query Specification Source DatabaseQuerySpec spec = new DatabaseSelectQuerySpec(findByKeySqlString()); // 1) Define the Input Shape and the Input Parameter Names of the Query Vector stringArgs; aCompoundType = new DatabaseCompoundType(); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base.DatabaseStringField("ACCOUNT00.V1")).setAttributes (8,0,1,false)); stringArgs = new Vector(); stringArgs.addElement("ACCOUNT00.V1"); // 2) Set the Input Shape of the Query Specification spec.setInputShape(aCompoundType); // Define the Output Shape of the Query aCompoundType = new DatabaseCompoundType(); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base.DatabaseDecimalField("balance")).setAttributes(8,2,3,false)); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base.DatabaseStringField("accountId")).setAttributes(8,0,1,false)); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base.DatabaseStringField("Cust_customerId")).setAttributes (4,0,1,false)); // Set the Output Shape of the Query Specification ((DatabaseSelectQuerySpec)spec).setOutputShape(aCompoundType); // 3) Set the Input Values and Parameter Names for the Query spec.setInputValues(args); spec.setInputNamesWithDuplicates(stringArgs); // Return the Query Specification aSpecArray.addElement(spec); return aSpecArray; } Figure 150. Query Method with Parm-Markers: findByKeyQuery 1. Define the Input Shape and Input Parameter Names of the Query. As with defining the output shape, defining the input shape (input parameters) provides information about the type of parameters that will be passed to the query as arguments. The setAttributes method is used for 228 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs this purpose. Defining the input parameter names provides labels that refer to the name of the entity that is an argument, in the form TableName.VariableName (in this case, it refers to the accountId in the BankAccount). These names are stored in a Vector in the order that the arguments appear in the SQL query statement. 2. Set the Input Shape of the Query. Once the input shape has been defined, we set the input shape for the query specification to our definition. 3. Set the Input Values and Parameter Names for the Query. The final step in identifying the structure of our input is to set the value of the input names with the definition in step 2, and to provide the actual values of the passed arguments to the query specification. As with the allInstancesQuery method, it suffices to use the findByKeyQuery method as a template in defining custom variable queries. Parameter Markers Option Turned Off If parameter markers are not used, then also the findByKeyQuery method is the same for all classes and is therefore only defined in the QueryPool superclass. Figure 151 shows an annotated version of the findByKeyQuery method with the parameter markers option turned off, for descriptive purposes. Again one difference to the version with the parameter markers option turned on is that no output shape and no input shape needs to be defined and set for the query specification. The important difference is in step 1, ‘Define the Select Query Specification Source’: The SQL query string is not used as it is, but first it is evaluated, that is all variables in the SQL string (:V1, :V2, .. :Vn) are directly replaced with the actual parameter values contained in the aVector variable. In addition the cacheStatement flag is set to false. You can use this method as template for custom variable queries with the parameter markers option turned off. Chapter 12. Custom Queries 229 // // findByKeyQuery Method of the QueryPool Class (Parameter Markers Option Turned Off) // public java.util.Vector findByKeyQuery(java.util.Vector args, com.ibm.vap.Persistence.BOInjector anInjector) { // Declare the Query Specification Source DatabaseSelectQuerySpec aSpec; // Declare and Define the SQL Query String SqlQueryString aQueryString = new SqlQueryString(findByKeySqlString()); // 1) Define the Select Query Specification Source aSpec = new DatabaseSelectQuerySpec(aQueryString.evaluate (anInjector.convertAll(aVector))); aSpec.cacheStatement(false); // Declare Result Variable Vector aSpecArray = new Vector(); // Add the Query Specification Source aSpecArray.addElement(aSpec); // Return the Query Specification return aSpecArray; } Figure 151. Query Method without Parm-Markers: findByKeyQuery Pessimistic Locking When pessimistic locking is defined for class, then the resources of the business object(s), that is the corresponding database row(s), are locked for exclusive use by one transaction. A query for a business object with pessimistic locking enabled will lock the database table while the query is being executed by performing an update query on the table, and then execute the custom query. If you are defining a custom query for a business object that has pessimistic locking enabled, then you must also define two methods in the business object’s query pool class in addition to the SQL string method and query method: an SQL string for locking method and a query for locking method. The name of the query for locking method must stick to the following naming scheme: Query Method Name + ForLocking. The implementation of these two methods is in analogy to the normal SQL string and query methods and has the same variations, depending on whether the parameter markers option is turned on or off. As the locking 230 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs query does not return any data a DatabaseQuerySpec instance will be used instead of a DatabaseSelectQuerySpec instance, and of course no output shape needs to be defined. Example of a Fixed Custom Query Suppose we wish to query the BankAccount home for all accounts that have a balance greater than 10000.00. These accounts are gold accounts, owned by the bank’s valued customers, and we would like to retrieve this information for use in an application. Coding this query using the methods provided by the home collection class and Vector manipulation would be cumbersome. A convenient approach is to create a custom query called goldAccounts. For our sample we use the Simple model and SimpleSimple map, therefore our query pool class is called SimpleSimpleBankAccountQueryPool. The sample makes use of parameter markers. To define any custom query we implement these methods: ❑ An SQL String Method ❑ A Query Method ❑ A Retrieval Method The retrieval method is defined in the respective home implementation class (BankAccountHomeImpl in our sample). If you use pessimistic locking, then another two methods must be implemented: ❑ An SQL String for Locking Method ❑ A Query for Locking Method We explore an implementation of a query with our goldAccounts example. For this example we use the pessimistic locking implementation in the itso.vap.simple.lockingservices package. We then copied the queries also to the itso.vap.simple.services and itso.vap.simple.opservices packages. Implement the SQL String Method In our example we need all bank accounts with a balance greater than 10000. This is a simple, static query. The SQL string method is shown in Figure 152. Note that the sequence of columns must be the same as in the allInstances query. Chapter 12. Custom Queries 231 // // Find all Gold Accounts - SQL String Method // // A Gold Account is an account with a balance greater than 10000.00 // public java.lang.String goldAccountsSqlString() { return "SELECT T1.balance, T1,accountId, T1.cust_customerId FROM ITSO.ACCOUNT00 T1 WHERE balance > 10000.00"; } Figure 152. Sql String Method: goldAccountsSqlString Implement the Query Method In our example, the query is a simple fixed query, and does not contain any variable arguments (Figure 153). // // Find all Gold Accounts - Query Method // // A Gold Account is an account with a balance greater than 10000.00 // public java.util.Vector goldAccountsQuery() { Vector aSpecArray = new Vector(); DatabaseCompoundType aCompoundType; DatabaseQuerySpec spec = new DatabaseSelectQuerySpec(goldAccountsSqlString()); aCompoundType = new DatabaseCompoundType(); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base.DatabaseDecimalField("balance")).setAttributes(8,2,3,false)); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base.DatabaseStringField("accountId")).setAttributes(8,0,1,false)); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base.DatabaseStringField("Cust_customerId")). setAttributes(4,0,1,false)); ((DatabaseSelectQuerySpec)spec).setOutputShape(aCompoundType); aSpecArray.addElement(spec); return aSpecArray; } Figure 153. Query Method: goldAccountsQuery We implemented this by copying the allInstancesQuery method and changing the argument for the DatabaseSelectQuerySpec constructor call. 232 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Implement a Retrieval Method in the Home Class Each HomeImpl class inherits—among others—the two methods from its parent called customQuery(String) and customQuery(String, Object[]) which can be used to invoke custom queries, depending on whether you have to pass arguments to the query or not. You could actually invoke your custom query by calling directly one of these methods, but it is considered good style to add a dedicated method to the respective home class which then calls the customQuery(String) method. Moreover the use of the custom query in conjunction with the Persistence Builder default model beans is somewhat easier if this additional retrieval method is defined. We add the retrieveGoldAccounts method to the BankAccountHomeImpl class (Figure 154), and the retrieveGoldAccounts method declaration to the BankAccountHome interface. // // Find all Gold Accounts - Retrieval Method // // A Gold Account is an account with a balance greater than 10000.00 // public java.util.Vector retrieveGoldAccounts() throws java.rmi.RemoteException, javax.ejb.FinderException, java.lang.Throwable { return this.customQuery("goldAccountsQuery"); } Figure 154. Retrieval Method: retrieveGoldAccounts Implement Methods for Pessimistic Locking Note that these methods must be implemented only if you want to use pessimistic locking in the BankAccount table. Implement the SQL String for Locking Method The SQL statement to lock the rows of the BankAccount table is a simple update on the primary key (Figure 155). Chapter 12. Custom Queries 233 // // Find all Gold Accounts - SQL For Lock String Method // // A Gold Account is an account with a balance greater than 10000.00 // public java.lang.String goldAccountsSqlStringForLocking() { return "UPDATE ITSO.ACCOUNT00 SET accountId = accountId WHERE balance > 10000.00"; } Figure 155. SQL String Method for Locking: goldAccountsSqlStringForLocking Implement the Query for Locking Method The query for locking method is defined in Figure 156. // // Find all Gold Accounts - For Locking Query Method // // A Gold Account is an account with a balance greater than 10000.00 // public java.util.Vector goldAccountsQueryForLocking() { // Declare Result Value Vector aSpecArray = new Vector(); // Define the Query Specification DatabaseQuerySpec spec = new DatabaseQuerySpec(goldAccountsSqlStringForLocking()); spec.cacheStatement(false); // Return the Query Specification aSpecArray.addElement(spec); return aSpecArray; } Figure 156. Query Method for Locking: goldAccountsQueryForLocking There are two glaring differences between this query for locking method and the original regular query method: ❑ The DatabaseQuerySpec is defined as a DatabaseQuerySpec, as opposed to a DatabaseSelectQuerySpec. This is because our query does not return data; it is, in fact, an UPDATE statement. ❑ The output shape is not defined here, because an UPDATE statement does not yield any output. If we were using a variable query, the input shape, and input arguments would be defined as usual. 234 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Test the Fixed Custom Query In our test sample (Figure 157), we will demonstrate the code used to call a custom query on a home collection. As always, we begin by activating the datastore, begin a transaction, then commit the transaction when complete. To invoke the query, we use the retrieval method on a home collection. The result of our query is a Vector that we have simply displayed to the console. // Activate the Datastore itso.vap.simple.lockingservices.SimpleSimpleDataStore.singleton().activate(); // Begin a Transaction com.ibm.vap.Transactions.Transaction.begin(); // Perform the Custom Query java.util.Enumeration enum = itso.vap.simple.domain.BankAccountHomeImpl.singleton(). retrieveGoldAccounts().elements(); System.out.println("Accounts with more than 10000"); itso.vap.simple.domain.BankAccount a; while (enum.hasMoreElements()) { a = (itso.vap.simple.domain.BankAccount)enum.nextElement(); System.out.println(a.getAccountId() + ": " + a.getBalance()); } // Commit the Transaction com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 157. Scrapbook: Custom Query Gold Accounts (GoldAccounts.scrap) Example of a Variable Custom Query The second sample shows a more advanced custom query with two input parameters and a join in the query’s where clause. We make use of parameter markers. We want to add a query to the SimpleSimpleCustomerQueryPool class. The query should return all Customers who own at least one BankAccount with a balance greater than a given value and whose last name is LIKE a given substring. The query name is allByLastNameAndAccountBalance. Implement the SQL String Method In this example we need two input variables and because we have the parameter markers option turned on we have two question marks (?) as place holders in the SQL string method (Figure 158). Chapter 12. Custom Queries 235 public java.lang.String allByLastNameAndAccountBalanceSqlString() { return "SELECT DISTINCT T1.lastName, T1.firstName, T1.customerId, T1.title" + " FROM ITSO.CUSTOMER00 T1, ITSO.ACCOUNT00 T2" + " WHERE T1.lastName LIKE ?" + " AND T1.customerId = T2.Cust_customerId" + " AND T2.balance > ?"; } Figure 158. Sql String Method: allByLastNameAndAccountBalanceSqlString Implement the Query Method We used the findByKeyQuery(Vector, BOInjector) method of the SimpleSimpleCustomerQueryPool class as template. We only changed the call of the SQL string method from findByKeySqlString to allByLastNameAndAccountBalanceSqlString, and we changed the input shape (Figure 159). public java.util.Vector allByLastNameAndAccountBalanceQuery(java.util.Vector args, com.ibm.vap.Persistence.BOInjector anInjector) { Vector aSpecArray = new Vector(); DatabaseCompoundType aCompoundType; DatabaseQuerySpec spec = new DatabaseSelectQuerySpec(allByLastNameAndAccountBalanceSqlString()); Vector stringArgs; aCompoundType = new DatabaseCompoundType(); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base. DatabaseStringField("CUSTOMER00.lastName")).setAttributes(30,0,12,false)); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base. DatabaseDecimalField("ACCOUNT00.balance")).setAttributes(8,2,3,false)); stringArgs = new Vector(); stringArgs.addElement("CUSTOMER00.lastName"); stringArgs.addElement("ACCOUNT00.balance"); spec.setInputShape(aCompoundType); aCompoundType = new DatabaseCompoundType(); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base. DatabaseStringField("lastName")).setAttributes(30,0,12, false)); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base. DatabaseStringField("firstName")).setAttributes(30,0,12,false)); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base. DatabaseStringField("customerId")).setAttributes(4,0,1,false)); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base. DatabaseStringField("title")).setAttributes(3,0,1,false)); ((DatabaseSelectQuerySpec)spec).setOutputShape(aCompoundType); spec.setInputValues(args); spec.setInputNamesWithDuplicates(stringArgs); aSpecArray.addElement(spec); return aSpecArray; } Figure 159. Query Method: allByLastNameAndAccountBalanceQuery 236 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs If you do not know exactly how to define the input shape field for the account balance parameter, then just copy the corresponding line from the allInstances method of the SimpleSimpleBankAccountQueryPool class. Implement a Retrieval Method in the Home Class By adding a dedicated method to the CustomerHomeImpl class we provide the application developer with an easier interface than the one provided by the customQuery(String, Object[]) method (Figure 160). Do not forget to add the method declaration to the CustomerHome interface. /** * Lookup all instances by lastName and balance * @return a Vector of found Customer objects whose lastName is like a certain * substring and who own at least one bankaccount whose balance is greater than a * certain amount. * @param lastName java.lang.String the customers lastName must be like this * substring * @param balance java.math.BigDecimal the balance of at least one account must be * greater than this value */ public java.util.Vector retrieveAllByLastNameAndAccountBalance (java.lang.String lastName, java.math.BigDecimal balance) throws java.rmi.RemoteException, javax.ejb.FinderException, java.lang.Throwable { String qryName = "allByLastNameAndAccountBalanceQuery"; Object[] qryArgs = { lastName, balance }; return this.customQuery(qryName, qryArgs); } Figure 160. RetrievalMethod: retrieveAllByLastNameAndAccountBalance Methods for Pessimistic Locking Note that these method must be implemented only if you want to use pessimistic locking. Implement the SQL String for Locking Method The SQL statement to lock the rows of the BankAccount table is a simple update on the primary key (Figure 161). Chapter 12. Custom Queries 237 public java.lang.String allByLastNameAndAccountBalanceSqlStringForLocking() { return "UPDATE ITSO.CUSTOMER00 SET customerId = customerId" + " WHERE customerId IN" + " (SELECT T1.customerId FROM ITSO.CUSTOMER00 T1, ITSO.ACCOUNT00 T2" + " WHERE T1.lastName LIKE ?" + " AND T1.customerId = T2.Cust_customerId" + " AND T2.balance > ?)"; } Figure 161. Locking: allByLastNameAndAccountBalanceSqlStringForLocking Implement the Query for Locking Method We used the allByLastNameAndBalanceQuery method as template and made these changes: ❑ We create a DatabaseQuerySpec and pass the SQL string for locking as parameter, instead of a DatabaseSelectQuerySpec with the normal SQL string. ❑ We removed the section that dealt with the output shape. The query for locking method is shown in Figure 162. public java.util.Vector allByLastNameAndAccountBalanceQueryForLocking (java.util.Vector args, com.ibm.vap.Persistence.BOInjector anInjector) { Vector aSpecArray = new Vector(); DatabaseCompoundType aCompoundType; DatabaseQuerySpec spec = new DatabaseQuerySpec(allByLastNameAndAccountBalanceSqlStringForLocking()); Vector stringArgs; aCompoundType = new DatabaseCompoundType(); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base. DatabaseStringField("CUSTOMER00.lastName")).setAttributes(30,0,12,false)); aCompoundType.addField((DatabaseTypeField)(new com.ibm.ivj.db.base. DatabaseDecimalField("ACCOUNT00.balance")).setAttributes(8,2,3,false)); stringArgs = new Vector(); stringArgs.addElement("CUSTOMER00.lastName"); stringArgs.addElement("ACCOUNT00.balance"); spec.setInputShape(aCompoundType); spec.setInputValues(args); spec.setInputNamesWithDuplicates(stringArgs); aSpecArray.addElement(spec); return aSpecArray; } Figure 162. Locking: allByLastNameAndAccountBalanceQueryForLocking 238 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Test the Variable Custom Query Figure 163 demonstrates how to use the custom query. itso.vap.simple.lockingservices.SimpleSimpleDataStore.singleton().activate(); com.ibm.vap.Transactions.Transaction.beginReadOnly(); itso.vap.simple.domain.Customer c; itso.vap.simple.domain.CustomerHomeImpl home = itso.vap.simple.domain.CustomerHomeImpl.singleton(); java.util.Enumeration enumCustomers; // Call the Custom Query enumCustomers = home.retrieveAllByLastNameAndAccountBalance ("P%", new java.math.BigDecimal("200")).elements(); while(enumCustomers.hasMoreElements()) { c = (itso.vap.simple.domain.Customer)enumCustomers.nextElement(); System.out.println(c.getTitle() + " " + c.getFirstName() + " " + c.getLastName()); } com.ibm.vap.Transactions.Transaction.getCurrent().commit(); Figure 163. Scrapbook: Custom Query (RetrieveAllByNameBalance .scrap) Use Custom Queries in Visual Composition The visual model beans (VapDefaultTableModel, VapDefaultListModel, and AWTListModel) provide properties to use custom queries. We can query the home that a model bean is connected to at any time by setting the queryName property to the name of the retrieval method defined in the home collection, and the queryArguments property to an object array of arguments that are passed to the retrieval method. GUI Example with Custom Query We developed a GUI with a JTable and a connected VapDefaultTableMOdel bean that invokes the retrieveAllByLastNameAndAccountBalance method of the CustomerHomeImpl class that we developed in “Example of a Variable Custom Query” on page 235 (Figure 164). Chapter 12. Custom Queries 239 1 Figure 164. GUI that Invokes a Custom Query (CustomQueryView) The GUI is basically built in the way that we introduced in “Customer List Using a JTable and VapDefaultTableModel” on page 100. We added—besides the additional GUI components—two things: ❑ In the Property Editor of the VapDefaultTableModel bean we set the queryName property to retrieveAllByLastNameAndAccountBalance. ❑ The actionPerformed event of the Search button triggers a new search method (1). In this method the two query parameters are read from the text fields and integrated into an array of objects. This array of objects is then passed as argument to the setQueryArguments method of the VapDefaultTableModel bean. The invocation of the setQueryArguments method automatically triggers the execution of the query. By the way, also the setHome and setQueryName methods of the VapDefaultTableModel class would trigger the query. public void search() { String lastName = getTFLastName().getText(); java.math.BigDecimal balance = new java.math.BigDecimal(getTFBalance().getText()); Object[] argArray = { lastName, balance }; getVapDefaultTableModel().setQueryArguments(argArray); return; } ❑ Do not forget to add the datastore activation to the initialize method: itso.vap.simple.lockingservices.SimpleSimpleDataStore.singleton().activate(); ❑ As usual, do not forget to specify the required projects in the class path. 240 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Run the GUI Example We assume that data is loaded into the CUSTOMER00 and ACCOUNT00 tables. You can use the customdata.sql file to load additional sample data. Figure 165 shows the result of a query. Figure 165. GUI Run with Custom Query Note that you have to enter name values in a format accepted by an SQL where clause. Custom Query Enhancements in Version 3 VisualAge for Java Version 3.0 provides an enhancement to the custom query facility. You can now write customer queries that do not return business objects; instead a service object that contains a vector of database row objects is returned. You can then retrieve the column values from the database row objects. The custom query framework also enables you to invoke stored procedures. See “Custom Query Enhancements” on page 257 for more information. Chapter 12. Custom Queries 241 242 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 13 Performance Considerations In this chapter we investigate two of the performance aspects of the Persistence Builder, lite collections and preload paths. There are many aspects of performance with the Persistence Builder. The scope of this redbook does not cover most of these aspects; we simply did not have the time to investigate performance in any detail. Here are a few performance aspects that we did not investigate: ❑ Stand-alone applications (local database) or network applications (remote database) Legacy database or new database designed for an object model Whether other applications are accessing the same database (locking) Concurrency (few or many users competing for the data) Database design (single or multicolumn keys, few tables or many normalized tables) ❑ Read paths (multitable joins) and write paths (single table) ❑ Tailored SQL statements ❑ ❑ ❑ ❑ © Copyright IBM Corp. 2000 243 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 allows 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 retrieve more detailed information 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. In this section we present an example where we define a lite collection to improve data retrieval time. We extend the Customer class to contain images of signatures. Without a lite collection, retrieving customer data requires retrieving the signature images as well. With a lite collection we retrieve a list of customers, and the signature information only if it is requested. Define the Model, Schema, and Mapping We will use the itso.lite.metadata package for definitions, itso.lite.domain for the model classes, and itso.lite.services for the service classes. Define the Model Create a new model in the Model Browser with the name Lite. Our model consists of only one class: Customer. If you wish, you may enhance the definition of the Customer class from a previous example, instead of defining a brand new model. The attributes for our Customer class appear in Table 18. Table 18. Attributes of Customer Class Name Attribute Name Attribute Type Required Customer customerId String Yes (Object ID) title String No firstName String No lastName String No signature byte[] No It is interesting to note that the signature attribute is a byte[] data type. This is because we will store our images in the database as BLOBs. When the images are retrieved from the database they are converted into an array of bytes. You will see in our example how to display the signatures in an applet. 244 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Define the Lite Collection Lite collections are defined on the Lite Collections tab of the Class Editor dialog, in the Model Browser (Figure 166). Figure 166. Customer Lite Collection For our lite collection we wish to retrieve all attributes except the signature attribute. To do this we create a new Lite collection with the New button, and assign it a name. For this example, we used the customerIdentity name to indicate that we are only retrieving information that describes the identity of a customer, such as their name and customer Id. To identify the properties that will be retrieved in the lite collection, select each property (except signature) from the Class properties list. Confirm the selection with the Apply button. The lite collection definition is complete. There are two features we chose not to use: ❑ Filter property. Selecting a property from this list allows us to further refine our lite collection to only a specific subset of records. For example, we could select lastName as our filter property and have the lite collection only return properties of customers whose last names begin with S. Chapter 13. Performance Considerations 245 Defining the lite collection will provide us with a getCustomerIdentityLiteCollection method in the CustomerHome that enables us to retrieve our subset of data. This will be demonstrated in our applet sample. Generate the Schema and Datastore Mapping Once the Customer class is defined, save the object model, and generate the schema from the model (Models -> Generate schema from model). Change the physical table name to CUSTOMERLITE with qualifier ITSO. Modify the Signature Data Type in the Schema By generating the schema from our object model, the signature column will be given the default data type of BLOB with a size of 30 bytes. Our signature images, however will be approximately 50000 bytes, so we must change the size in the column editor (Figure 167). Figure 167. Column Editor: Signature BLOB 246 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Export the Schema to the Database Once the schema is properly defined, save it and export it to the database (Schemas -> Import/Export Schemas -> Export Entire Schema to Database...), configuring the parameters to export it to a database of your choice, for example, VAPSAMPL. Generate the Domain and Service classes The model definition is now complete. Save the datastore mapping, and generate both the domain classes and service classes into appropriate packages in your project. Examine the Generated Code Once the code has been generated, have a look at the CustomerHome interface and implementation. A getCustomerIdentityLiteCollection method has been generated. This method returns a VapLiteCollection, which is a subclass of Vector, containing data objects (LiteLiteCustomerDataObject). Guidelines for Lite Collections Here are a few guidelines on how to work with lite collections: ❑ The lite collections methods return a Vector of data objects. The data object class is in the services package. Therefore, you are not dealing with model objects. ❑ You can access the attributes of the data objects using the same methods as in the model objects, for example, getCustomerId. ❑ Do not change any attributes in the data objects; the setter methods are for internal use only. ❑ To update data, retrieve the model (implementation) object. You can invoke the find method on the home using the key attribute from the data object: <home>.find( <dataobject>.getCustomerId() ); ❑ You can use the lite collection vector to display data objects in a list or table, for example, using the VapDefaultTableModel. ❑ A lite collection accesses a snapshot of the data. You will not see any updates performed on the model objects, or committed by child transactions. Chapter 13. Performance Considerations 247 Applet to Test the Lite Collection To illustrate the workings of Lite Collections, we build a user interface with a table (JTable), and an image box (actually a JLabel). When an entry in the table is selected and the show button (>>) is clicked, the customer’s signature is displayed in the image box. Applet Design Figure 168 shows the layout of the applet. A text label (on the right side) is used to display the image; it is inside a scroll pane. 1 3 2 8 4 5 6 7 Figure 168. Lite Collection Applet Design The basic design ideas for this applet are: ❑ The view has a JTable for customer data on the left (1), and a JLabel to display the image on the right (2). ❑ The JTable is connected to a VapDefaultTableModel (3), which in turn is connected to a transaction and the customer home. This setup executes the allInstances query by default and fills the table. ❑ The table model (3) must be tailored with the class of the objects (itso.lite.services.LiteLiteCustomerDataObject) and the columns that will be displayed (customerId, title, firstname, lastname) as explained in “Customer List Using a JTable and VapDefaultTableModel” on page 100. 248 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs You also change the name of the query that is executed; instead of allInstances, you want to run getCustomerIdentityLiteCollection. ❑ Tear-off the selectedObject attribute from the table model, and change its type to LiteLiteCustomerDataObject and rename it appropriately ( 4). ❑ Connect the actionPerformed event of the show button (>>) to the find method of the home to retrieve the full customer object. Pass the customerId property of the data object as parameter (5). ❑ Drop a variable of type CustomerImpl and connect the normalResult of the find method to the this of the variable ( 6). ❑ Drop a factory of type ImageIcon and connect the this event of the CustomerImpl variable to the ImageIcon(byte[]) constructor and pass the signature attribute as parameter (7). This is the image we want to display. ❑ To display the icon, connect the this event of the ImageIcon to the setIcon method of the JLabel and pass the this as parameter (8). ❑ After generating the code, activate the data store in the initConnections method: itso.lite.services.LiteLiteDataStore.singleton().activate(); Save the bean and make sure the class path is set correctly. Applet Run Figure 169 shows a capture of the running applet. Figure 169. Lite Collection Applet Run Chapter 13. Performance Considerations 249 Load the Signatures into the Table We provide a command stream (litedata.sql) to load a few customers with IDs 201 to 206 into the ITSO.CUSTOMERLITE table. To load the signatures, we provide the GIF files (20x.gif) and a Java program to load them. Run this program from the \sampcode\lite subdirectory using the command: d:\xxxxx\sampcode\lite> java itso.lite.gui.LoadSignature 250 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Preload Paths You can define the depth to which data for an object should be retrieved from the database in a single query by specifying a preload path. Let us again look at the Customer–BankAccount example, and assume that most of the time when a user is interested in a Customer object, the user is also interested in that Customer’s owned BankAccounts. In this case we can define within the Customer class the ownedAccounts association as preload path, so that the Customer and its linked BankAccount data are retrieved in one single query. To define the preload path open the Map Browser and select the Customer persistent class in the SimpleSimple datastore map. From its pop-up menu select Change default pre-load path and enter the ownedAccounts association name in the Information Required dialog (Figure 170). Multiple entries should be separated by a space character. Figure 170. Pre-Load Path You can easily verify the changed data retrieval strategy by turning the trace option on (in the Status Browser) and watching the executed database statements before and after regeneration of the service classes. The retrieval of one specific Customer object will cause a database access in both cases. Without this preload path another access to the database can be observed when the ownedAccounts method of a Customer object is invoked. This is not the case when the preload path is specified. The following restrictions apply: ❑ When preloading trees of objects, pessimistic locking is supported only for the root object of the tree. ❑ The preload path cannot be turned on and off at runtime. Note: The preload path can be used effectively for many-to-many associations to follow the relationships from a business object through the intermediate class to the related target business objects. Chapter 13. Performance Considerations 251 252 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 14 Persistence Builder Enhancements in Version 3 In this chapter we describe the changes and new function of the Persistence Builder in VisualAge for Java Version 3.0. To make an existing model work in VisualAge for Java Version 3.0 it is necessary to regenerate the domain (business) and service classes. © Copyright IBM Corp. 2000 253 Persistence Builder Changes In this section we list the small changes and enhancements to the Persistence Builder. Some of these enhancements were introduced with the Enterprise Update to Version 2.0, others are new in Version 3.0. Model, Schema, and Map Browser The required setting of an attribute in the model is used to generate a not null column when the schema is generated from the model, and a not null column becomes a required attribute when the model is generated from the schema. Columns with CHAR data type specify the VapTrimStringConverter by default. The sequence of columns in the generated database schema may be different than in the previous Version. Most model elements (class, attribute, table, column) may now be renamed, with the exception of associations, which cannot be renamed. Attributes of superclasses cannot be mapped to columns in a subclass. The check box for the root of the inheritance structure is set automatically. The map name that is generated may be different in Version 3, for example, for a model or schema named ItsoBank, the generated map name is ItsoBankItsoBank, and not ItsoBankItsobank. This changes the names of the generated service classes. The model, schema, and map are now validated in detail so that invalid constructs cannot be created. Generated Code Custom query methods in the home class must throw java.lang.Throwable in addition to java.rmi.RemoteException and javax.ejb.FinderException. Existing queries will be flagged with an error. Example: public java.util.Vector retrieveGoldAccounts() throws java.rmi.RemoteException, javax.ejb.FinderException, java.lang.Throwable { return this.customQuery("goldAccountsQuery"); } 254 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Persistence Builder New Function In this section we list the new function in Version 3.0 of the Persistence Builder. Refresh from Database The implementation object provides now a refresh method to retrieve the current column values from the database table and update the attribute values. Version 3.0: ((AccountImpl)account).refresh(); Version 2.0: Transaction curTx = Transaction.getCurrent(); ((AccountImpl)account).getBom().getVersionForRead().refresh( curTx.getSessionOn( dataStore ) ); Commit Failure When an SQL update or delete statement fails in the commit processing of a transaction, no exception is thrown by default. Version 3.0 provides a public method to force an exception if the affected row count is zero: com.ibm.vap.RelationalPersistence.SQLQuery.setThrowNoRowFoundException(true); This method should be used, for example, when optimistic predicates have been defined in the map browser. An update may fail because of the optimistic predicate (balance changed in an account). WebSphere Connection Pools In VisualAge for Java Version 3.0, the Persistence Builder introduces the ability to tap into the WebSphere connection manager. Connections used by Persistence Builder's service layer are created from an existing WebSphere connection pool. Connection information is gathered as usual through the Database Connection Info window, but the appropriate pool is used in allocating these connections. Service Generation for Connection Pool Figure 171 shows the generation dialog for a relational database schema. Chapter 14. Persistence Builder Enhancements in Version 3 255 Figure 171. Service Class Generation for WebSphere Connection Pool Select the Use database connections from WebSphere connection pool radio button and click on the Change button to provide the database connection information. Verify the generated methods in the data store class. The getConnectionSpec method contains the JDBC driver, database, user ID and password values. The singleton method contains the connection policy code: public static com.ibm.vap.Persistence.DataStore singleton() { if (singleton == null) { singleton = new XxxxXxxxDataStore(); singleton.setConnectionPolicy(new WebSphereConnectionPolicy()); } return singleton; } This is the only change in the generated code. 256 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Testing the Connection Pool in VisualAge for Java When running GUI applications with the connection pool specification the class path specification is incomplete. Open the class path (Run -> Check Class Path) and edit the project path by adding the IBM WebSphere Test Environment project. If this project is omitted you get an exception that the JDBC driver class is not found, but actually the WebsphereJdbcConnPoolAccess class is not found. For a simple test regenerate the Simple model service classes with the Use database connections from WebSphere connection pool specification into the itso.vap.simple.services package. Edit the class path of the CustomerAWTListView class (in the itso.vap.simple.gui package) to include the IBM WebSphere Test Environment project and verify that the initialize method points to the correct data store. Run the application, it should work! Database Connection with User ID and Password On the Generation Options page of the generation wizard for a Relational schema (Figure 171 on page 256), you can now specify Supply login id and password at runtime for database connections. If this option is selected, the program must pass the user ID and password as arguments to the activate method of the data store. itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(userId,password); Custom Query Enhancements In VisualAge for Java Version 3.0, custom queries have been enhanced to allow for queries that do not return business objects and for queries that use stored procedures. Non-Business Object Queries These types of queries work the same as ordinary custom queries except that they return a ServiceResult containing a vector of DatabaseRow objects and not a Vector of business objects. Methods of the Home Class The non-business object query methods in the home class would call the customNonBOResult(String) method of the PersistentHomeCollection class instead of the customQuery(String) method. For SQL queries with arguments they would call the customNonBOResultQuery(String,Object[]) method. Chapter 14. Persistence Builder Enhancements in Version 3 257 Methods of the QueryPool Class With these types of queries, only the retrieval method in the Home class and an SQL string method in the QueryPool class are required. It is not necessary to define a query method on the QueryPool class, unless you pass arguments and use parameter marker bindings. The name of the SQL string method is the string passed to the customNonBOResultQuery method, with a suffix of SqlString. For example, if the string is countOfStudentsOver21, then an SQL string method in the QueryPool class named countOfStudentsOver21SqlString must exist. Results of a Non-Business Object Custom Query The return object from customNonBOResultQuery(String) is a com.ibm.vap.Persistence.ServiceResult. A ServiceResult object contains a vector of com.ibm.ivj.db.base.DatabaseRow objects that is retrieved by sending the results() method to the ServiceResult object. One DatabaseRow object is returned for each resulting row in the query. The column data from a row object is retrieved based on indexes, for example, aRow.getAtIndex(1) would be the first element in a row object). The order of the elements in a DatabaseRow object depends on the order of the columns within the SQL string method. The application designer decides what to do with the column data that is returned. The important difference to standard custom queries is that no business object is created. Queries Using Stored Procedures There are three ways to call a stored procedure from a Persistence Builder application: ❑ Implement a stored procedure that performs the same function as a generated query. The generated service code must be modifed by replacing the DatabaseQuerySpec with a DatabaseCallableQuerySpec object. The result must be identical to the generated query. ❑ Implement the custom query method (in the query pool class) that uses a DatabaseCallableQuerySpec to invoke a stored procedure. The result is a vector of business objects. ❑ Implement a custom method in the home class that calls the customStoredProcedure method. It returns a ServiceResult object as described above for non-business object custom queries. For more information go to the VisualAge Developer Domain Web site at www.software.ibm.com/vadd and follow the links to Library, Technical Articles. 258 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Multithreading Support In this section we discuss the multithreading support of the Persistence Builder. Transactional Thread VisualAge for Java, Version 2 introduced some support for multithreading. The mechanism was the transactional thread. A transactional thread is a special thread that holds a transaction context that identifies the current transaction for that thread. When the Persistence Builder transaction framework went to find the current transaction, it would first check to see if the transaction were running on a transactional thread. If so, we would get the current transaction from its context. What about Servlets? This solution works for some types of applications, but it is not ideal for many server environments. Manipulating threads is often a key performance feature of application servers. For that reason, using a transactional thread is not always possible or optimal. Following the VisualAge for Java Enterprise Update, it was apparent that we needed a more flexible solution for managing the current transaction in a multithreading environment. As we looked at the possible application architectures for Web applications, it was also clear that no single solution could address them all. In particular, if a Web application wanted to keep Persistence Builder transactions and objects live to service a series of client interactions, we had no generic mechanism to identify a unit of work. On the other hand, as long as the Web application did not need long-running transactions, there turned out to be a straightforward solution. In the short-running transaction model, every servlet request that interacts with persistent objects must begin its own transaction at the top of its request handler, and it must commit or roll back its transaction before returning. Given this model, we end up with a model that is very similar to the transactional-thread model. That is, at any one time, there is a single current transaction attached to a thread that is executing a servlet request. Because we do not control the thread creation in the server, we need to have an external mechanism to record this binding. A look-up table was introduced that mapped a thread instance to a transaction instance. Whenever the current transaction was requested, the current thread looked it up in that table. Whenever the servlet code created or resumed a transaction, the look-up table was maintained accordingly. Chapter 14. Persistence Builder Enhancements in Version 3 259 Transaction Binding Policies In order to enable other flavors of transaction-to-thread binding, the current transaction mechanism was redesigned in VisualAge for Java Version 3.0 to delegate the get- and set-current behavior through a transaction binding policy. Transaction binding policies have to implement three methods: ❑ public Transaction getCurrentTransaction(), which returns the transaction bound to the current thread ❑ public void setCurrentTransaction(Transaction aTransaction), which binds a transaction to the current thread ❑ public void terminateTransaction(Transaction aTransaction), which disconnects a terminated transaction from a thread If the transactions’ object references have to be converted to handles (to be stored in an external table, for example), there are two helper methods in the Transaction class for converting and resolving the handles (you may need to do additional conversion depending on the system): ❑ int getId(), an instance method for converting a transaction's object reference to a numeric ID ❑ static Transaction getTransaction(int anId), a static method for converting IDs to object references Sending setBindingPolicy() to the Transaction class sets a system-wide binding policy, as follows: Transaction.setBindingPolicy(TransactionBindingPolicy aPolicy) Two pre-defined policies are: ❑ TransactionToThreadBindingPolicy, which uses the look-up table approach. ❑ TransactionToContextThreadBindingPolicy, which uses the instance-variable approach. Each thread has to implement a ContextThread-interface for accessing the context that holds the current transaction. For backwards compatibility, this is the default policy. 260 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Setting the Transaction Binding Policy for Servlets In the WebSphere environment, the look-up table approach is preferable: Transaction.setBindingPolicy(new TransactionToThreadBindingPolicy()); If you are building a Persistence Builder Web application, you will want to add that line of code to the initialization code for your servlet. It only needs to be run once for each instance of the Persistence Builder runtime. We used the TransactionToThreadBindingPolicy in our ITSO Bank servlet and JSP applications. See “Initialization” on page 353 (in “Implement the Controller Servlet” ) for a servlet example and “Implement the JSP Controller Servlet” on page 399 for a JSP/servlet example. Example of Transactional Thread Here is a sample program that uses the transactional thread class to run three threads that access the simple model (CUSTOMER00 table in VAPSAMPL) in parallel (Figure 172). This short description of the program explains the main code sequences: ❑ The CustomerThread class implements the Runnable interface so that it can run as multiple threads. ❑ The main method activates the data store and sets the transaction binding policy to either the TransactionToContextThreadBindingPolicy or to the TransactionToThreadBindingPolicy. Then three threads are started with three customer numbers. Starting the thread invokes the run method of each instance. ❑ The run method starts a top-level transaction, finds the customer, updates the first name, and commits the transaction. Finally the thread is stopped. Sample output shows which transaction is used to perform the operation. Using the debugger you can observe that the three threads run in parallel. You can set breakpoints and decide which thread to continue. Use the Status Tool to display the threads and their objects. Chapter 14. Persistence Builder Enhancements in Version 3 261 package itso.vap.simple.thread; import com.ibm.vap.Transactions.*; import itso.vap.simple.domain.*; public class CustomerThread implements Runnable { static String customerNumber[] = {"101","102","103"}; public CustomerThread() { super(); } /** MAIN **/ public static void main(java.lang.String[] args) { itso.vap.simple.services.SimpleSimpleDataStore.singleton().activate(); // set transaction-thread binding policy Transaction.setBindingPolicy(new TransactionToContextThreadBindingPolicy()); // Transaction.setBindingPolicy(new TransactionToThreadBindingPolicy()); for (int i=0; i<3; i++) { CustomerThread instance = new CustomerThread(); TransactionalThread custThread = new TransactionalThread(instance,customerNumber[i]); custThread.start(); } } /** RUN **/ public void run() { Thread thread = Thread.currentThread(); String threadName = thread.getName(); System.out.println(thread.toString()); try { Transaction tx = Transaction.begin(threadName); System.out.println("Tx-new "+tx); Customer cust = CustomerHomeImpl.singleton().find(threadName); System.out.println("Thread "+threadName+" Cust: "+cust.getLastName()); System.out.println("Tx-cur "+Transaction.getCurrent()); cust.setFirstName( cust.getFirstName()+"X" ); String custFirst = cust.getFirstName(); Transaction.getCurrent().commit(); System.out.println("Thread "+threadName+" ending, firstName "+custFirst); thread.stop(); } catch (Exception e) { System.out.println("Exception "+e); } } } Figure 172. Transactional Thread Example You can run this program with either of the two transaction binding policies. When you run the program multiple times you can observe that the sequence of the updates is random. Figure 173 shows the output of one run. 262 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Thread[101,5,main] Thread[102,5,main] Thread[103,5,main] Tx-new (1)101:class com.ibm.vap.Transactions.TopLevelTransaction Tx-new (1)102:class com.ibm.vap.Transactions.TopLevelTransaction Tx-new (1)103:class com.ibm.vap.Transactions.TopLevelTransaction Thread 102 Cust: Peter Tx-cur (1)102:class com.ibm.vap.Transactions.TopLevelTransaction Thread 101 Cust: Wahli Tx-cur (1)101:class com.ibm.vap.Transactions.TopLevelTransaction Thread 103 Cust: Bardot Tx-cur (1)103:class com.ibm.vap.Transactions.TopLevelTransaction Thread 102 ending, firstName DanielX Thread 101 ending, firstName UeliX Thread 103 ending, firstName BrigitteX Figure 173. Transactional Thread Example Output Persistence Builder Code Problems After testing all the redbook examples with Version 3.0 of the Persistence Builder we identified two problems. Extra Table Beans Fail with Basic Java Types When you open the columnIdentifiers property of one of the special GUI beans (VapDefaultTableModel or VapDefaultRelationshipTableModel) and you select attributes of a model class that are basic Java types (int, float, boolean, ...), the ColumnIdentifiers dialog fails. The dialog works fine with the wrapper classes Integer, Float, or Boolean. This problem has been fixed in VisualAge for Java Version 3.02. Optimistic Predicate in Inheritance Structure Fails The code generated for an optimistic predicate works fine for a single class as demonstrated in “Optimistic Predicate for the Account Balance” on page 138. The code generated for an inheritance structure (Account with subclass Checking- and SavingsAccount) looks different and does not work. A manual fix of the generated code is described in “Test Optimistic Predicate” on page 373. This problem has been fixed in VisualAge for Java Version 3.02. Chapter 14. Persistence Builder Enhancements in Version 3 263 264 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Part 3 ITSO Bank Application In this part we implement a banking application using the Persistence Builder as the interface to a relational database and GUIs, servlets, and JSPs for the application logic and the user interface. © Copyright IBM Corp. 2000 265 266 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 15 ATM Application Requirements and Database In Part 2, we demonstrate the features of the Persistence Builder of VisualAge for Java Enterprise, and construct sample applications to explain the functionality. In this chapter, we describe a more sophisticated application that combines several features. We define the application requirements and the underlying relational database. In the chapters that follow, we implement the business logic together with different user interfaces and data sources. © Copyright IBM Corp. 2000 267 ATM Application Requirements Basically, the application used in this book is similar to that described in the redbooks Application Development with VisualAge for Java Enterprise, SG24-5081, and VisualAge for Java Enterprise Version 2: Data Access Beans Servlets - CICS Connector, SG24-5265. When we wrote this book we had to decide which sample application we would use. We concluded that the best way was to reimplement an existing sample and improve it with the extended functionality of the Persistence Builder of VisualAge for Java Enterprise Version 2. This would give you the opportunity to draw on your experience using the first book and become familiar with new approaches of VisualAge for Java without spending too much time on unimportant things. In fact, the business object model and the controller of the ATM application are almost identical. What we did change are the user interface, database access, and transaction invocation. The ATM application handles two types of accounts, savings and checking. For both accounts, customers can perform debit and credit transactions. In addition, customers can list the transaction history belonging to an account. Customers must maintain a minimum balance in a savings account and cannot withdraw funds beyond a specified overdraft amount from a checking account. Figure 174 shows the basic layout of the panels and the application flow. The application simulates an ATM card reader installed at real ATM machines. To start a bank transaction, the user is prompted to enter the ATM card identification number (card ID). On receiving a valid card ID, the application greets the customer with the customer’s name and title in the PIN panel. The card ID is re-displayed, so that the customer can verify the card. The application prompts the customer for the personal identification number (PIN). The application verifies the PIN. If the PIN is invalid, a message is displayed indicating that the number is invalid, and the customer can reenter the PIN. On successful validation, a list of accounts that belong to the card is displayed in the Account panel. The application requests the customer to select an account for further processing. When the customer selects an account, the Transaction panel showing the customer information (title, name) and the account information 268 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs (account ID, account type, old balance, new balance) is displayed. At the start of a transaction, the new balance is the same as the old balance. The customer can enter an amount and proceed with a debit or a credit transaction. After every successful transaction, the new balance is displayed. If the customer requests to see the account’s transaction history, the application displays the accumulated transactions in a drop-down list on the same panel. Card Panel Please enter card ID: - (valid) Cancel OK Ms Fname Lname, please verify your card and enter your PIN#: PIN Panel Card ID : ####### (valid) Select Account Panel Transaction Panel OK PIN# : - Cancel Select an Account ID for Transaction: ###-#### ###-#### ###-#### ###-#### OK Cancel Ms Fname Lname, please perform your transaction Account ID Account Type : ###-#### : Saving Account Old Balance : $$$$$.$$ New Balance $$$$$.$$ Amount: : $$.$$ Deposit Withdraw History Cancel Transaction History ###### ######### ############### ###### #### ###### ######### ############### ###### #### ###### ######### ############### ###### #### Figure 174. ATM Application Panels and Flow Chapter 15. ATM Application Requirements and Database 269 ITSOBANK Database Implementation We extended the database used in the previous redbooks to add more functionality. This ITSOBANK database is now being used in a series of redbooks on VisualAge for Java and VisualAge Generator. Figure 175 shows the physical database design and the relationships between the tables. BANKID 1 primary key BANK BANKNAME foreign key 1 CUSTOMER CUSTID 1 TITLE m FNAME LNAME USERID PASSWORD BANKID 1 m CARD CARDID PIN CARDTYPE CUSTID 1 m CARDID ACCID CARDACCOUNT m m ACCOUNT 1 ACCID m BANKID CUSTID ACCTYPE BALANCE TRANSTYPE TRANSAMT TRACCID TRANSRECORD POLNAME POLVALUE ACCTYPE POLICY Figure 175. ITSOBANK Tables and Relationships 270 BILLTITLE m m ACCID OVERDRAF m 1 TRANSID MINAMT VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Tables 19 through 25 show the physical design of the ITSOBANK tables. Table 19. Bank Table Column Name Type Length Key Nulls Description BANKID CHAR 4 PK No Bank ID BANKNAME CHAR 30 No No Bank name Table 20. Customer Table Column Name Type Length Key Nulls Description CUSTID CHAR 4 PK No Customer ID TITLE CHAR 3 No No Title FNAME CHAR 30 No No First name LNAME CHAR 30 No No Last name USERID CHAR 8 No Yes User ID PASSWORD CHAR 8 No Yes Password BANKID CHAR 4 FK No Bank ID Table 21. Account Table Column Name Type Length Key Nulls Description ACCID CHAR 8 PK No Account ID BANKID CHAR 4 FK Yes Bank ID CUSTID CHAR 4 FK No Customer ID ACCTYPE CHAR 10 No No Account type (CHECKING, SAVINGS, PAYEE) BALANCE DEC (8, 2) No No Balance MINAMT DEC (8, 2) No Yes Minimum amount OVERDRAF DEC (8, 2) No Yes Overdraft amount BILLTITLE VAR CHAR 32 No Yes Payee title for bill Chapter 15. ATM Application Requirements and Database 271 Table 22. Card Table Column Name Type Length Key Nulls Description CARDID CHAR 7 PK No Card ID PIN CHAR 4 No No PIN CARDTYPE CHAR 4 No No Card type (ATM, VISA, ...) CUSTID CHAR 4 FK No Customer ID Table 23. CardAccount Table Column Name Type Length Key Nulls Description CARDID CHAR 7 PK/FK No Card ID ACCID CHAR 8 PK/FK No Account ID Table 24. Transrecord Table Column Name Type Length Key Nulls Description TRANSID TIMESTAMP 26 Yes No Transaction ID ACCID CHAR 8 No No Account ID TRANSTYPE CHAR 1 No No Transaction type (D = Debit C = Credit T = Transfer to F = Transfer from) TRANSAMT DEC (8, 2) No No Transaction amount TRACCID CHAR 8 No Yes Transfer account Table 25. Policy Table 272 Column Name Type Length Key Nulls Description ACCTYPE CHAR 10 PK No Account type POLNAME CHAR 16 PK No Policy name POLVALUE DECC (8,2) No No Policy value VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Relationships The tables support the following logical relationships: ❑ BANK—CUSTOMER: 1:m A bank has many customers, and a customer belongs to a bank. BANKID is a foreign key in the CUSTOMER table, pointing to the BANK table. ❑ BANK—ACCOUNT: 1:m A bank has many accounts, and an account belongs to a bank. We decided to make this relationship optional, that is, we can have accounts that do not belong to a bank. BANKID is a foreign key in the ACCOUNT table, pointing to the BANK table. ❑ CUSTOMER—CARD: 1:m A customer can have many cards, and a card belongs to one customer. CUSTID is a foreign key in the CARD table, pointing to the CUSTOMER table. ❑ CUSTOMER—ACCOUNT: 1:m A customer can have many accounts and an account belongs to one customer. CUSTID is a foreign key in the ACCOUNT table, pointing to the CUSTOMER table. ❑ CARD—ACCOUNT: m:m A card can access many accounts, and an account can be accessed by many cards. An intermediate table, CARDACCOUNT, holds two foreign keys pointing to the CARD and ACCOUNT tables. ❑ ACCOUNT—TRANSRECORD: 1:m An account has many transaction records, and a transaction record belongs to one account. ACCID is a foreign key in the TRANSRECORD table, pointing to the ACCOUNT table. ❑ ACCOUNT—POLICY: m:m This relationship is not implemented in the database through foreign keys. A policy applies to all accounts that have the same account type as the policy. Database Definition DDL After determining the physical database design, we use command line processor commands and SQL statements to create the database and the objects within it. Figure 176 shows the definitions of the tables, and Figure 177 shows the foreign key relationships and the grant statements. Chapter 15. ATM Application Requirements and Database 273 echo --- create the ITSOBANK database and tables --- ITSOBANK.DDL CREATE DATABASE ITSOBANK CONNECT TO ITSOBANK CREATE TABLE ITSO.BANK ( \ bankid CHAR( 4) NOT NULL, \ bankname CHAR(30) NOT NULL, \ PRIMARY KEY (BANKID) \ ) CREATE TABLE ITSO.CUSTOMER ( \ custid CHAR( 4) NOT NULL, \ title CHAR( 3) NOT NULL, \ fname CHAR(30) NOT NULL, \ lname CHAR(30) NOT NULL, \ userid CHAR( 8), \ password CHAR( 8), \ bankid CHAR( 4) NOT NULL, \ PRIMARY KEY (CUSTID) \ ) CREATE TABLE ITSO.CARD ( \ cardid CHAR( 7) NOT NULL, \ pin CHAR( 4) NOT NULL, \ cardtype CHAR( 4) NOT NULL, \ custid CHAR( 4) NOT NULL, \ PRIMARY KEY (CARDID) \ ) CREATE TABLE ITSO.POLICY ( \ acctype CHAR(10) NOT NULL, \ polname CHAR(16) NOT NULL, \ polvalue DEC(8,2) NOT NULL, \ PRIMARY KEY (ACCTYPE,POLNAME) \ ) CREATE TABLE ITSO.ACCOUNT ( \ accid CHAR( 8) NOT NULL, \ bankid CHAR( 4), \ custid CHAR( 4) NOT NULL, \ acctype CHAR(10) NOT NULL, \ balance DEC(8,2) NOT NULL, \ minamt DEC(8,2), \ overdraf DEC(8,2), \ billtitle VARCHAR(32), \ PRIMARY KEY (ACCID) \ ) CREATE TABLE ITSO.TRANSRECORD ( \ transid TIMESTAMP NOT NULL, \ accid CHAR( 8) NOT NULL, \ transtype CHAR( 1) NOT NULL, \ transamt DEC(8,2) NOT NULL, \ traccid CHAR( 8), \ PRIMARY KEY (TRANSID) \ ) CREATE TABLE ITSO.CARDACCOUNT ( \ cardid CHAR( 7) NOT NULL, \ accid CHAR( 8) NOT NULL, \ PRIMARY KEY (ACCID,CARDID) \ ) Figure 176. ITSOBANK Database Data Definition Language (Part 1) 274 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs CREATE SYNONYM ITSO.TRANS FOR ITSO.TRANSRECORD echo --- referential integrity --ALTER TABLE ITSO.CARD ADD CONSTRAINT "CustomerCard" FOREIGN KEY (CUSTID) REFERENCES ITSO.CUSTOMER ON DELETE RESTRICT \ \ ALTER TABLE ITSO.TRANSRECORD \ ADD CONSTRAINT "AccountTransrecord" FOREIGN KEY (ACCID) \ REFERENCES ITSO.ACCOUNT ON DELETE RESTRICT ALTER TABLE ITSO.ACCOUNT ADD CONSTRAINT "CustomerAccount" FOREIGN KEY (CUSTID) REFERENCES ITSO.CUSTOMER ON DELETE RESTRICT \ \ ALTER TABLE ITSO.CARDACCOUNT ADD CONSTRAINT "AccountCards" FOREIGN KEY (ACCID) REFERENCES ITSO.ACCOUNT ON DELETE RESTRICT \ \ ALTER TABLE ITSO.CARDACCOUNT ADD CONSTRAINT "CardAccounts" FOREIGN KEY (CARDID) REFERENCES ITSO.CARD ON DELETE RESTRICT \ \ ALTER TABLE ITSO.ACCOUNT ADD CONSTRAINT "BankAccount" REFERENCES ITSO.BANK \ \ FOREIGN KEY (BANKID) ALTER TABLE ITSO.CUSTOMER ADD CONSTRAINT "BankCustomer" FOREIGN KEY (BANKID) REFERENCES ITSO.BANK ON DELETE RESTRICT echo --- execute GRANT BINDADD ON GRANT CONNECT ON GRANT ALL ON GRANT ALL ON GRANT ALL ON GRANT ALL ON GRANT ALL ON GRANT ALL ON GRANT ALL ON GRANT ALL ON GRANT statements DATABASE DATABASE ITSO.BANK ITSO.CUSTOMER ITSO.CARD ITSO.POLICY ITSO.ACCOUNT ITSO.TRANSRECORD ITSO.CARDACCOUNT ITSO.TRANS \ \ --TO PUBLIC TO PUBLIC TO PUBLIC TO PUBLIC TO PUBLIC TO PUBLIC TO PUBLIC TO PUBLIC TO PUBLIC TO PUBLIC echo --- connect reset --CONNECT RESET Figure 177. ITSOBANK Database Data Definition Language (Part 2) We supply these DDL statements in the itsobank.ddl file. You can use the DB2 command line processor to create the database and the tables: db2 -f itsobank.ddl Chapter 15. ATM Application Requirements and Database 275 DB2 Userid Our sample applications use a user ID of ITSO (with password itso) to connect to the ITSOBANK database. We suggest that you define such a user ID and grant it all DB2 privileges on the ITSOBANK database. Note that the prefix (creator ID) of all tables is ITSO. Sample Data of ITSOBANK Tables The sample data of the ITSO bank tables shows the internal relationships among the tables: ❑ We have six customers, with numbers 101 to 106. ❑ There are seven ATM cards with numbers 1111111 to 7777777, and matching PINs 1111 to 7777. ❑ Account numbers are structured xxx-yyyy, where xxx is the customer number. ❑ There is one additional payee customer (901) with a payee account. Tables 26 through 32 list an extract of the sample data of the ATM tables. Table 26. Bank Table Sample Data BANKID BANKNAME ITSO THE ITSO BANK Table 27. Customer Table Sample Data 276 CUSTID TITLE FNAME LNAME USERID PASSWORD 101 Mr. John Akerley cust101 JA 102 Mr. Pat McCarthy cust102 PM 103 Mr. Markus Muetschard cust103 MM 104 Mr. Joaquin Picon cust104 JP 105 Ms. Unknown Lady 106 Mr. Ueli Wahli cust106 UW 901 THE XYZ CORPORATION VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Table 28. Card Table Sample Data CARDID PIN CARDTYPE CUSTID 1111111 1111 ATM 101 2222222 2222 ATM 102 ....... ATM 6666666 6666 ATM 106 7777777 7777 ATM 106 9999999 9999 SPEC 901 Table 29. Account Table Sample Data ACCID BANK ID CUST ID ACCTYPE BALANCE MIN AMT OVER DRAF 101-1001 ITSO 101 CHECKING 80.00 101-1002 ITSO 101 SAVINGS 375.26 100.00 102-2001 ITSO 102 SAVINGS 9375.26 100.00 106-6003 ITSO 106 SAVINGS 3000.00 100.00 106-6004 ITSO 106 CHECKING 4000.00 901-9001 ITSO 901 PAYEE 0 200.00 .... 200.00 BILLTITLE: XYZ VISA Table 30. Transaction Table Sample Data TRANSID ACCID TRANS TYPE TRANS AMT TRACCID 1997-10-07-14.30.26.720001 101-1001 C 80.00 CURRENT TIMESTAMP 101-1002 C 200.00 CURRENT TIMESTAMP 106-6001 T 66.66 106-6002 CURRENT TIMESTAMP 106-6002 F 66.66 106-6001 CURRENT TIMESTAMP 106-6004 D 100.00 ... Chapter 15. ATM Application Requirements and Database 277 Table 31. CardAccount Table Sample Data CARDID ACCID 1111111 101-1001 1111111 101-1002 1111111 105-5001 2222222 102-2001 .... 6666666 106-6004 9999999 901-9001 Table 32. Policy Table Sample Data ACCTYPE POLNAME POLVALUE SAVINGS MINIMUM AMOUNT 100.00 CHECKING OVERDRAFT AMOUNT 200.00 PAYEE MAXIMUM AMOUNT 10000.00 PAYEE POST DATING 1 Figures 178 and 179 show the SQL statements to load the sample data into the tables. Run this file with: db2 -f itsobank.sql 278 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs echo --- connect to ITSOBANK database --- ITSOBANK.SQL CONNECT TO ITSOBANK echo --- insert into BANK table --INSERT INTO ITSO.BANK (bankid, bankname ) VALUES ('ITSO','THE ITSO BANK') \ \ echo --- insert into CUSTOMER table --INSERT INTO ITSO.CUSTOMER (custid, title, fname, ('101', 'Mr.', 'John', ('102', 'Mr.', 'Pat', ('103', 'Mr.', 'Markus', ('104', 'Mr.', 'Joaquin', ('105', 'Ms.', 'Unknown', ('106', 'Mr.', 'Ueli', ('901', 'THE', 'XYZ', lname, 'Akerley', 'McCarthy', 'Muetschard', 'Picon', 'Lady', 'Wahli', 'CORPORATION', userid, 'cust101', 'cust102', 'cust103', 'cust104', null, 'cust106', null, password, bankid) VALUES 'JA', 'ITSO'), 'PM', 'ITSO'), 'MM', 'ITSO'), 'JP', 'ITSO'), null, 'ITSO'), 'UW', 'ITSO'), null, 'ITSO') \ \ \ \ \ \ \ \ echo --- insert into CARD table --INSERT INTO ITSO.CARD (cardid, pin, ('1111111', '1111', ('2222222', '2222', ('3333333', '3333', ('4444444', '4444', ('5555555', '5555', ('6666666', '6666', ('7777777', '7777', ('9999999', '9999', cardtype, custid) VALUES 'ATM', '101' ), 'ATM', '102' ), 'ATM', '103' ), 'ATM', '104' ), 'VISA', '105' ), 'ATM', '106' ), 'ATM', '106' ), 'SPEC', '901' ) \ \ \ \ \ \ \ \ \ echo --- insert into POLICY table --INSERT INTO ITSO.POLICY (acctype, polname, polvalue) VALUES ('SAVINGS', 'MINIMUM AMOUNT', 100.00), ('CHECKING', 'OVERDRAFT AMOUNT', 200.00), ('PAYEE', 'MAXIMUM AMOUNT', 10000.00), ('PAYEE', 'POST DATING', 1 ) \ \ \ \ \ Figure 178. ITSOBANK Database Sample Data Load (Part 1) Chapter 15. ATM Application Requirements and Database 279 echo --- insert into ACCOUNT table --INSERT INTO ITSO.ACCOUNT (accid, bankid, custid, acctype, ('101-1001', 'ITSO', '101', 'CHECKING', ('101-1002', 'ITSO', '101', 'SAVINGS', ('102-2001', 'ITSO', '102', 'SAVINGS', ('102-2002', 'ITSO', '102', 'CHECKING', ('103-3001', 'ITSO', '103', 'SAVINGS', ('103-3002', 'ITSO', '103', 'CHECKING', ('104-4001', 'ITSO', '104', 'CHECKING', ('104-4002', 'ITSO', '104', 'CHECKING', ('105-5001', 'ITSO', '105', 'CHECKING', ('106-6001', 'ITSO', '106', 'CHECKING', ('106-6002', 'ITSO', '106', 'SAVINGS', ('106-6003', 'ITSO', '106', 'SAVINGS', ('106-6004', 'ITSO', '106', 'CHECKING', INSERT INTO ITSO.ACCOUNT (accid, bankid, custid, acctype, ('901-9001', 'ITSO', '901', 'PAYEE', balance, 80.00, 375.26, 9375.26, 75.50, 100.00, 222.22, 362.00, 5.00, 0.00, 1000.00, 2000.00, 3000.00, 4000.00, minamt, overdraf) VALUES 0.00, 200.00), 100.00, 0.00), 100.00, 0.00), 0.00, 200.00), 100.00, 0.00), 0.00, 200.00), 0.00, 200.00), 0.00, 200.00), 0.00, 0.00), 0.00, 200.00), 100.00, 0.00), 100.00, 0.00), 0.00, 200.00) balance, 0.00, billtitle ) VALUES 'XYZ VISA') \ \ echo --- insert into TRANSRECORD table --INSERT INTO ITSO.TRANSRECORD (transid, accid, (CURRENT TIMESTAMP, '101-1001', INSERT INTO ITSO.TRANSRECORD (transid, accid, (CURRENT TIMESTAMP, '101-1002', ... INSERT INTO ITSO.TRANSRECORD (transid, accid, (CURRENT TIMESTAMP, '106-6001', INSERT INTO ITSO.TRANSRECORD (transid, accid, (CURRENT TIMESTAMP, '106-6002', transtype, transamt) VALUES 'C', 80.00 ) transtype, transamt) VALUES 'C', 200.00 ) \ \ transtype, transamt, traccid) VALUES 'F', 66.66, '106-6001' ) echo --- insert into CARDACCOUNT table --INSERT INTO ITSO.CARDACCOUNT \ (cardid, accid) VALUES \ ('1111111', '101-1001'), \ ('1111111', '101-1002'), \ ('1111111', '105-5001'), \ ('2222222', '102-2001'), \ ... ('6666666', '106-6004'), \ ('6666666', '105-5001'), \ ('9999999', '901-9001') echo --- connect reset --CONNECT RESET Figure 179. ITSOBANK Database Sample Data Load (Part 2) 280 \ \ transtype, transamt, traccid) VALUES 'T', 66.66, '106-6002' ) VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 16 ATM Application Business Model and Controller In this chapter we design an implementation of the ATM application, using a layered approach with a user interface, business logic, and persistence layer. We generate the initial design of the business logic layer using the reverse engineering capabilities of the Persistence Builder and extend the resulting model with our business logic. We also implement a controller that interacts with the different layers to minimize direct interactions between the layers. Note: We do not use the bank table, the policy table, and payee accounts in this ATM application. That data may be used by other redbook projects that we base on the same underlying database. © Copyright IBM Corp. 2000 281 Application Design The first step in developing an application is to create an application design based on the requirements the application should satisfy. The power of this design determines how much effort you must spend on implementing it, as well as how difficult it is to modify some features when the requirements change. Application Layers One prerequisite for maintainable applications is a layered architecture that assigns responsibility for certain services to an appropriate application layer. These responsibilities are similar to an object model, where we assign responsibilities to each object. First, all business logic and application knowledge should be modeled in business objects, located in the business object layer. These objects represent core entities of the ATM application and have the responsibility for its correct behavior. To satisfy the needs of the underlying entities, the business objects implement both properties and methods in a standardized way. Next, we define a user interface layer to separate the GUI part of the application from the core business objects. The GUI objects are responsible for handling all user interactions and for presenting business objects in a nice format to users. Whenever information from the business objects is required, or an action is triggered by a user, the respective service from the business objects is called. We also want to separate the data access from the rest of the application. The ATM application has to remember the customer, the card, the account, and the transaction information. Neither the GUI nor the business objects should be aware of the details about where the data is stored. This separation makes it possible to have the core of the application unchanged, even if we change database access, or use other services such as a transaction system to access the enterprise data. Application Layer Architecture We end up with three layers for the ATM application as shown in Figure 180. 282 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Applet, Servlet, HTML ... Business Object Layer Account, Customer, Card, Transrecord Persistence Layer DB2 Database Application Controller User Interface Layer Figure 180. Layers of the ATM Application Figure 180 also shows a cross-layer called the Application Controller. The application controller is necessary to connect the layers. Basically there are three ways of connecting the layers. The important principle is that there be a crisp interface among the layers. Here are the three basic ways of connecting the layers: ❑ Each object has all the knowledge required to access other objects across the borders. This approach looks very appealing at first but can become quite cumbersome because changes at one point might affect multiple classes. Therefore, this approach is only feasible for small applications. ❑ A framework provides the necessary interfaces and underlying services. The application objects just connect to the framework, by either inheritance or delegation. This is a great approach if such a framework is available. However, the creation of a framework is difficult and requires another approach and different skills from those required for an application development project. ❑ The interfaces are modeled in objects that have some knowledge of both sides. Such objects are often called mediators. This approach has the advantage that we have only one place to update, if the interface of a layer or subsystem changes. The downside, of course, is a somewhat longer path for the messages. We decided to use mediator objects in the ATM application; these objects are part of the Controller. We explain their responsibilities when we describe the objects in more detail. Chapter 16. ATM Application Business Model and Controller 283 The most encapsulated layer is the business object layer. Business objects are not aware of the existence of both the user interface layer and the persistence layer. The user interface layer classes use the business objects but have no connection to the persistence layer. The persistence layer knows about the business objects, but not about the user interface layer. The business object layer is generated by the persistence builder of VisualAge for Java. The business logic is added manually to the generated objects. The persistence builder also generates the persistence layer that contains the service classes that provide the automatic interface between the DB2 database and the business layer. Most actions between the two layers will be triggered by the application controller. Business Object Layer Figure 181 shows the complete object model of the business object layer. 0,m Card checkPin cardid pin Methods Properties owningCustomer 0,m ownedCards availableAccount atmCard 0,m 1 Account Customer getGreetings custid title fname lname userid password ownedAcc. 1 Transrecord deposit withdraw 1,m getAccountType owningCust AccTransrec 1 0,m owningAcc accid balance SavingsAccount CheckingAccount withdrawAllowed withdrawAllowed minamt overdraf Figure 181. Object Model of the ATM Business Object Layer 284 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs transid transtype transamt We deal with two type of accounts, savings and checking. The main business logic functions are customer identification and account transactions. In that respect, our implementation requires the following classes: ❑ SavingsAccount and CheckingAccount. Because the main functionality is identical, we introduce a third class, Account, from which the checking and savings account classes inherit most of their behavior. We have to extend the model generated by the persistence builder to add the two subclasses. ❑ Card and Customer. This allows customer identification. ❑ Transrecord. Every bank transaction should be logged. The following types of bank transactions have been identified: PIN validation, debit transaction (deposit), and credit transaction (withdraw). In addition, customer information, card information, and transaction history have to be maintained. In our implementation, Card is the class that knows the card number entered by the user and the PIN related to that card number. Starting from this information, it must perform the PIN validation when the user enters the PIN. After validation, the Card sends a successful or unsuccessful message. Card also holds a property of type Customer. We need the information stored in a Customer object to welcome the card holder with title and name. Account, SavingsAccount, and CheckingAccount are closely related to each other. In fact, the Account class represents the generic bank account, with all of the features of a bank account, such as account ID, balance, and customer ID. SavingsAccount and CheckingAccount inherit from Account, but each of them has additional information and behavior. The SavingsAccount class contains the information related to the minimum amount that it can reach; the CheckingAccount, instead, contains its overdraft value. In other words, the instances of the SavingsAccount and CheckingAccount classes represent the real accounts of the customer. When a customer uses the ATM application to perform a withdrawal transaction, different answers can come from the system, depending on the kind of account. The SavingsAccount class performs the withdrawal transaction only if the balance, after the transaction, is still greater than the minimum amount; the CheckingAccount class checks that a resulting negative balance is higher than the overdraft amount. When a customer requires a deposit transaction, both the SavingsAccount and CheckingAccount classes have the same behavior, that is, they increase the balance by the deposit amount. Chapter 16. ATM Application Business Model and Controller 285 Figure 181 does not show the additional classes that are stored in the ITSOBANK database, Bank and Policy, because they are not required for the implementation of ATM bank transactions. In the current implementation of the persistence builder, and extra class is required for the m:m relationship between the Card and the Account. We will discuss this class when we reverse engineer the database. We will also add some extra properties in the business model. The Account class implements an oldBalance property that holds the balance before a deposit or withdraw transactions, and a property of type Vector to hold all successful bank transactions made by the customer as a transaction history. The Card class implements a property of type Vector that holds a list of accounts that can be accessed by the ATM card. Reverse-Engineer the Business Object Layer The ATM application is built on an existing DB2 database. We use the Persistence Builder to reverse-engineer the database into a business model through the following steps: ❑ Import the database tables into the schema browser of the Persistence Builder ❑ Generate the business model from the database schema ❑ Add the checking and savings accounts to the business model (inheritance) ❑ Adjust the mapping between business model and database schema to support the inheritance between the account classes ❑ Tailor string conversions ❑ Generate the domain classes (business model) ❑ Generate the service classes (interface to the persistent datastore) ❑ Save the model, schema, and mapping Note: In the screen captures you will find two names, ItsoBank and TestBank. ItsoBank represents the finished tailored model, whereas TestBank is the name used to recreate and describe the tailoring steps that we performed. 286 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Import the Database Tables To import an existing schema, that is a set of DB2 tables, perform these steps: ❑ Start the Schema Browser (Workspace -> Tools -> Persistence Builder Tools -> Browse Schemas). ❑ Start the import facility (Schemas -> Import / Export Schema -> Import Schema from Database) and when prompted, enter the name of the new schema, for example, ItsoBank. ❑ Enter the connection information to access the running DB2 system (Figure 182). Figure 182. Import Schema Connection Information We are using the DB2 app driver because DB2 will be running on the same machine as the ATM application; otherwise we would use the DB2 net driver. ❑ In the Select Tables dialog, select the ITSO qualifier and click on Build Table List. Select all tables except for the TRANS synonym (Figure 183). Note: We created the TRANS synonym for the TRANSRECORD table to run the ATM application described in the redbook VisualAge for Java Enterprise Version 2: Data Access Beans - Servlets - CICS Connector. Chapter 16. ATM Application Business Model and Controller 287 Figure 183. Import Schema Select Tables ❑ Browse the schema and check out all the tables and foreign key relationships (Figure 184). Figure 184. Import SchemaResult 288 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Note: In our first database definition we did not name the foreign key constraints. The Schema Browser generated its own numbered names for the foreign key relationships that we did find user-friendly. We manually changed the names and regenerated the DDL for the database from the Schema Browser. Update the Foreign Key Relationship When regenerating the DDL from the schema, the foreign key relationships are generated into SQL DDL statements, such as: ALTER TABLE ITSO.ACCOUNT ADD CONSTRAINT CustomerAccount FOREIGN KEY (CUSTID) REFERENCES ITSO.CUSTOMER ON DELETE RESTRICT; DB2 does not preserve the mixed case constraint name, unless it is put between double quotes: ALTER TABLE ITSO.ACCOUNT ADD CONSTRAINT "CustomerAccount" FOREIGN KEY (CUSTID) REFERENCES ITSO.CUSTOMER ON DELETE RESTRICT; We can open each foreign key relationship and add the double quotes to the physical name (Figure 185). This preserves the mixed case names when generating the DDL from the Schema Browser. Figure 185. Mixed-Case Names for Foreign Key Relationships Chapter 16. ATM Application Business Model and Controller 289 Generate the Business Model from the Schema The next step is to generate the business model from the imported schema. In the Schema Browser select Schemas -> Generate Model from Schema to perform the step; then open the Model Browser (Figure 186). Figure 186. Generated Business Model To tailor the business model for our needs we performed three steps: ❑ Relationship tailoring (names, required) ❑ Class tailoring (required attributes) ❑ Account inheritance Relationship Tailoring Figure 186 shows the two relationships of the Card class: Card–Customer and Card–Cardaccount. Double-click on a class association to tailor a relationship. Figure 187 shows the Card-Customer relationship before and after tailoring. 290 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 187. Relationship Tailoring (Before and After) We tailored the relationships in two ways: assigning user-friendly names and specifying the required attribute. (The setting of the required attribute is lost in the generation process; custid is a NOT NULL column in the card table.) The tailored relationships are: ❑ Card CustomerCard Customer: owningCustomer (1, required), ownedCards (m) ❑ Cardaccount CardAccounts Card: atmCard (1, required), toAccounts (m) ❑ Account BankAccount Bank: owningBank (1), ownedAccounts (m) ❑ Customer BankCustomer Bank: theBank (1, required), hasCustomers (m) ❑ Account CustomerAccount Customer: owningCustomer (1, required), ownedAccounts (m) ❑ CardAccount AccountCards Account: availableAccount (1, required), toCards (m) ❑ Transrecord AccountTransRecord Account: owningAccount (1, required), accountTransrecords (m) Note that the m:m relationship between Card and Account is implemented as two 1:m relationships from Card to Cardaccount and from Account to Cardaccount. Chapter 16. ATM Application Business Model and Controller 291 Class Tailoring The attributes in the generated business classes are defined as required or not required based on the nulls allowed specification in the schema. Open each class (Classes -> Edit Class) and verify the generated attributes (Figure 188). Figure 188. Class Editor Account Inheritance The most difficult task is to define the inheritance structure of the accounts. After the import, the Account class carries all the attributes from the table columns: accid, acctype, balance, minamt, and overdraf. What we really want is three classes: ❑ Account, with accid, balance, and billtitle ❑ CheckingAccount, subclass, with overdraf ❑ SavingsAccount, subclass, with minamt The acctype attribute defines the type of account. This is called the discriminator column in the Persistence Builder. A value of PAYEE defines an account (we did not use a subclass for payees in the ATM application), a value of CHECKING defines a checking account, and a value of SAVINGS defines a savings account. 292 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs First we edit the Account class and delete the acctype, minamt, and overdraf attributes. We only keep the accid (object ID), the balance, and the billtitle. Next we define a new class, CheckingAccount, as a subclass of Account, and add a new attribute called overdraf (Figure 189). Figure 189. Defining the CheckingAccount Class In the same way we define the SavingsAccount class with the minamt attribute. Figure 190 shows the Account class hierarchy in the Model Browser. Expand and compress the hierarchy by double-clicking on the Account class. The relationships are only defined at the Account class, that is the superclass. The subclasses inherit the relationships and attributes of the superclass, but the inherited objects are not displayed in the Model Browser. Chapter 16. ATM Application Business Model and Controller 293 Figure 190. Account Class withSubclasses Adjust the Mapping for Inheritance The mapping that was generated during transformation of the schema to the business model does not support the inheritance that we introduced in the Model Browser. Our task is to adjust the mapping so that the Persistence Builder knows how to differentiate between the account classes in the database. When we open the Map Browser we immediately see the broken mapping for the Account class (Figure 191). Our database has only one table for the three account classes; this is called single table inheritance. We have to create the mapping between the three account classes and the single Account table, using a discriminator column to distinguish between the classes. 294 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 191. Map Browser with Broken Mapping The steps to define the single table inheritance for the Account class are: ❑ Delete the existing mapping (select the broken map, and Table_Maps -> Delete Table Map) ❑ Define the mapping for the superclass. Select the Account class and Table_Maps -> New Table Map -> Add Single Table Inheritance Table Map (Figure 192). Figure 192. Single Table Inheritance Mapping Root Chapter 16. ATM Application Business Model and Controller 295 Select the ACCOUNT table, the discriminator column (ACCTYPE), and the discriminator value (PAYEE). The Root of the model hierarchy box is checked automatically. Note: The ACCTYPE column is defined as CHAR(10). Therefore all discriminator values must be expanded with blanks to a length of 10 characters, that is “PAYEE “, “CHECKING “, and “SAVINGS “. If you specify the values without the trailing blanks, retrieval of the accounts from the database fails. Select the new mapping and open it (Table_Maps -> Edit Property Maps). Map each attribute and association (Figure 193). Figure 193. Mapping Attributes and Associations Define the Mapping for the CheckingAccount Subclass Define the mapping for the CheckingAccount subclass. Select the CheckingAccount subclass and Table_Maps -> New Table Map -> Add Single Table Inheritance Table Map. Select the ACCOUNT table and the discriminator value (CHECKING, with two blanks). The discriminator column (ACCTYPE) and the Root of the model hierarchy checkbox are preset and disabled (Figure 194). 296 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 194. Single Table Inheritance Mapping Subclass Edit the property map for the CheckingAccount class and map the overdraf attribute to the matching column. All the other attributes belong to the superclass (Account) and are already mapped. Define the Mapping for the SavingsAccount Subclass Perform the same mapping for the SavingsAccount subclass with a discriminator value of SAVINGS (with 3 blanks) and edit the property map to map the minamt attribute to the matching column. The mapping of the business model into the relational schema is now complete. Tailor String Conversions The Persistence Builder provides many conversions between column values in the database and object values in the business model. By default, the so-called VapConverter is used, and it performs a reasonable conversion for most data types. For CHAR columns that map to string attributes the VapTrimStringConverter is used by default. For example, the first and last names of a customer are defined as CHAR(30) in the database, and the business model attributes are strings. In most programs we want to deal with trimmed strings, and the VapTrimStringConverter is the proper choice. Select each CHAR column in the Schema Browser and select Columns -> Edit Column to verify the converter (Figure 195). Chapter 16. ATM Application Business Model and Controller 297 Figure 195. Column Converter Generate the Domain Classes The model, schema, and mapping are in place now and we can generate the Java code. From the Model Browser, select Models -> Generate, and select Domain Classes and Interfaces. We store the generated code in the ITSO SG245426 ITSOBANK ATM project and itso.bank.persistence.model package. The Generate Bound Bean Properties checkbox should be selected so that events are generated for changed properties (Figure 196). 298 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 196. Generate Domain Classes and Interfaces Generate the Service Classes The service classes provide the interface between the model classes and the persistent datastore. We generate the service classes from the Model Browser or from the Map Browser. In the Map Browser select Datastore_Maps -> Generate Services and then select Data Service Classes and Interfaces. In the Generations Options dialog, select Relational SQL, and enter itso.bank.persistence.services as the package name (Figure 197). Chapter 16. ATM Application Business Model and Controller 299 Figure 197. Service Generation Options (Part 1) On the next page, specify the same connection information as in Figure 182 on page 287. Select the Generate queries using parm marker binding checkbox (Figure 198). Figure 198. Service Generation Options (Part 2) 300 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs On the next page select all the model classes and click on Finish. Save the Model, Schema, and Mapping After generating the supporting Java code we save the model, schema, and mapping in the itso.bank.persistence package: ❑ In the Model Browser, we use Models -> Save Model, select the project and package, and name the class ItsoBankModel. ❑ In the Schema Browser, we use Schemas -> Save Schema, select the project and package, and name the class ItsoBankSchema. ❑ In the Map Browser, we use Datastore_Maps -> Save Datastore Map, select the project and package, and name the class ItsoBankItsoBankMap. Note: The classes that are generated when saving the model, schema, and map contain only an infoString method with a textual description. The metadata is saved in the repository. However, these classes can be versioned together with the generated Java code for configuration management. Extend the Business Object Layer The business (or domain) classes that we generated from the Model Browser contain all the data attributes, or properties, with corresponding get and set methods, and property change events. Instances of business objects are created by retrieving data from the database, or by creating new objects. Updated objects are written back to the database when the transaction is committed. The decision we have to make is where to put the additional business logic and the handling of transactions. For example, a deposit touches both the Account object and creates a new Transrecord object, all in one transaction. We put most of this logic into the application controller, especially the transaction handling. We will add a little bit of logic to some of the business objects, mainly to collect information into a user-suitable format. Notice We use a simplified approach and add the logic to the Impl classes and not the Bean classes. The new methods either forward processing to the bean, or the new attributes are not volatile. We describe the approach with extending the Bean classes in Chapter 7, “Enhance Business Object Model” on page 123. Chapter 16. ATM Application Business Model and Controller 301 Extend the Business Objects For each business object the generation process created: ❑ ❑ ❑ ❑ ❑ ❑ An interface, for example, Customer An implementation, for example, CustomerImpl A bean, for example, CustomerBean A home interface, for example CustomerHome A home implementation, for example, CustomerHomeImpl A key, for example, CustomerKey The home will be used by the application controller to retrieve and create objects. Retrieving an object from the database returns an implementation object that implements the interface. The interface or the implementation is used in the front-end application code. The bean object holds the actual data values. Methods invoked on the interface or implementation are forwarded to the bean object. When working with transactions, multiple versions of the bean object may exist at a given time. The implementation object forwards the method to the correct bean object depending on the transaction scope. We implement our additional code in the implementation object. When attribute data is required we get the data from the bean object in the same way as the get methods of the implementation object, or we use the get methods directly. We also add any public method to the interface. The get methods of the bean object may throw a number of exceptions that must either be handled or thrown to the caller. Note: Be careful when adding new attributes (properties) to a business object. If they are added to the bean, then values may be lost when data is committed, because a new instance of the bean is instantiated. If attributes are added to the implementation object, the values may not reflect the latest state of the bean object. In our application the added data is not volatile, so we decided to add the new properties to the implementation objects. Customer Business Object We extend the Customer interface and the CustomerImpl class with two methods: ❑ getGreetings, to return a string composed of title, first name, and last name ❑ toString, for tracing and debugging Customer Interface String getGreetings() throws java.rmi.RemoteException; 302 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Note: We do not add the toString method to the public interface because every object supports that interface. CustomerImpl Class We implement the methods in the CustomerImpl class: ❑ getGreetings: public String getGreetings() throws java.rmi.RemoteException { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForRead(); CustomerBean bean = (CustomerBean)version.getBean(); return bean.getTitle().trim() + " " + bean.getFname().trim() + " " + bean.getLname().trim(); } ❑ toString: public String toString() { try { return "Customer " + getCustid() + "-" + getGreetings(); } catch (Exception e) { return "Customer invalid"; } } Card Business Object The Card class can validate a PIN and knows the holder (customer) and the accounts associated with the card. Card Interface We add the following supporting methods: java.util.Vector getAccounts() throws java.rmi.RemoteException, javax.ejb.FinderException; Account getAccount(String accountId) throws java.rmi.RemoteException, javax.ejb.FinderException; String getGreetings() throws java.rmi.RemoteException, javax.ejb.FinderException; boolean checkPin(String pinEntered) throws java.rmi.RemoteException; CardImpl Class We implement the methods in the CardImpl class: ❑ getAccounts, retrieves the accounts associated with the card from the database into the vector of accounts public java.util.Vector getAccounts() throws java.rmi.RemoteException, javax.ejb.FinderException { java.util.Vector accounts = new java.util.Vector(); LinkCollectionShell cardaccounts = this.getToAccounts(); if (cardaccounts.size() > 0) { Chapter 16. ATM Application Business Model and Controller 303 Enumeration enum = cardaccounts.elements(); for (int i=0; enum.hasMoreElements(); i++) { Cardaccount ca = (Cardaccount)enum.nextElement(); accounts.addElement( (Account)ca.getAvailableAccount() ); } } return accounts; } ❑ getAccount, retrieves the account object for a given account ID from the related accounts public Account getAccount(String accountId) throws java.rmi.RemoteException, javax.ejb.FinderException { LinkCollectionShell cardaccounts = this.getToAccounts(); if (cardaccounts.size() > 0) { Enumeration enum = cardaccounts.elements(); for (int i=0; enum.hasMoreElements(); i++) { Cardaccount ca = (Cardaccount)enum.nextElement(); Account account = (Account)ca.getAvailableAccount(); if ( account.getAccid().equals(accountId) ) return account; } } return null; } ❑ checkPin, checks the PIN against a given number public boolean checkPin(String pinEntered) throws java.rmi.RemoteException { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForRead(); CardBean bean = (CardBean)version.getBean(); return bean.getPin().trim().equals( pinEntered.trim() ); } ❑ getGreetings, returns the greeting from the associated customer public String getGreetings() throws java.rmi.RemoteException, javax.ejb.FinderException { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForRead(); CardBean bean = (CardBean)version.getBean(); return bean.getOwningCustomer().getGreetings(); } ❑ toString, for tracing and debugging public String toString() { try { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForRead(); CardBean bean = (CardBean)version.getBean(); 304 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs return "Card " + bean.getCardid() + " PIN " + bean.getPin(); } catch (Exception e) { return "Card invalid"; } } Notes: ❑ The getAccounts method follows the m:m relationship between Card and Account classes in two steps. First a list of intermediate Cardaccount objects is retrieved as a LinkCollectionShell. In a second step, the associated account is retrieved for each intermediate object by looping through the collection. We did not implement other methods to support the m:m relationship as described in Chapter 9, “Many-to-Many Associations” on page 165. ❑ The getAccount method looks through the related accounts and compares the given account ID with the stored account objects. ❑ The checkPin method returns either true of false. ❑ The getGreetings method follows the relationship from the Card to the Customer and invokes the getGreetings method of the customer object. Transrecord Business Object The only addition to the Transrecord object is the toString method. TransrecordImpl Class We implement the toString method: public String toString() { try { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForRead(); TransrecordBean bean = (TransrecordBean)version.getBean(); return "Transrecord " + bean.getTransid() + " " + bean.getTranstype() + " " + bean.getTransamt(); } catch (Exception e) { return "Transrecord invalid"; } } Account Business Object The Account class holds the account ID and the balance, and has two subclasses, CheckingAccount and SavingsAccount. For the purposes of the user interface we add an oldBalance property that displays the previous balance after a deposit or withdraw transaction. We also add a transactions property, of type Vector, that holds a list of Chapter 16. ATM Application Business Model and Controller 305 Transrecord objects. Both properties are defined as bound so that they fire property change events when the value changes. A Transrecord object is added to the list after each successful deposit or withdraw operation. The user can also request to see all associated Transrecord objects; they are retrieved from the database in this case. Account Interface We add the following supporting methods: java.math.BigDecimal getOldBalance(); java.util.Vector getTransactions(); String getAccountType(); void addTransaction(Transrecord tran); void getTransactionsFromDb() throws java.rmi.RemoteException, javax.ejb.FinderException; void deposit(String amount) throws java.rmi.RemoteException; boolean withdraw(String allowed) throws java.rmi.RemoteException; boolean withdrawAllowed(java.math.BigDecimal amount) throws java.rmi.RemoteException; AccountImpl Class We implement the properties and methods in the AccountImpl class: ❑ Properties oldBalance and transactions, generate: private java.math.BigDecimal fieldOldBalance = new java.math.BigDecimal(0); private Vector fieldTransactions = new Vector(); public java.math.BigDecimal getOldBalance() { return fieldOldBalance; } public Vector getTransactions() { return fieldTransactions; } private void setOldBalance(java.math.BigDecimal oldBalance) { java.math.BigDecimal oldValue = fieldOldBalance; fieldOldBalance = oldBalance; firePropertyChange("oldBalance", oldValue, oldBalance); } private void setTransactions(Vector transactions) { Vector oldValue = fieldTransactions; fieldTransactions = transactions; firePropertyChange("transactions", oldValue, transactions); } Note that we made both set methods private; they are only used internally and are not part of the public interface. 306 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs ❑ getAccountType, returns the type of account (this method will be overwritten by the subclasses) public String getAccountType() { return "PAYEE"; } ❑ addTransaction, called by the controller after deposit and withdraw to add the newly created Transrecord string to the transactions vector public void addTransaction(Transrecord tran) { fieldTransactions.addElement(tran); } ❑ getTransactionsFromDb, fills the transactions vector with all the associated Transrecords from the database public void getTransactionsFromDb() throws java.rmi.RemoteException, javax.ejb.FinderException { java.util.Vector trans = new java.util.Vector(); LinkCollectionShell acctrans = this.getAccountTransrecords(); if (acctrans.size() > 0) { Enumeration transenum = acctrans.elements(); for (int i=0; transenum.hasMoreElements(); i++) trans.addElement( (Transrecord)transenum.nextElement() ); } setTransactions( trans ); } ❑ deposit, saves the previous balance and adds an amount to the balance public void deposit(String amount) throws java.rmi.RemoteException { java.math.BigDecimal amt = new java.math.BigDecimal(amount); setOldBalance( getBalance() ); setBalance( getBalance().add(amt) ); } ❑ withdraw, calls withdrawAllowed to check if enough funds are available, saves the previous balance, and subtracts an amount from the balance public boolean withdraw(String amount) throws java.rmi.RemoteException { java.math.BigDecimal amt = new java.math.BigDecimal(amount); if ( withdrawAllowed(amt) ) { setOldBalance( getBalance() ); setBalance( getBalance().subtract(amt) ); return true; } return false; } ❑ withdrawAllowed, checks the available funds and returns true or false (this method will be overwritten by the subclasses) Chapter 16. ATM Application Business Model and Controller 307 public boolean withdrawAllowed(java.math.BigDecimal amount) throws java.rmi.RemoteException { return ( getBalance().compareTo(amount) >= 0 ); } ❑ toString, for tracing and debugging public String toString() { try { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForRead(); AccountBean bean = (AccountBean)version.getBean(); return getAccountType() + " " + bean.getAccid() + " Balance " + bean.getBalance(); } catch (Exception e) { return "Account invalid"; } } CheckingAccount Business Object CheckingAccount is a subclass of Account and therefore inherits all of its properties and methods. In addition it has an overdraft property (named overdraf), which is the maximum amount by which the account is allowed to be overdrawn. If the customer attempts to withdraw an amount that exceeds this limit, a false result is returned. No changes are necessary to the interface, only three methods must be implemented in the implementation class. CheckingAccountImpl Class We implement the methods in the CheckingAccountImpl class: ❑ getAccountType, returns the type of account public String getAccountType() { return "CHECKING"; } ❑ withdrawAllowed, checks the available funds and returns true or false public boolean withdrawAllowed(java.math.BigDecimal amount) throws java.rmi.RemoteException { return ( getBalance().add(getOverdraf()).compareTo(amount) >= 0 ); } ❑ toString, for tracing and debugging public String toString() { try { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForRead(); CheckingAccountBean bean = (CheckingAccountBean)version.getBean(); return getAccountType() + " " + bean.getAccid() + " Balance " 308 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs + bean.getBalance() + " Overdraft " + bean.getOverdraf(); } catch (Exception e) { return "Checking account invalid"; } } SavingsAccount Business Object SavingsAccount is a subclass of Account. It has an additional minamt property, which is the minimum amount of the account’s balance. If the customer attempts to withdraw an amount that would leave a balance below the limit, a false result is returned. No changes are necessary to the interface, only three methods must be implemented in the implementation class. SavingsAccountImpl Class We implement the methods in the SavingsAccountImpl class: ❑ getAccountType, returns the type of account public String getAccountType() { return "SAVINGS"; } ❑ withdrawAllowed, checks the available funds and returns true or false public boolean withdrawAllowed(java.math.BigDecimal amount) throws java.rmi.RemoteException { return ( getBalance().subtract(amount).compareTo( getMinamt() ) >= 0 ); } ❑ toString, for tracing and debugging public String toString() { try { com.ibm.vap.Transactions.Version version = this.getBom().getVersionForRead(); SavingsAccountBean bean = (SavingsAccountBean)version.getBean(); return getAccountType() + " " + bean.getAccid() + " Balance " + bean.getBalance() + " MinAmount " + bean.getMinamt(); } catch (Exception e) { return "Savings account invalid"; } } Persistence Layer The persistence layer is completely generated by the Persistence Builder; it consists of the service classes that are generated from the Map Browser. No code must be added or modified. Chapter 16. ATM Application Business Model and Controller 309 Application Controller The application controller, or controller for short, works as a manager between the different application layers. Its task is to delegate the work and to control what is happening inside the application. We can describe the flow of information in the controller thus: ❑ Whenever an event occurs, for example, a user clicks on a button, the user interface layer invokes a method in the controller, and this method invokes one or multiple methods in the business object layer. ❑ The controller checks the result of methods in the business object layer where necessary and takes appropriate action, that is, the controller fires events to notify the user interface layer. Controller Methods and Events The user interface layer assumes that it can invoke the methods shown in Table 33, and that it is notified by the events listed in Table 34. In other words, our ATM application controller must provide an implementation of these methods and events. Table 33. ATM Application Controller Methods 310 Method Return Type Parameters Remarks getCard Card String cardId Retrieve Card object, fire cardFound or cardNotFound event checkPin boolean Card, String pin Check the PIN, fire PinCheckedOk or PinCheckedNotOk event getAccounts Vector Card Retrieve accounts of a card deposit Account Account, String amount Deposit amount, create Transrecord, fire newTransaction event withdraw Account Account, String amount Withdraw amount if allowed, create Transrecord, fire newTransaction or limitExceeded event getTransactions Account Account Retrieve all transactions for an account disconnect void - Terminate datastore VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Table 34. ATM Application Controller Events Event Event Listener Method Remarks cardFound handleCardFound Valid card number entered cardNotFound handleCardNotFound Invalid card number entered pinCheckedOk handlePinCheckedOk Valid PIN entered pinCheckedNotOk handlePinCheckedNotOk Invalid PIN entered newTransaction handleNewTransaction Deposit or withdraw succeeds limitExceeded handleLimitExceeded Withdraw not allowed because limit exceeded DBOutOfSynch handleDBOutOfSynch Deposit or withdraw fails because database content of account changed Controller and Business Model Interaction Figure 199 shows the interaction between the controller methods and the business model, and the events that may occur: ❑ The CardHome is used to retrieve the Card. ❑ The resulting Card is used to check the PIN and to retrieve the associated accounts. ❑ Once an account has been selected, it is used for deposit and withdraw transactions. ❑ Deposit and withdraw call an internal createTransrecord method that uses the TransrecordHome to create a new Transrecord object. ❑ Various events are fired to alert the user interface of every condition that occurs in the business model. In addition to the interactions with the business model, we have to interact with the persistent datastore. These activities include the starting of the datastore, the setup of the homes that are required to retrieve and create business objects, and the transaction management. Chapter 16. ATM Application Business Model and Controller 311 Controller get Transactions create Trans record (private) disconnect DataStore getCard Transrecord Home with draw CardHome Account Card check Pin get Accounts deposit Events limit Exceeded DBOut OfSynch new Trans action pin Checked NotOk pin Checked Ok Figure 199. Controller and Persistence Interfaces 312 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs card NotFound card Found Implement the Controller To start, we create a new class, ATMApplicationController, as a subclass of Object in the itso.bank.persistence.model package. We have to implement the events that are fired, the methods that are available to the user interface layer, and the interactions with the underlying datastore. We implement the code in the sequence: ❑ Define the events that are fired ❑ Interactions with the datastore ❑ Implement the methods Controller Events On the BeanInfo page, add all the events listed in Table 34 on page 311. Select New Listener Interface in the Features menu to create the events. On the first page of the New Event Listener SmartGuide specify the event name, and on the second page add the event listener method (prefix the event name with handle, for example, handleCardFound). We will fire these events in the methods of the controller depending on the results of database access or validation. Interactions with the Datastore We prepare the following variables (fields) in the controller for the interactions with the datastore: ❑ ❑ ❑ ❑ ❑ ❑ datastore, to activate the persistent datastore readonlyTx, read-only transaction for all retrieve activities updateTx, read-write transaction for deposit and withdraw operations cardHome, home to retrieve a Card object tranHome, home to create new Transrecord objects acctHome, home to retrieve an Account object The Java definitions for these variables are: private private private private private private com.ibm.vap.Persistence.DataStore dataStore = null; com.ibm.vap.Transactions.Transaction readonlyTx = null; com.ibm.vap.Transactions.Transaction updateTx = null; CardHomeImpl cardHome = null; TransrecordHomeImpl tranHome = null; AccountHomeImpl acctHome = null; Chapter 16. ATM Application Business Model and Controller 313 The constructor of the controller initializes the variables. public ATMApplicationController() { super(); dataStore = itso.bank.persistence.services.ItsoBankItsoBankDataStore.singleton(); dataStore.activate(); cardHome = itso.bank.persistence.model.CardHomeImpl.singleton(); tranHome = itso.bank.persistence.model.TransrecordHomeImpl.singleton(); acctHome = itso.bank.persistence.model.AccountHomeImpl.singleton(); readonlyTx = com.ibm.vap.Transactions.Transaction.beginReadOnly(); com.ibm.vap.RelationalPersistence.SqlQuery.setThrowNoRowFoundException(true); } The read-only transaction stays active all the time. Updates are performed using a new read/write child transaction that is committed after each deposit and withdraw operation. The last line of code forces an exception when a commit fails. Controller Methods Now we review the scenario of the ATM application and implement the methods from Table 33 on page 310 step by step. We add all the methods on the BeanInfo page as new method features. ❑ getCard The user enters the card number. If the number is valid, the system prompts for the PIN; otherwise an error message appears. Therefore, we retrieve the card from the database, and, depending on the result, fire an event. public Card getCard(String cardId) { Card newCard = null; try { newCard = cardHome.find(cardId); fireHandleCardFound( new CardFoundEvent(this) ); } catch (Exception e) { fireHandleCardNotFound( new CardNotFoundEvent(this) ); } return newCard; } ❑ checkPin The user enters the PIN. This PIN is validated against the PIN stored in the Card and a PinCheckOk or PinCheckNotOk event is fired. public boolean checkPin(Card card, String pinEntered) { boolean result = false; try { 314 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs result = card.checkPin(pinEntered); } catch (Exception e) { e.printStackTrace(System.out); result = false; } if (result) fireHandlePinCheckedOk(new PinCheckedOkEvent(this)); else fireHandlePinCheckedNotOk(new PinCheckedNotOkEvent(this)); return result; } ❑ getAccounts If the PIN is valid, the system shows a list of the accounts the card is authorized to access. Later, the user can select one account to see the details. The actual code is implemented in the Card class. public java.util.Vector getAccounts(Card card) { try { return card.getAccounts(); } catch (Exception e) { return null; } } ❑ deposit The user performs a deposit transaction for the selected account. The account is asked to update itself, and a new Transrecord is created and added to the transactions property of the account. All update activity is performed in a child transaction of the read-only transaction. If the updating and the commit of the child transaction is successful, a newTransaction event is fired, otherwise a DBOutOfSynch event is fired. The creation of the Transrecord object is performed in a private createTransrecord method that is reused by the withdraw method. public Account deposit(Account account, String amount) { try { updateTx = readonlyTx.beginChild(); account.deposit(amount); Transrecord trec = createTransrecord(account,"D",amount); account.addTransaction(trec); updateTx.commit(); readonlyTx.resume(); fireHandleNewTransaction(new NewTransactionEvent(this)); } catch (Exception e) { try { updateTx.rollback(); readonlyTx.resume(); ((AccountImpl)account).refresh(); } catch(Exception e2) {} fireHandleDBOutOfSynch(new DBOutOfSynchEvent(this)); e.printStackTrace(System.out); } return account; Chapter 16. ATM Application Business Model and Controller 315 } ❑ createTransrecord This private method creates the new Transrecord object that is related to the Account object. The current timestamp is used as the key of the object. private Transrecord createTransrecord(Account account, String type, String amt) throws java.rmi.RemoteException, javax.ejb.FinderException { Transrecord tr = tranHome.create( new java.sql.Timestamp(System.currentTimeMillis()) ); tr.setTranstype(type); tr.setTransamt( new java.math.BigDecimal(amt) ); tr.setOwningAccount(account); return tr; } ❑ withdraw Withdraw is handled in a way similar to deposit. The system performs a check. If the overdraft or minimum balance limit of the account would be reached because of the withdrawal, the request is rejected; otherwise the balance is updated and a new Transrecord is added to the account history. If the withdrawal was not allowed (the account returns false), the controller fires a limitExceeded event and returns the account unchanged. All update activity is performed in a child transaction of the read-only transaction. If the updating and the commit of the child transaction is successful, a newTransaction event is fired, otherwise a DBOutOfSynch event is fired. public Account withdraw(Account account, String amount) { try { updateTx = readonlyTx.beginChild(); if (account.withdraw(amount) == false) { fireHandleLimitExceeded ( new LimitExceededEvent (this, "Sorry - your balance is too small") ); updateTx.rollback(); readonlyTx.resume();; return account; } Transrecord trec = createTransrecord(account,"C",amount); account.addTransaction(trec); updateTx.commit(); readonlyTx.resume(); fireHandleNewTransaction(new NewTransactionEvent(this)); } catch (Exception e) { try { updateTx.rollback(); readonlyTx.resume(); ((AccountImpl)account).refresh(); } catch(Exception e2) {} fireHandleDBOutOfSynch(new DBOutOfSynchEvent(this)); e.printStackTrace(System.out); } 316 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs return account; } ❑ getTransactions The user might want to look at the transaction history. public Account getTransactions(Account account) { try { account.getTransactionsFromDb(); } catch (Exception e) { e.printStackTrace(System.out); } return account; } ❑ disconnect The user interface may decide to terminate the application. It can invoke the disconnect method that terminates the datastore. This also removes the connection to the database. public void disconnect() { try { dataStore.reset(dataStore); } catch (Exception e) { e.printStackTrace(System.out); } } ❑ transactionResume In the servlet environment we have to resume the read-only transaction for each interaction. public void transactionResume() { try { readonlyTx.resume(); } catch (Exception e) {} } The AtmApplicationController bean is ready. Note that in VisualAge for Java Version 2 we defined all methods as synchronized to work properly in a multithreaded servlet environment. In Version 3 multithreaded servlets are supported through new transaction binding policies. See “Multithreading Support” on page 259 for a description of multithreading support and “Initialization” on page 353 (in “Implement the Controller Servlet” ) for a servlet example and “Implement the JSP Controller Servlet” on page 399 for a JSP/servlet example. Exceptions are caught and logged on the console standard output. Later, you may change the controller to have an improved logging mechanism. Chapter 16. ATM Application Business Model and Controller 317 Test the Controller and the Business Objects After the beans (classes) for the business objects are created, they are ready for testing. If you ensure now that every bean performs the task for which it is responsible, and the beans interact properly, it is easier to locate problems—if there are problems—as you add the other layers. To test the business objects, we use the Scrapbook window, the Console window, and inspectors. We suggest saving test scripts for the various test cases as files. This approach can be helpful for regression testing. Test with the Scrapbook Window To run a sample page of the Scrapbook window, add the code listed in Figure 200 to a Scrapbook page and select one of the business model classes in the Page -> Run in menu to allow short class names without the package prefix. // set Page->Run in to ATMApplicationController class in itso.bank.persistence.model java.util.Enumeration enum; Account acct1; ATMApplicationController ctl = new ATMApplicationController(); Card card1 = ctl.getCard("1111111"); System.out.println(card1); boolean pinok = ctl.checkPin(card1,"1111"); System.out.println("PIN OK " + pinok); ctl.getAccounts(card1); enum = card1.getAccounts().elements(); while (enum.hasMoreElements()) { System.out.println( (Account)enum.nextElement() ); } acct1 = (Account)card1.getAccount("101-1001"); System.out.println("\n"+acct1); ctl.getTransactions(acct1); enum = acct1.getTransactions().elements(); while (enum.hasMoreElements()) { System.out.println( enum.nextElement() ); } acct1 = (Account)card1.getAccount("101-1002"); System.out.println("\n"+acct1); ctl.deposit(acct1,"400"); System.out.println(acct1); ctl.withdraw(acct1,"400"); System.out.println(acct1); ctl.getTransactions(acct1); enum = acct1.getTransactions().elements(); while (enum.hasMoreElements()) { System.out.println( enum.nextElement() ); } ctl.disconnect(); Figure 200. Scrapbook for Testing the Controller and the Business Model 318 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 17 Swing GUI for ATM Application In this chapter we develop a GUI for the ATM application, using the Java Swing classes, also known as the Java Foundation Classes (JFC). We basically use the design implemented in the redbook VisualAge for Java Enterprise Version 2: Data Access Beans - Servlets - CICS Connector. The only changes required are the exchange of the business model classes with the classes generated by the persistence builder, and the use of the new application controller. We do not explain the Swing classes in any detail because many books about Swing are available. We use Swing to create a simple GUI that is more attractive than an AWT GUI. © Copyright IBM Corp. 2000 319 Design of the GUI Application Figure 201 shows the basic layout of the application. We use one main panel with a card layout of four panels: ❑ ❑ ❑ ❑ Card panel to enter the ATM card number PIN panel to enter the PIN for the ATM card Select account panel to select an account that belongs to the card Transaction panel for deposit and withdrawal transactions Figure 201. ATM Application Panels 320 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Application Controller The heart of the GUI application is the ATM application controller that performs all the processing together with an implementation of the persistence interface (Figure 202). initialization termination get Transactions create Trans record (private) with draw disconnect DataStore getCard Transrecord Home Account deposit CardHome Card check Pin get Accounts Figure 202. GUI Application with Application Controller Panel Design The four panels have the same basic layout: ❑ ❑ ❑ ❑ A container panel with a border layout A greeting panel in the North area (flow or grid bag layout) A button panel in the South area (flow layout) A processing panel in the center (grid bag layout) Chapter 17. Swing GUI for ATM Application 321 Nonvisual Variables All four panels have the same four nonvisual variables: ❑ Controller (of type ATMApplicationController), used for application processing ❑ Card (of type Card, the interface generated by the Persistence Builder), used to display the card number and customer name ❑ Main (of type JPanel), required as parent in the calls to CardLayout ❑ CardLayout (of type CardLayout), used to switch to the next or previous panel The select account panel and the transaction panel contain one additional variable that represents the current bank account. Setup of the Variables The Controller, Card, and Main variable are set up at the start of the application and the CardLayout variable is prepared on each subpanel: ❑ The Controller is allocated in the main panel and assigned to the variables on each subpanel. ❑ A Card variable is set up in the main panel and connected to all subpanels. An actual card object is retrieved in the card panel. ❑ The Main variable represents the main panel itself and is passed on to all subpanels. ❑ The CardLayout is the layout manager property of the main panel and is allocated in each subpanel. To set the CardLayout variable, connect the this event of the Main variable to the this property of the CardLayout variable and pass the layout property as a parameter. Promotion of the Variables The this property of the Controller, Card, and Main variables on all subpanels is promoted so that the variables are accessible from the main panel. This creates the properties: ❑ controllerThis ❑ cardThis ❑ mainThis 322 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Implementation of the Application Panels We implement the four panels independently. We assemble the panels in the main applet panel in a later step. We create a new itso.bank.persistence.gui package for the GUI application and create the four panels as subclasses of the Swing JPanel class. Card Panel Figure 203 shows the visual composition of the card panel (CardPanel class). 2 1 3 9 4 1 2 8 7 6 5 Figure 203. Visual Composition of the Card Panel Initialization ❑ Connect the initialize event of the panel to the message and display the welcome message. Connect the same event to the requestFocus method (expert) of the entry field (1). Draw the same connections from the componentShown event of the panel (2). ❑ Use the componentShown event to set the entry field to null (3). Chapter 17. Swing GUI for ATM Application 323 Processing ❑ Connect the Ok button to the message and display Please wait (4). ❑ Connect the Ok button to the getCard method of the controller with the card number entry field as a parameter (5). Connect the normalResult to the this property of the card variable (6). ❑ Connect the cardNotFound event of the controller to the message and display Invalid card (the same panel is displayed) (7). Next Panel ❑ Connect the cardFound event of the controller to the next method of the card layout with the main panel as parent parameter (8). ❑ Connect the Exit button to the first method of the card layout with the main panel as parent parameter (9). PIN Panel Figure 204 shows the visual composition of the PIN panel (PinPanel class). 2 1 8 1 6 5 4 2 3 Figure 204. Visual Composition of the PIN Panel 324 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 7 Initialization ❑ Use the componentShown event and set the message and the PIN entry field to null. Connect the same event to the requestFocus method of the entry field (1). ❑ Use the this event of the card to set the customer text (...title...first...last...), using the getGreetings method as a parameter value. Use the exceptionOccurred event to set the customer text to null. Use the this event of the card to display the card number, passing the cardid property of the card variable as the parameter (2). Processing ❑ Connect the Ok button to the checkPin method of the controller with the PIN entry field as parameter (3). ❑ Use the checkPinOk event of the controller to set the message to null (4). ❑ Use the checkPinNotOk event to set the PIN entry field to null (5). ❑ Use the checkPinNotOk event to set the error message as PIN invalid, please reenter (the same panel is displayed) (6). Next Panel ❑ Connect the checkPinOk event to the next method of the card layout (with main as parent) (7). ❑ Connect the Cancel button to the previous method of the card layout (with main as parent) (8). Chapter 17. Swing GUI for ATM Application 325 Select Account Panel Figure 205 shows the visual composition of the select account panel (SelectAccountPanel class). 1 6 8 11 4 2 4 9 10 12 5 7 13 3 7 Figure 205. Visual Composition of the Select Account Panel Additional Beans ❑ Drop an AccountImpl variable and name it SelectedAccount (1). Promote its this property as selectAccountThis. ❑ The list of accounts is displayed in a list box (JList) that is inside a scroll pane (JScrollPane) (2). ❑ Drop a VapDefaultListModel bean and connect its list property to the this property of the list box (3). By default, the accounts are displayed in the list box using the toString method. We only want to display the account ID. Open the VapDefaultListModel bean and set the useDisplayString property to true. Create a displayString method in the AccountImpl bean: public String displayString() { try { return this.getAccid(); } catch (Exception e) { return "Account invalid"; } } 326 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Initialization ❑ Use the componentShown event to set the two fields for the account and type to null (4). (These fields are entry fields set as noneditable.) ❑ Open the Ok button and set it to be disabled. Connect the componentShown event to the setEnabled method with false as parameter (5). ❑ Use the this event of the card to set the customer text (same as in PIN panel) (6). ❑ Connect the componentShown event to the removeAllElements and to the addElements method of the VapDefaultListModel. Construct the parameter vector for the addElements method using the getAccounts method of the card (7). This process fills the list model with the accounts stored in the card. Processing ❑ Connect the listSelectionEvents event of the list box to the this property of the selected account variable ( 8). Connect the parameter to the getElementAt(int) method of the VapDefaultListModel and pass the selectedIndex of the list box as parameters (9). This method extracts the account selected in the list box. Note: A simpler solution is to connect the selectedValue property of the list box to the this property of the selected account variable. Then open the connection and set the source event to listSelectionEvents. This solution uses only one connection instead of three connections. ❑ Connect the listSelectionEvents event of the list box to the setEnabled method of the Ok button with true as a parameter (10). ❑ Connect the this event of the selected account variable to the account text field with the accountID property as a parameter (11). Connect the same event to the type text field with the getAccountType method as a parameter. The selected account information is displayed in the two text fields. Next Panel ❑ Connect the Ok button to the next method of the card layout (with main as parent) (12). ❑ Connect the Cancel button to the previous method of the card layout (with main as parent) (13). Chapter 17. Swing GUI for ATM Application 327 Transaction Panel Figure 206 shows the visual composition of the transaction panel (TransactionPanel class). 4 5 1 7 4 14 11 2 3 6 11 10 13 4 14 15 12 9 8 Figure 206. Visual Composition of the Transaction Panel Additional Beans ❑ Drop an AccountImpl variable and name it BankAccount (1). Promote its this property as bankAccountThis. ❑ The list of transactions is displayed in a table (JTable) that is inside a scroll pane (JScrollPane) (2). ❑ Drop a VapDefaultTableModel bean and connect its table property to the this property of the table (3). Open the VapDefaultTableModel bean and set the columnIdentifiers property to the Transrecord class (full name itso.bank.persistence.model.Transrecord) and select the Transid, Transtype, and Transamt columns as noneditable for display. Initialization ❑ Use the componentShown event to set the message, amount entry field, and old balance text field (noneditable) to null (4). 328 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs ❑ Use the componentShown event to set the three fields, account number, account type, and new balance, to the values of the matching properties of the bank account ( 5). (Use the getAccountType method for the type field.) ❑ Connect the componentShown event to the setRowVector method of the VapDefaultTableModel bean and use the getTransactions method of the bank account as parameter to fill the account transaction table (6). ❑ Use the this event of the card to set the customer text (same as in PIN panel) (7). Processing ❑ Connect the Deposit button to the deposit method of the controller and pass the amount and the bank account as parameters (8). ❑ Connect the Withdraw button to the withdraw method of the controller and pass the amount and the bank account as parameters. ❑ Connect the History button to the getTransactions method of the controller and pass the bank account as parameters (9). Connect the button to the setRowVector method of the VapDefaultTableModel bean and use the getTransactions method of the bank account as parameter (10). This process refreshes the table with the account transactions. ❑ Connect the newTransaction event of the controller with the new balance field and pass the balance property of the bank account as a parameter; this displays the new balance. Connect the same event to the old balance field and use the oldBalance property as a parameter (11). ❑ Connect the newTransaction event of the controller with the setRowVector method of the VapDefaultTableModel bean and use the getTransactions method of the bank account as parameter (12). ❑ Use the newTransaction event to set the message to Account updated (13). ❑ Use the limitExceeded event of the controller to display the Limit exceeded - not enough funds available message. ❑ Use the DBOutOfSynch event of the controller to display the Account data has changed - verify and reenter transaction message. Connect the same event to the new balance field and pass the balance property of the bank account as a parameter (14). Next Panel ❑ Connect the Cancel button to the previous method of the card layout (with main as parent) (15). ❑ Connect the Exit button to the first method of the card layout (with main as parent). Chapter 17. Swing GUI for ATM Application 329 ATM Applet You create the ATM applet (ATMApplet class) as a subclass of the Swing JApplet class. You set the layout manager to CardLayout and drop five panels on the main applet’s panel: ❑ A JPanel from the palette. This is the starting panel where the user selects what persistence implementation to use for the applet. ❑ Four beans of type CardPanel, PinPanel, SelectAccountPanel, and TransactionPanel. This is the sequence of the application panels. Use the Beans List to make individual panels of the card layout visible in the applet. This is necessary for some of the connections. Figure 207 shows the visual composition of the ATM applet (ATMApplet class), with the additional persistence panel displayed. 5 5 5 5 8 4 7 6 10 3 2 Figure 207. Visual Composition of the ATM Applet 330 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 9 1 Additional Beans ❑ Drop an ATMApplicationController bean (1) and a Card variable (2). ❑ Tear off the layout property of the applet’s main panel (3). Change its type to CardLayout and name it CardLayout. ❑ Tear off the selectedAccountThis property from the SelectAccountPanel (4). Initialization and Termination ❑ Connect the this property of the applet panel to the mainThis property of each of the four subpanels (5). This sets the Main variable on all panels. ❑ Connect the this property of the Card variable to the cardThis property of each of the four subpanels (6). This sets the Card variable on all panels. ❑ Connect the this property of the ATM application controller bean to the controllerThis property of each of the four subpanels (7). This sets the Controller variable on all panels. ❑ Connect the this property of the selectedAccountThis to the bankAccountThis property of the TransactionPanel (8). This passes the selected bank account from the SelectAccountPanel to the TransactionPanel. ❑ Connect the destroy event of the applet to the disconnect method of the controller (9). Next Panel ❑ Connect the Start button to the next method of the card layout and pass the applet’s main panel as parent parameter (10). Run the ATM GUI Applet Before running the ATM GUI applet, make sure to check the class path for the ATMApplet class through the Run -> Check class path pop-up. The JFC class libraries, VisualAge Persistence, and VisualAge Persistence Common Runtime projects must be included in the class path. Also check that DB2 is started, including the Java daemon process if you used the net driver when generating the service classes (“Generate the Service Classes” on page 299): db2jstrt 8888 Chapter 17. Swing GUI for ATM Application 331 332 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 18 ATM Application Using Servlets In this chapter we implement all of the user interfaces of the ATM application with servlets. The servlets interact with the ATM application controller designed in Chapter 16, “ATM Application Business Model and Controller” and will work with the underlying Persistence Builder. We basically use the design implemented in the redbook VisualAge for Java Enterprise Version 2: Data Access Beans - Servlets - CICS Connector. The only changes required are the exchange of the business model classes with the classes generated by the persistence builder, and the use of the new application controller. We implement the user interface with five visual servlets and one nonvisual controller servlet that controls the flow of the application: ❑ ❑ ❑ ❑ ❑ ❑ Card servlet (CardView class) PIN servlet (PinView class) Account servlet (AccountView class) Transaction servlet (TransactionView class) Thank you servlet (ThankYouView class) ATM controller servlet (ATMServletController class) © Copyright IBM Corp. 2000 333 We use the servlet builder to create all six servlets as subclasses of the VisualServlet. For the five visual servlets we use the visual composition editor of the servlet builder to compose the resulting HTML page. The controller servlet is a router servlet, it does not generate an HTML page. We use the visual composition editor to build the interface between the servlet controller and the ATM application controller, and to set up which of the other servlets are invoked to generate the next HTML page for the browser. Create a Skeleton Controller Servlet The application flow is handled by the controller servlet, that is, all other servlets invoke the controller through the action specification of the form. The controller contains most of the business logic and decides which servlet to invoke next. To facilitate development of the view servlets, it is good to have a skeleton controller from the beginning. Let’s develop the skeleton controller first. Create a new package named itso.bank.persistence.servlet. Create a visual servlet, named ATMServletController, using QuickStart -> Create Servlet. Delete the HTML page from the visual composition and save the servlet. Your skeleton controller servlet is in place. Servlet Views Now let’s design the five views that represent the ATM application. Card Servlet When a user requests to start the ATM application, the card servlet displays a form for the customer to enter the ATM card number. This simulates the action of sliding the ATM card through a reader and is similar to today’s home banking systems (Figure 208). 334 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 208. Card Servlet View The card servlet has an image, a welcome text, and a form with an entry field, push buttons, and a message field. As an image we use the ITSO banner that is displayed on the home page of the International Technical Support Organization (point your browser to http://www.redbooks.ibm.com or http://w3.itso.ibm.com). Let’s construct the card servlet named CardView. We do not describe all of the steps to visually create a servlet. The purpose of the card servlet is to get the card number. We minimize the logic in this servlet to keep it simple. The development steps are: ❑ GUI layout ❑ Auxiliary properties ❑ Auxiliary method ❑ Connections GUI Figure 209 shows the design of the card servlet. We use a form and set its action property to the ATMApplicationController. The HTML image bean has a source property that can point to a URL (use your own favorite URL) or a local file. Only with a local file can you see the image at design time. Chapter 18. ATM Application Using Servlets 335 1 3 2 Figure 209. Card Servlet Design Table 35 shows the GUI beans. This table does not represent all of the GUI elements; only a subset is listed with the important properties. We also use a table, paragraphs, and a rule. You can arrange the beans yourself. Table 35. GUI Beans in Card Servlet Type Bean Name Property Property Value HTMLImage logo source /itso/image/itso.gif HTMLForm form action ATMServletController HTMLEntryField cardId HTMLText message string ..message.. HTMLText server string ..server.. HTMLPushButton okButton string Ok HTMLPushButton exitButton string Exit HTMLTable 336 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Auxiliary Properties Define a messageText property (String) on the BeanInfo page. This property will be connected to the message field in the GUI to set an error message. The logo image is hard-coded as source property of HTMLImge. Some of you might not like such a hard-coded reference. But think again, when you create an HTML file, you write a source URL directly, don’t you? However, it is possible to set this URL from the controller. We promote the source property of the logo image as logoImageURL. Auxiliary Method We define a serverName method that returns the HTTP server’s IP address: public String serverName() { try {return java.net.InetAddress.getLocalHost().getHostAddress(); } catch (java.net.UnknownHostException e) { return null; } } Connections We use connections to set up the server name. ❑ Connect the aboutToGenerateOrTransfer event (of the servlet) to the serverName method, using an event-to-code connection (1). ❑ Connect the normalResult to the string of the server field (2). ❑ Connect the messageText property to the string of the message field (3). Chapter 18. ATM Application Using Servlets 337 PIN Servlet The PIN servlet (PinView class) is invoked by the controller servlet and asks the user to enter the PIN associated with the ATM card (Figure 210). Figure 210. PIN Servlet View The user name consists of title, first name, and last name. This user-unique data is defined in the customer class of the business model, and the customer class is accessed through the card class (see “Business Object Layer” on page 284). We keep the card object in a SessionDataWrapper Bean to have it available in all subsequent servlets. This view uses a number of GUI beans and connections to extract data for the GUI, but it has no business logic. The development steps are: ❑ GUI layout ❑ Session data for user-unique information ❑ Auxiliary property ❑ Connections GUI Figure 211 shows the design of the PIN servlet, and Table 36 shows the major GUI beans and their properties. 338 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 4 1 5 5 2 3 6 Figure 211. PIN Servlet Design Table 36. GUI Beans in PIN Servlet Type Bean Name Property Property Value HTMLImage logo source /itso/image/itso.gif HTMLText custTitle HTMLText custFirst HTMLText custLast HTMLText message string ..message.. HTMLForm form action ATMServletController HTMLText cardId string ..cardid.. HTMLEntryField pin passwordProtected true HTMLPushButton okButton string Ok HTMLPushButton cancelButton string Cancel Chapter 18. ATM Application Using Servlets 339 Notice that we protect the PIN code of the user through the passwordProtected property of the entry field. Session Data We use session data to keep user-unique data. Every servlet session is separate for each user’s HTTP session. The servlet handles multiple users, so we must keep the data of multiple users for the next servlet interaction, and we have to control the lifetime of the data. To keep it simple, we decide to keep the data as session data, which, as we learned, is pointed to by a cookie that is generated automatically. If you do not want to use a cookie, you should create a thread that controls all user-unique data, return the data by request from the servlet, and purge the data at some point in time. Our servlet lifetime is quite short, only as long as one HTTP session. The SessionDataWrapper contains the card object. We will implement this later in the construction of the controller servlet. For now we can just extract a card object from the SessionDataWrapper to get the card ID and the customer object. ❑ Drop a SessionDataWrapper bean on the free-form surface, name it CardData, and name its propertyName property card ( 1). The propertyName is important to access the same data. ❑ Tear off the propertyValue to extract a card object. Change the type of the variable to the Card class of the itso.bank.persistence.model package (ignore the warning message) and name the variable Card (2). ❑ Tear off the customer property from the Card variable to extract a customer object. Name the variable Customer (3). Open the connection and set the target event to <none>. Auxiliary Property Define a messageText property on the BeanInfo page. This property will be connected to the message field in the GUI to set an error message. Promote the source property of the logo image as logoImageURL. Connections We set up the title, first name, last name, and card ID before generating the HTML. We also clear the PIN entry field. ❑ Use the aboutToGenerateOrTransfer event to set a title text string and pass the title property of the Customer object as a parameter (4). Set up the first name and last name in the same way. 340 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs ❑ Set up the card ID from the cardNumber property of the Card variable, and clear the PIN entry field, using similar connections from the aboutToGenerateOrTransfer event (5). ❑ Connect the messageText property to the string of the message field (6). Account Servlet After PIN validation, the user can choose one of the accounts. The account servlet (AccountView class) displays the customer’s title and name, and a list of the accounts. The customer’s name comes from the session data bean (Figure 212). When an account is selected, the account ID and the account type (savings or checking) are displayed. We implement this with a JavaScript that runs on the client. The account ID and type are displayed in a drop-down list instead of a text field because the selected list item can be changed by the JavaScript without regenerating the HTML. Figure 212. Account Servlet View This view is composed of beans, connections, and some logic to extract the account data into two arrays of strings to fit into an HTML list bean. The steps to complete this view are: Chapter 18. ATM Application Using Servlets 341 ❑ ❑ ❑ ❑ ❑ GUI layout Session data for user-unique information Auxiliary property and methods to extract account data JavaScript for dynamic account selection Connections GUI Figure 213 shows the design of the account servlet, and Table 37 shows the major GUI beans and their properties. 3 1 5 4 6 7 2 Figure 213. Account Servlet Design Table 37. GUI Beans in Account Servlet 342 Type Bean Name Property Property Value HTMLImage logo source /itso/....../itso.gif HTMLText custTitle HTMLText custFirst HTMLText custLast HTMLForm form action ATMController Servlet VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Type Bean Name Property Property Value HTMLList AccountList visibleItemCount 4 HTMLDropDownList AccountID visibleItemCount 1 HTMLDropDownList AccountType visibleItemCount 1 HTMLPushButton okButton string Ok HTMLPushButton cancelButton string Cancel HTMLScript SelectionScript string <see below> Session Data We use the session data in the same way as for the PIN servlet. Place a SessionDataWrapper (with propertyName card), tear off the propertyValue (as type Card). Tear off the customer property from the Card (1) and open the connection and set the target event to <none>. Auxiliary Property and Methods for List of Accounts We promote the source property of the logo image as logoImageURL. The card object contains the list of accounts. The card object has a vector of account objects and each account object contains the account ID and the account type. We write two methods to extract the account IDs and account types into an array of strings: private String[] fillAccountIDList(java.util.Vector accounts) { String[] result = new String[accounts.size()]; for (int i=0; i<accounts.size(); i++) try { result[i] = new String ( ((itso.bank.persistence.model.Account)accounts.elementAt(i)).getAccid() ); } catch (Exception e) { result[i] = new String("Unknown account"); } return result; } private String[] fillAccountTypeList(java.util.Vector accounts) { String[] result = new String[accounts.size()]; for (int i=0; i<accounts.size(); i++) try { result[i] = new String ( ((itso.bank.persistence.model.Account)accounts.elementAt(i)).getAccountType() ); } catch (Exception e) { result[i] = new String("Unknown type"); } return result; } JavaScript We implement a JavaScript (2) to display the selected account ID and type when the user selects an account from the list of accounts. We allow selection of the account in either of the three lists and adjust the other two lists. You write the JavaScript code in the string property of the HTMLScript bean that Chapter 18. ATM Application Using Servlets 343 we named SelectionScript (below the push buttons). Define a function for each of the three lists. Each function accepts a parameter, the name of the form that contains the list. function selectAccountListFunc(s){ s.AccountID.options[s.AccountID.selectedIndex].selected =true; s.AccountType.options[s.AccountID.selectedIndex].selected =true; } function selectAccountIDFunc(s){ s.AccountList.options[s.AccountList.selectedIndex].selected =true; s.AccountType.options[s.AccountList.selectedIndex].selected =true; } function selectAccountTypeFunc(s){ s.AccountList.options[s.AccountType.selectedIndex].selected =true; s.AccountID.options[s.AccountType.selectedIndex].selected =true; } Each function changes the selection in the two other lists to the selected index of the originating list box. To invoke this function, add an event handler to each of the lists. Edit the extraAttributes property of each list to one line of code: onChange=selectAccountListFunc(form) onChange=selectAccountIDFunc(form) onChange=selectAccountTypeFunc(form) <== in AccountList <== in AccountID <== in AccountType This code will be written to the list tag of the generated HTML: <LIST ...... onChange=selectAccountXxxxFunc(form) ...> The JavaScript now contains three functions, one for each of the three lists. A selection in either list triggers the other two to be positioned on the matching item. Note that the names in the script must match the names of the GUI beans defined in Table 37. Connections We set up the customer and account information before generating HTML: ❑ Connect the aboutToGenerateOrTransfer event to extract the customer information (same as in PIN servlet) (3). ❑ Connect the aboutToGenerateOrTransfer event to the fillAccountIDList method (event-to-code) and pass the accounts property of the Card variable as a parameter (4). Connect the normalResult to the items property of both the accountList and accountID bean (5). ❑ Connect the aboutToGenerateOrTransfer event to the fillAccountTypeList method (event-to-code) and pass the accounts property of the Card variable as parameter (6). Connect the normalResult to the items property of the accountType bean (7). 344 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Transaction Servlet The transaction servlet (TransactionView class) provides the main function of the ATM application. The transaction servlet handles deposit, withdraw, and query operations. A bank account object created by the model contains the account ID, account type, current balance, and transaction history. The bank account object is not saved as session data but accessed as a property of the servlet that is set by the controller servlet before invoking the transaction servlet. Each deposit, withdraw, and query operation invokes the controller servlet for processing, and the updated bank account object is assigned to the transaction servlet. The Cancel button returns to the account selection servlet and the Exit button terminates the dialog with the thank you servlet (Figure 214). Figure 214. Transaction Servlet View Chapter 18. ATM Application Using Servlets 345 This view is composed of beans and connections. The transaction list is displayed in an HTML table that is filled through the default table model bean of the Persistence Builder. The steps to complete this view are: ❑ GUI layout ❑ Session data for user-unique information ❑ Connections GUI Figure 215 shows the design of the transaction servlet, and Table 38 shows the major GUI beans and their properties. 3 6 2 5 4 8 11 10 1 Figure 215. Transaction Servlet Design 346 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 7 9 Table 38. GUI Beans in Transaction Servlet Type Bean Name Property Property Value HtmlImage logo source /itso/image/itso.gif HtmlText custTitle string ..title.. HtmlText custFirst string ..first.. HtmlText custLast string ..last.. HtmlForm form action ATMServletController HtmlText accountID string ..id.. HtmlText accountType string ..type.. HtmlText oldBalance string ..oldBal.. HtmlText newBalance string ..newBal.. HtmlEntryField amount size 8 HtmlResultTable TransactionList HtmlText message string ..message.. HtmlPushButton depositButton string Deposit HtmlPushButton withdrawButton string Withdraw HtmlPushButton historyButton string History HtmlPushButton cancelButton string Cancel HtmlPushButton exitButton string Exit HtmlHiddenInput accountIdHidden The account ID is not stored in a live object. The account ID is used only in the transaction servlet and for processing in the controller servlet. As you know, the card object has a relationship to all the accounts, but it does not record which account the user selected. We use a hidden field to keep the account ID (1). The controller servlet gets the (hidden) account ID from the form. Of course we could use a cookie or add a property in the Card class, but we do not want to modify the business model. Chapter 18. ATM Application Using Servlets 347 Session Data We use the session data in the same way as for the PIN servlet. Place a SessionDataWrapper (with propertyName card), tear off the propertyValue (as type Card). Tear off the customer property from the Card (2 and open the connection and set the target event to <none>. Auxiliary Properties Define a messageText property on the BeanInfo page. This property will be connected to the message field in the GUI to set an error message. Promote the source property of the logo image as logoImageURL. We define a bankAccount property of type Account. (The Account class is in the itso.bank.persistence.model package.) Connections ❑ Connect the aboutToGenerateOrTransfer event to extract the customer information (same as in PIN and account servlets) (3). ❑ Place a variable on the free-form surface, name it BankAccount, and change the type to itso.bank.persistence.model.Account (4). This is the current account. Connect the bankAccount property of the servlet to the this of the variable (5). ❑ Extract account ID, account type, old balance, and new balance from the bank account variable using the aboutToGenerateOrTransfer event (6). ❑ The transaction history is displayed in an HtmlResultTable. Drop a VapDefaultTableModel on the free-form surface (7) and connect its this property to the tableModel property of the HtmlResultTable (8). Open the VapDefaultTableModel and set the columnIdentifiers property to the itso.bank.persistence.model.TransrecordImpl class and select the columns Transid, Transtype, and Transamt as noneditable. ❑ To fill the history table, connect the aboutToGenerateOrTransfer event to the setRowVector method of the VapDefaultTableModel and pass the transactions property of the bank account as parameter (9). ❑ To store the account ID for the next transaction process, connect the aboutToGenerateOrTransfer event to the hidden field with the accountid of the bank account as a parameter (10). ❑ Connect the messageText property to the string of the message field (11). 348 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Thank You Servlet The thank you servlet (ThankYouView class) is displayed at the end of an ATM session (Figure 216). This servlet runs when the user clicks on the Exit button in the card or transaction servlet. The Restart Transaction button tells the controller servlet to start a new session with the card servlet. Figure 216. Thank You Servlet View The design of this servlet is so simple that we leave it to you to complete. Do not forget to set the action property in the form! Chapter 18. ATM Application Using Servlets 349 Application Flow Design In this section we describe the general flow of the ATM application. In “Implement the Controller Servlet” on page 352 we construct the controller servlet based on this design. The controller servlet has two functions: to control the sequence of the servlet views and to act as the gateway to the application controller layer (see “Application Controller” on page 310) that interfaces with the business model (Figure 217). View Layer (Servlets) Controller Controller Servlet Card Servlet ATM Application Controller FormData Pin Servlet Account Servlet Transaction Servlet FormData SessionData FormData FormData Business Model Classes Card Customer Account Business Object Layer Figure 217. Servlet Application Flow 350 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Keep the ATM Application Controller Alive Servlets are instantiated when invoked and dismissed after processing. However, we want to keep the ATM application controller object alive because it has database or enterprise connections. Therefore we instantiate the application controller when the first user invokes the controller servlet. We store the ATMApplicationController object in a static variable. Flow Control Each servlet passes control to the controller servlet through the action in the form. All processing is done in the controller servlet that decides to which servlet to pass control to display a user view. Customer Verification The entry point of the application is the card servlet. The controller servlet requests a card object from the ATM application controller, which generates a cardFound or cardNotFound event. A valid card is stored in session data, and the PIN servlet is invoked, for an invalid card the card servlet displays an error message. PIN Verification The PIN servlet passes the PIN to the controller servlet. The PIN is validated against the card object (no database access required). For a correct PIN the account servlet is invoked; for an invalid PIN the PIN servlet displays an error message. Selecting an Account The account servlet passes the selected account to the controller servlet, which retrieves the bank account object and invokes the transaction servlet. Processing a Transaction The account information is available in the bank account object. Each deposit, withdraw, or history transaction is processed by the controller servlet and an updated account object is sent back to the transaction servlet. The Cancel button invokes the account servlet to select another account, and the Exit button invokes the thank you servlet. Exit the ATM Application The controller servlet handles the exit request from the card and transaction servlets and invokes the thank you servlet. The Restart Transaction button from the thank you servlet invokes the card servlet. Chapter 18. ATM Application Using Servlets 351 Implement the Controller Servlet It is time to implement the controller servlet (ATMServletController class) with the processing and flow logic. The controller servlet is a kind of connector with visual composition and business logic. Controller Servlet Total Design Figure 218 shows the total design of the controller servlet in the Visual Composition Editor. Figure 218. Controller Servlet Total Design The dashed lines highlight the five servlets as they are invoked in sequence by the controller servlet using the events of the ATM application controller. 352 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs We implement the function in small and understandable pieces: ❑ ❑ ❑ ❑ ❑ ❑ ❑ ❑ ❑ ❑ Preparation for testing Initialization Customer verification PIN verification Account selection Deposit transaction Withdraw transaction Query transaction history Termination and restart Disable caching of the output HTML Preparation for Testing The servlets use the ATM application controller for processing. The ATM application controller must initialize the underlying datastore. For the database test, make sure that: ❑ DB2 is started ❑ The DB2 Java daemon is started (db2jstrt 8888), if you used the DB2 JDBC net driver. Initialization To keep the ATMApplicationController alive for a whole session, we use a static field and assign it to a variable using visual composition. ❑ Use import statements to allow short class names and create a static field named applicationController of type itso.bank.persistence.model.ATMApplicationController: import itso.bank.persistence.model.*; import com.ibm.vap.Transactions.*; private static ATMApplicationController applicationController = null; ❑ Create a getApplicationController method to initialize the application controller and the transaction binding policy for multithreaded servlets: public ATMApplicationController getApplicationController() { if (applicationController == null) { applicationController = new ATMApplicationController(); setTransferToServiceHandler(getCardView()); // default start servlet // set transaction to thread binding policy Transaction.setBindingPolicy(new TransactionToThreadBindingPolicy()); } // activate the read-only transaction applicationController.transactionResume(); return applicationController; } Chapter 18. ATM Application Using Servlets 353 The setTransferToServiceHandler method specifies the card servlet as the default target. We can start the application by invoking the controller servlet. ❑ Go to the Visual Composition page and add a variable of type ATMApplicationController (1). ❑ Connect the initialize event (of the servlet) to the this property of the controller variable and get the parameter from the getApplicationController method (parameter-from-code) (2). ❑ To indicate that the controller servlet does not generate HTML, connect the initialize event (of the servlet) to the isTransferring property with a parameter value of true (3). Figure 219 shows the visual composition of the initialization phase. 3 2 1 Figure 219. Initializing the Controller Servlet Customer Verification Figure 220 shows the customer verification implementation. The steps to implement the customer verification are: ❑ Add a CardViewFormData bean to receive the data and event from the card servlet (1). ❑ Add a SessionDataWrapper bean, name it CardData, with card as the property name (2). We will save the card object in session data for subsequent servlets. ❑ Add the two target servlets, CardView and PinView, as beans (3). 354 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 1 3 6 8 7 3 4 5 2 Figure 220. Customer Verification ❑ The starting point for processing is the Ok button in the card servlet. Connect the okButtonPressed event (in CardViewFormData) to the getCard method of the ATMApplicationController. Pass the cardIdString (of CardViewFormData) as a parameter (4). Do the same for the enterKeyPressed event. ❑ The getCard method retrieves the card from the database. Connect the normalResult to the propertyValue in the CardData session data bean (5). ❑ The ATMApplicationController fires a cardFound or cardNotFound event after retrieving the card. We use these events to invoke the next servlet. • For a valid card we invoke the PIN servlet. Connect the cardFound event to the transferToServiceHandler property of the controller servlet and pass the this of PinView as a parameter (6). • For an invalid card we prepare an error message and invoke the card servlet. Connect the cardNotFound event to the messageString property of CardView and set the parameter to The card number is invalid (7). Connect the cardNotFound event to the transferToServiceHandler property of the controller servlet and pass the this of CardView as a parameter (8). ❑ We will handle the Exit button event later. Save the controller servlet and run it. When the card servlet displays the greeting, enter a valid card number and click on Ok. The PIN servlet should get control and display the next form. Chapter 18. ATM Application Using Servlets 355 PIN Verification Figure 221 shows the PIN verification connections. 6 7 8 3 2 1 5 4 Figure 221. PIN Verification The steps to implement the PIN verification are: ❑ Tear off the propertyValue of CardData (session data) and name it Card (1). Change the type to itso.bank.persistence.model.Card (ignore the warning). ❑ Add a PinViewFormData bean to receive the PIN entered by the user (2). ❑ The PIN is checked when the user clicks on the Ok button or presses the Enter key. Connect the okButtonPressed event (of PinViewFormData) to the checkPin method of the application controller (3). Two parameters are required, the this of the Card, and the pinString of the PinViewFormData. Create the same connections for the enterKeyPressed event. ❑ After checking a PIN, the application controller fires an event, either pinCheckedOk and pinCheckedNotOk. 356 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs • If the PIN is correct, we pass control to the account servlet. Add a bean of type AccountView (4). Connect the pinCheckedOk event to the transferToServiceHandler method and pass the this of AccountView as a parameter (5). • If the PIN is incorrect, we set an error message and return to the pin servlet. Connect the pinCheckedNotOk event (of the application controller) to the messageText property of PinView with the text PIN invalid, please reenter! (6) Connect the pinCheckedNotOk to the transferToServiceHandler method with the this of PinView as a parameter (7). ❑ When the Cancel button is clicked (in PinView) we return to the card servlet. Connect the cancelButtonPressed event to the transferToServiceHandler method with the this of CardView as a parameter (8). Account Selection Figure 222 shows the account selection connections. The steps to implement account selection are: ❑ Add an AccountViewFormData and a TransactionView bean to the freeform surface (1). ❑ The TransactionView requires a bank account object that contains the balance and the transactions. The getAccount method of the application controller returns a bank account based on the account ID. • Connect the okButtonPressed (and same for enterKeyPressed) event of the AccountViewFormData to the getAccount method of the card. Pass the accountIDSelectedItemString property of AccountViewFormData as a parameter; this is the selected item in the account ID list (2). • The result of the connection is the bank account object. Connect the normalResult of the connections to the bankAccount property of the TransactionView (3). • Connect the okButtonPressed and enterKeyPressed events to the transferToServiceHandler method with the this of TransactionView as a parameter (4). ❑ Connect the cancelButtonPressed event to the transferToServiceHandler method with the this of PinView as a parameter (5). Chapter 18. ATM Application Using Servlets 357 2 3 1 1 4 5 Figure 222. Account Selection Deposit Transaction Figure 223 shows the connections for the deposit transaction. The transaction is applied to the bank account, and the account servlet is invoked again with the updated account object. 358 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 7 2 4 1 3 5 6 Figure 223. Deposit Transaction ❑ Add a TransactionViewFormData bean to the free-form surface (1). ❑ The Deposit button invokes the deposit transaction. Connect the depositButtonPressed event (of TransactionViewFormData) with the deposit method of the application controller to update the bank account (2). The deposit method requires two parameters: a bank account and an amount: • The bank account we get from the card. Connect the account parameter to the getAccount method of the card and with the accountIDhiddenString property of TransactionViewFormdata as a parameter (3). (We saved the account ID as hidden data in the form.) • We get the amount from the amountString property of the form (4). ❑ The result of the deposit method is the updated bank account. Connect the normalResult to the bankAccount property of the TransactionView (5). ❑ The application controller fires a newTransaction event when the deposit is complete. Connect the newTransaction event to the messageText property of the TransactionView with the text Transaction complete, account updated ( 6). Then connect the newTransaction event to the transferToServiceHandler method with the this of TransactionView as a parameter (7). Chapter 18. ATM Application Using Servlets 359 Withdraw Transaction Figure 224 shows the connections for the withdraw transaction. Withdraw is similar to deposit but may fire a limit exceeded event if not enough funds are available. 3 2 1 3 2 Figure 224. Withdraw Transaction ❑ The withdraw transaction is implemented with connections that match the deposit transaction; connect the withdrawButtonPressed event to the withdraw method of the application controller and use the same two parameters (account as getAccount of Card and amount as amountString of the form). Connect the normalResult to the bankAccount property of the TransactionView (1). ❑ A complete withdrawal transaction fires the new transaction event. We have already handled that in the deposit transaction. ❑ To handle the case of not enough funds, connect the limitExceeded event (of the application controller) with the messageText property of the TransactionView and set the text to Withdraw failed, not enough funds. Then connect the limitExceeded event to the transferToServiceHandler method with the this of TransactionView as a parameter (2). ❑ The application controller may fire a DBOutOfSynch event if the account in the database does not match the account object. Deposit or withdraw transactions are not performed. Connect the DBOutOfSynch event to a suitable messageText in the TransactionView and to the transferToServiceHandler method with the this of TransactionView as a parameter (3). 360 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Query Transaction History Figure 225 shows the connections for the transaction history that retrieves all transaction objects of an account for display. The initial list of transactions displays current transactions only. 3 1 2 4 Figure 225. Query Transaction History ❑ Connect the historyButtonPressed event to the getTransactions method of the application controller (1). ❑ The parameter of this connection is the bank account that we get in the same way as for deposit and withdraw (use getAccount method in card) (2). Connect the normalResult to the bankAccount property of the TransactionView. ❑ No event is fired by the application controller. Connect the normalResult of the getTransactions method to the transferToServiceHandler method with the this of TransactionView as a parameter (3). Cancel ❑ Connect the cancelButtonPressed event of the TransactionViewDataForm to the transferToServiceHandler method with the this of AccountView as a parameter (4). Chapter 18. ATM Application Using Servlets 361 Termination and Restart Figure 226 shows the exit and restart connections. Here we handle the Exit button in the card and transaction servlet to invoke the thank you servlet, and we schedule the card servlet for the Restart button in the thank you servlet. We also have to make sure that the event listeners of the servlet are removed from the application controller because each user interaction creates a new instance of the servlet. 2 5 4 3 1 3 2 Figure 226. Termination and Restart ❑ Add a ThankYouView bean to the free-form surface (1). ❑ Connect the exitButtonPressed event of the CardViewFormData and of the TransactionViewFormData to the transferToServiceHandler method with the this of ThankyouView as a parameter (2). Connect the normalResult of both connections to the CardData (session data) and set the propertyValue to null. This removes the card from the session data object (3). ❑ Add the ThankYouViewFormData bean to receive the Restart button event and connect the restartButtonPressed and enterkeyPressed events to 362 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs the transferToServiceHandler method with the this of CardView as a parameter to restart the ATM application (4). ❑ Add a connection from the aboutToGenerateOrTransfer event of the servlet to the this property of the ATMApplicationController and set the value to null (5). This connection removes all event listeners that the controller servlet has set up with the application controller. (We found that without this connection all previous instances of the controller servlet received the events of the current interaction.) Disable Caching of the Output HTML When testing the ATM servlets we found that sometimes wrong accounts were displayed for an ATM card, or that entering a PIN number of a previously displayed card worked for another card ID. Analysis proved that bad HTML pages were displayed to the end user because they were cached from previous requests. The solution is to disable the caching of the output HTML pages. Instead of disabling caching in each of the servlets, you can add the required code to the initialize method of the controller servlet. We also found that when invoking the ATMServletController in the middle of a session without any parameters, a null pointer exception occurred because no target servlet was set up with the setTransferToServiceHandler method. The easiest solution for this problem is to set up the card servlet as the default transfer servlet. Figure 227 shows the tailored initialize method of the controller servlet. Note that the connection method numbers (connEtoMx) may be different in your implementation. Chapter 18. ATM Application Using Servlets 363 private void initialize() { // user code begin {1} // user code end initConnections(); connEtoM1(); // setIsTransferring(true) connEtoM3(); // setATMApplicationController(...) // user code begin {2} setTransferToServiceHandler(getCardView()); // default target servlet // get response object javax.servlet.http.HttpServletResponse resp = getResponse(); resp.setContentType("text/html"); resp.setHeader("Pragma","no-cache"); // no caching resp.setHeader("Cache-Control","no-cache"); resp.setDateHeader("Expires",0); // cache expires // user code end } Figure 227. Disabling Caching for the ATM Servlets Test the ATM Servlet Application Confirm that the functions of the system service layer, such as DB2, are properly configured and started. Select the controller servlet (ATMServletController) and click on the Run button in the tool bar. VisualAge for Java starts the internal HTTP server, and a Web browser (defined in the Window->Options->Help menu) that invokes the selected servlet. The card servlet should display the first user interface. You can also select the card servlet (CardView) as the starting point. The browser points to the URL: http://127.0.0.1:8080/servlet/itso.bank.persistence.servlet.ATMServletController Test with the WebSphere Application Server VisualAge for Java 3.0 contains the WebSphere Application Server. When testing servlets WebSphere is started within VisualAge for Java. WebSphere can be started in two ways: ❑ Use the Run button for the servlet. This action starts the HTTP server and the browser pointing to the selected servlet. 364 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs ❑ Start the WebSphere test environment as described in “Starting WebSphere” on page 13. When WebSphere is ready, launch the servlet by selecting Selected -> Tools -> Servlet Launcher -> Launch. Watch the Console window to see the messages of the HTTP server. Test with the Servlet Runner HTTP Server The default HTTP server comes from the Sun JSDK and is known as the Servlet Runner. The Servlet Runner works as a servlet run-time engine and not really as a Web server. To use the Servlet Runner, change the configuration.properties file (in \IBMVJava\ide\project_resources\IBM Servlet IDE Utility class library\com\ibm\ivj\servlet\runner) by removing the comment from the line: #serverClassName=com.ibm.ivj.servlet.runner.JsdkServletRunnerStarter serverClassName=com.ibm.ivj.servlet.runner.JsdkServletRunnerStarter <=== before <=== after WebSphere Application Server or Servlet Runner? The Servlet Runner provides a much faster test environment, but has certain limitations: ❑ WebSphere serves files, Servlet Runner does not ❑ WebSphere handles JSP, Servlet Runner does not We suggest that you use the fast test environment of Servlet Runner to test the logic of your servlets, and use the WebSphere Application Server for comprehensive testing of a whole application solution. Once the WebSphere test environment has been started, the performance difference is not significant, but the startup time is quite long. Deploy Servlets The target for deploying servlets is a Web server. You can use Lotus Domino Go Webserver, Netscape Application Server, Apache, and many others; they all support servlets. We used WebSphere Application Server Version 2.0 and Version 3.0 with the IBM HTTP Server on Windows NT. For detailed information on deploying the servlets see “Deployment of Servlets” on page 414. Chapter 18. ATM Application Using Servlets 365 366 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 19 ATM Application Concurrent Processing In this chapter, we describe how the ATM application must be modified to support concurrent processing from multiple client environments. Potential problems in the ATM application are encountered when we run multiple persistent data stores on the same DB2 database. When accounts are updated in deposit or withdraw transactions, the new balance is calculated from the current balance in the memory cache with the amount added or subtracted. However, the balance could have been updated in the meantime by a transaction running in another data store. In the sections that follow, we describe different approaches to solve the problem. © Copyright IBM Corp. 2000 367 When Does the Problem Occur? We have two front-ends for the ATM application: a GUI and HTML with servlets. We can run: ❑ Multiple HTML clients connecting to a WebSphere server ❑ Multiple HTML clients connecting to a series of WebSphere servers with a load balancing distributor ❑ Multiple GUI clients on one machine ❑ Multiple GUI clients on separate machines ❑ A mix of GUI and HTML clients, on one or multiple machines Note: When running with multiple GUI clients on separate machines or with multiple WebSphere servers, we have to use the DB2 JDBC net driver to connect to a single machine with the DB2 database. The problem of concurrent updates can occur in all of the above configurations. Clients may see and update old account information, unless we improve the ATM application. How to Force the Problem? The easiest way to force the problem is to open two ATM GUI applications and to work on the same account. When each GUI displays the account information, run a deposit in one GUI, and then a deposit in the second GUI. The second GUI uses its owned cached account information to update the balance and wipes out the transaction performed in the first GUI. This is not acceptable. How to Solve the Problem? We looked at three ways to solve the problem: ❑ Retrieve the latest account information and compare with the cached object before updating ❑ Pessimistic locking to lock account objects ❑ Optimistic predicate for the balance of the account (verify the current balance in the update SQL statement) 368 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Retrieve and Compare Account Information To retrieve the current account information before updating the account through a deposit or withdraw transaction we change the code in the application controller for both the deposit and withdraw methods. Deposit Method public Account deposit(Account account, String amount) { try { checkAccountAgainstDB(account); updateTx = readonlyTx.beginChild(); account.deposit(amount); Transrecord trec = createTransrecord(account,"D",amount); account.addTransaction("+"+trec.toString()); updateTx.commit(); readonlyTx.resume(); fireHandleNewTransaction(new NewTransactionEvent(this)); } catch (Exception e) { .... } return account; } Withdraw Method public Account withdraw(Account account, String amount) { try { checkAccountAgainstDB(account); updateTx = readonlyTx.beginChild(); ... checkAccountAgainstDB Method In this new method we refresh the account object from the DB2 database and compare the cached balance with the latest balance. If they are not the same we inform the user and display the new account information. Here is the code: private void checkAccountAgainstDB(Account account) throws java.rmi.RemoteException, javax.ejb.FinderException, Exception { java.math.BigDecimal bal1 = account.getBalance(); ((AccountImpl)account).refresh(); java.math.BigDecimal bal2 = account.getBalance(); if ( !bal1.equals(bal2) ) { System.out.println("Account balance mismatch mem/db=" +bal1+" "+bal2); throw new Exception(); } } Chapter 19. ATM Application Concurrent Processing 369 In Version 3 the implementation object provides a refresh method to retrieve the latest data from the database table. In Version 2 the code was more complicated: ((AccountImpl)account).getBom().getVersionForRead().refresh( <currentTransaction>.getSessionOn( dataStore ) ); If we find a mismatch between the two values of the balance we throw an exception. We already have the code in the deposit and withdraw methods to intercept exceptions and generated a DBOutOfSynch event that can be used to inform the user about database problems. Note that such an implementation still leaves a small window between checking the balance and the update where another process could change the balance in the database. Pessimistic Locking of Accounts In the pessimistic approach account objects are locked before the update and the latest information is automatically read from the database. Enable Pessimistic Locking In the Map Browser, select the Account, CheckingAccount, and SavingsAccount, and mark Enable pessimistic locking in the context menu (Figure 228). Figure 228. Enable Pessimistic Locking for the Account Classes 370 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs To activate pessimistic locking, we regenerate the service classes. It is enough to generate the Account, CheckingAccount, and SavingsAccount classes. Note: You might want to version the itso.bank.persistence.service package before regenerating the service classes. This makes it easy to go back to the previous implementation. Generation creates new methods in the ItsoBankItsobankAccountQueryPool class, for example, singleLockSqlString: public java.lang.String singleLockSqlString() { return "UPDATE ITSO.ACCOUNT SET ACCID = ACCID } WHERE ACCID = ? "; Test Pessimistic Locking To test the pessimistic locking approach we remove the call to checkAccountAgainstDB in both the deposit and withdraw methods. Debugging the deposit method and tracing the SQL calls when executing reveals that the account row is locked when the account is updated (account.deposit(amount)), and then the account is retrieved again. This refreshes the balance in the account object. The row is unlocked when the transaction is committed. This all happens in the deposit and withdraw methods of the application controller and therefore the duration of the lock is very short. If we run two instances of the GUI application and update the same account, we can see how the balance is refreshed before the deposit or withdraw operation is performed. If you stop one GUI application in the deposit method using a breakpoint then the other application waits in the deposit (withdraw) method until the first application continues and commits the update. Optimistic Predicate for the Account Balance In the optimistic predicate approach, the balance is compared in the update SQL statement itself by including the balance value in the WHERE clause: update itso.account set balance = newvalue where (accid=’xxx-xxxx’) and (balance=oldvalue) If the balance has changed in the meantime by another process the update statement fails. Chapter 19. ATM Application Concurrent Processing 371 Enable Optimistic Predicate Before implementing the optimistic predicate, remove the Enable pessimistic locking mark for the three account classes in the Map Browser. In the Map Browser, select the Account table map, and the balance property map, and mark Be part of optimistic predicate in the context menu (Figure 229). Figure 229. Enable Optimistic Predicate for the Balance Property To activate the optimistic predicate, we regenerate the service classes. It is enough to generate the Account, CheckingAccount, and SavingsAccount classes. Note: You might want to version the itso.bank.persistence.services package before regenerating the service classes. This makes it easy to go back to the previous implementation. Alternatively you can use a different package name, for example, itso.bank.persistence.servicesopt. In this case you have to change the constructor in the ATMApplicationController to use the new datastore class: dataStore = itso.bank.persistence.servicesopt.ItsoBankItsoBankDataStore.singleton(); Generation changes the methods in the ItsoBankItsoBankAccountQueryPool class, for example, updateSqlString: public java.lang.String updateSqlString() { return "UPDATE ITSO.ACCOUNT SET BANKID = ?, CUSTID = ?, BALANCE = ?, BILLTITLE = ?, WHERE (ACCID = ?) AND (BALANCE = ?)"; } This statement fails if the balance in the database has changed and does not match the value of the business object. 372 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Test Optimistic Predicate To test the pessimistic predicate approach we remove the call to checkAccountAgainstDB in both the deposit and withdraw methods. Tracing the SQL calls when executing reveals that the update of the account is only performed if the balance matches the value in the database. If we run two instances of the GUI application or the servlet and update the same account, we get an DBOutOfSynch event and the balance is not updated. To get an exception when an optimistic predicate fails we called the SqlQuery.setThrowNoRowFoundException(true) method in the constructor of the application controller (see “Implement the Controller” on page 313). Note: The service code generated for optimistic locking with our inheritance structure is bad. We had to change the generated methods for SQL updates in the ItsoBankItsoBankAccountQueryPool, ...CheckingAccount..., and ...SavingsAccount... classes: For example, change the updateQuery method in the ItsoBankItsoBankCheckingAccountQueryPool class: public java.util.Vector updateQuery (java.util.Vector args, com.ibm.vap.Persistence.BOInjector anInjector) { Vector aSpecArray = new Vector(); ... aCompoundType = new DatabaseCompoundType(); aCompoundType.addField((DatabaseTypeField) (new com.ibm.ivj.db.base.DatabaseStringField("ACCOUNT.BANKID")) .setAttributes(4,0,1,true)); ... aCompoundType.addField((DatabaseTypeField) (new com.ibm.ivj.db.base.DatabaseDecimalField("ACCOUNT.V1")) .setAttributes(8,2,3,false)); stringArgs = new Vector(); stringArgs.addElement("ACCOUNT.BANKID"); ... stringArgs.addElement("ACCOUNT.V1"); spec.setInputShape(aCompoundType); ... } The change involves changing the last aCompoundType.addField and the last stringArgs.addElement call from ACCOUNT.BALANCE to ACCOUNT.V1. It looks like the same name (ACCOUNT.BALANCE) is not allowed twice in the list of parameters to be inserted into the SQL statement. This problem has been fixed in VisualAge for Java Version 3.02. Chapter 19. ATM Application Concurrent Processing 373 374 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 20 ATM Application Using Java Server Pages In this chapter we implement a number of programs using JSP. We demonstrate two techniques of using the JSP technology. The first approach uses Java application logic within the JSP, whereas the second approach puts the logic into JavaBeans and only the user interface in the JSP. We also implement all of the user interfaces of the ATM application with JSPs. The JSPs interact with one servlet that provides the interface to the ATM application controller and its underlying database access using the persistence framework. © Copyright IBM Corp. 2000 375 Java Server Pages Java Server Pages (JSP) technology is a cooperation between Sun Microsystems and IBM. The basic idea of JSPs is to remove the generation of the HTML output pages from the servlet code. A traditional servlet uses standard output to write HTML code to the Web server for display in a browser. The programmers who write servlet code in Java are, however, not user interface designers and will generally not produce good-looking Web pages. Even with the Servlet Builder, it is still the programmer who designs the output Web page. Web pages on a successful Web site require frequent updates to the look and feel, even if the basic information stays the same. The dynamic content of such pages can only be produced by Java code accessing enterprise data. Such Java code belongs in a servlet, whereas the Web page generation belongs in the hands of a Web page designer. JSPs and Servlets The solution to this problem is the combination of servlets and JSPs. A JSP is basically an HTML page with a few extra tags to retrieve data from JavaBeans. The JavaBeans could be produced by a servlet, they could retrieve enterprise data on their own, or they could be Enterprise JavaBeans. JSPs also support tags that allow Java code to be written inside the JSPs. This allows for the coding of small calculations, but there is really no limit; it is possible to write complete Java programs in a JSP. However, this seems to contradict the basic idea that a Web page designer writes the JSP, and the programmer writes the supporting servlet or JavaBean code. JSPs can be stand-alone and only interact with JavaBeans, or they can be called from a servlet to produce the HTML output. How Does a JSP Run? A JSP is compiled into a servlet for execution. This happens when the JSP is invoked the first time, and whenever its content changes. So a JSP is just another way of coding a servlet, using the HTML language for the Web page content, and additional tags for processing. 376 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs JSP Tags This redbook is not the place to describe the JSP language in detail. The specification is still evolving at the time of writing this redbook. We only document the tags that we used in our small sample programs. See the Sun Microsystem Java home page (http://java.sun.com/products/jsp) for details on the JSP specification. Warning JSP Language Specification The JSP 1.0 specification will change the names of some of the tags, for example, the Bean tag will be called UseBean. The tags shown here are from the JSP 0.91 specification and are implemented in WebSphere 2.0. Bean The bean tag creates a reference to a JavaBean to allow subsequent access to the properties and methods of the bean. <BEAN name="custlist" type="itso.bank.persistence.jsp.CustomerList"> </BEAN> This tag defines custlist as a reference to access an object of the named Java type. The bean object may have been created and registered by a servlet. If no instance exists, an instance is allocated and the processRequest method of the bean is called (if such a method exists). The processRequest method is typically used to create the dynamic content of the bean, for example, by accessing enterprise data. Directives Directive are placed at the start of a JSP, before any other JSP tags. The general form of directives is: <%@ variable="value" %. Here are some examples of directives: <%@ language="java" @> Java is the default language for JSP <%@ import="java.io.*;java.util.*;itso.bank.persistence.model.*" %> Declarations Declarations allow to declare variables and methods for later use in the JSP. The general form is: Chapter 20. ATM Application Using Java Server Pages 377 <SCRIPT RUNAT=server> int i=0; String name="Wahli"; private void foo() { ...code... } </SCRIPT> Scriptlets Scriptlets consist of Java code that is copied as is into the generated servlet. The general form of a scriptlet is: <% ....... java code ........ %> This example activates the persistent datastore, starts a transaction, allocates the home for a class, and retrieves all instances of the class: <% ItsoBankItsobankDataStore.singleton().activate(); Transaction.beginReadOnly(); CustomerHomeImpl custhome = CustomerHomeImpl.singleton(); Enumeration allcust = custhome.allInstances().elements(); ... %> Scriptlets can also refer to the implict variables request (the servlet request object), response (the servlet response object), out (the output writer for the generated HTML), and in (the servlet input reader). <% out.println("Some <b>bold</b> text"); %> Expressions An expression is a place holder for a Java variable or expression that is evaluated and that replaces the tag in the JSP. Typically the expression refers to a property or method of a JavaBean, or to previously defined variable. The general form of an expression is: <%= expression %> This example replaces the expression tags with the greeting of a bank customer: <p> Hello <%= custlit.getTitle() %> <%= custlit.getFname() %> <%= custlit.getLname() %>, welcome to the bank! </p> 378 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Repeat The repeat tag allows you to retrieve values in a loop, until an array out of bounds exception stops the processing. Here is an example: <h3> Customer Listing </h3> <REPEAT index="i"> <%= custlist.getCustomer(i) %> <%= custlist.getTitle() %> <%= custlist.getLname() %> ... </REPEAT> With the knowledge of these few tags we can start writing JSPs. JSP Examples Let us use the persistence framework classes of the ITSO BANK to write some simple JSP. In the first example we write a complete program as a JSP; in the second example we restructure the program and extract the logic into a JavaBean that is used by a JSP that formats the HTML output. Complete JSP Program This JSP produces a list of all bank customers with their accounts. All the processing is performed in the JSP. We activate the datastore, set up a transaction, allocate the home, and retrieve all customers. For each customer, we retrieve the associated accounts. All the data is formatted into HTML tables. Figure 230 shows the resulting output. Chapter 20. ATM Application Using Java Server Pages 379 Figure 230. Customer Listing JSP Output Figure 231 shows the JSP source. JSP tags are highlighted in bold. 380 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs <HTML><HEAD><TITLE> ITSO BANK Customer Listing </TITLE></HEAD><BODY> <image src="/itso/image/itso.gif"> <h1> ITSO BANK Customer Listing </h2> <%@ import ="itso.bank.persistence.model.*" %> <%@ import ="itso.bank.persistence.services.*" %> <%@ import ="com.ibm.vap.Transactions.*" %> <%@ import ="java.util.*,java.math.BigDecimal" %> <% try { ItsoBankItsoBankDataStore.singleton().activate(); Transaction.beginReadOnly(); CustomerHomeImpl custhome = CustomerHomeImpl.singleton(); Enumeration allcust = custhome.allInstances().elements(); while (allcust.hasMoreElements()) { Customer cust = (Customer)allcust.nextElement(); BigDecimal total = new BigDecimal(0); %> <table BORDER=3> <tr><td align="left"> <%= cust.getTitle() %> </td> <td align="left"> <%= cust.getFname() %> </td> <td align="left"> <%= cust.getLname() %> </td> </table><dir> <% LinkCollectionShell custaccounts = cust.getOwnedAccounts(); if (custaccounts.size() > 0) { %> <table border=1> <% Enumeration accounts = custaccounts.elements(); for (int i=0; accounts.hasMoreElements(); i++) { Account acct = (Account)accounts.nextElement(); %> <tr><td align=left> <%= acct.getAccountType() %> </td> <td align=left> <%= acct.getAccid() %> </td> <td align=right> <%= acct.getBalance() %> </td> <% total = total.add( acct.getBalance() ); } %> </table> <br> Total balance: <%= total.toString() %> </dir><p> <% } } } catch (Exception e) { %> <%= e.toString() %> <% } %> <h3> END </h3> </BODY> </HTML> Figure 231. Customer Listing JSP Source (ItsobankCustlist.jsp) Chapter 20. ATM Application Using Java Server Pages 381 This example demonstrates how to write complete programs in JSPs. The HTML is interspersed with the Java logic and makes it quite hard to understand. It would be difficult for a Web page designer to improve the visual output of such a program. JSP with a JavaBean For the second example we move the processing logic into a JavaBean and let the JSP do the HTML formatting. First we design the JavaBean that retrieves the customers from the database and stores them in a vector for the JSP. We provide simple methods for the JSP to get to the customer data and to access the accounts for the current customer. CustomerList Bean This bean is instantiated from the JSP. We store the list of customers in a vector and provide methods to retrieve a customer and its properties. When a customer is retrieved we also fill the associated accounts into a vector and provide methods to access individual accounts of the customer. The account data is only available for the currently selected customer and account. Here is the class definition: import itso.bank.persistence.model.*; import itso.bank.persistence.services.*; import com.ibm.vap.Transactions.*; public class CustomerList { java.util.Vector customers= new java.util.Vector(); java.util.Vector accounts = new java.util.Vector(); Customer cust = null; // current customer Account account = null; // current account java.math.BigDecimal total = new java.math.BigDecimal(0); } Constructor and Database Access The constructor is invoked when the bean is created from the JSP. We activate the data store and retrieve all customers: public CustomerList() { super(); execute(); } public void execute() { try { ItsoBankItsoBankDataStore.singleton().activate(); 382 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs com.ibm.vap.Transactions.Transaction.beginReadOnly(); CustomerHomeImpl custhome = CustomerHomeImpl.singleton(); java.util.Enumeration allcust = custhome.allInstances().elements(); while (allcust.hasMoreElements()) { Customer cust = (Customer)allcust.nextElement(); customers.addElement(cust); } } catch (Exception e) { } } Retrieve a Customer Here we retrieve one specific customer and all the associated accounts. An exception is thrown when an invalid index is passed as parameter. We also calculate the total balance for the customer and provide a method to retrieve the total: public String getCustomer(int i) throws java.lang.ArrayIndexOutOfBoundsException { try { cust = (Customer)customers.elementAt(i); accounts = new java.util.Vector(); total = new java.math.BigDecimal(0); LinkCollectionShell custaccounts = cust.getOwnedAccounts(); if (custaccounts.size() > 0) { java.util.Enumeration accenum = custaccounts.elements(); for (int j=0; accenum.hasMoreElements(); j++) { Account acct = (Account)accenum.nextElement(); accounts.addElement(acct); total = total.add( acct.getBalance() ); } } return ""; } catch (Exception e) { throw new java.lang.ArrayIndexOutOfBoundsException ("...."); } } public java.math.BigDecimal getTotal() { return total; } Customer Properties A set of methods provide access to the selected customer: public String getTitle() { try { return cust.getTitle(); } catch (Exception e) { return ""; } Chapter 20. ATM Application Using Java Server Pages 383 } public String getFname() { try { return cust.getFname(); } catch (Exception e) { return ""; } } public String getLname() { try { return cust.getLname(); } catch (Exception e) { return ""; } } Retrieve an Account For the selected customer we provide a method to retrieve an account from the list of accounts: public String getAccount(int i) throws java.lang.ArrayIndexOutOfBoundsException { try { account = (Account)accounts.elementAt(i); return ""; } catch (Exception e) { throw new java.lang.ArrayIndexOutOfBoundsException ("...."); } } Account Properties A set of methods provide access to the selected account: public String getAccid() { try { return account.getAccid(); } catch (Exception e) { return ""; } } public String getBalance() { try { return account.getBalance().toString(); } catch (Exception e) {return "";} } public String getType() { try { return account.getAccountType(); } catch (Exception e) { return ""; } } public String getMinamt() { try { return ((SavingsAccount)account).getMinamt().toString(); } catch (Exception e) {return "";} } public String getOverdraf() { try { return ((CheckingAccount)account).getOverdraf().toString(); } catch (Exception e) {return "";} } 384 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Customer List JSP The JSP code consists of pure HTML formatting with just a few tags to access the data from the customer list bean (Figure 232). <HTML><HEAD><TITLE> ITSO BANK Customer Listing </TITLE> </HEAD> <BODY> <image src="/itso/image/itso.gif"> <h1> ITSO BANK Customer Listing </h2> <BEAN name="custlist" type="itso.bank.persistence.jsp.CustomerList"> </BEAN> <REPEAT index="i"> <%= custlist.getCustomer(i) %> <table BORDER=3> <tr> <td align="left"> <%= custlist.getTitle() %> </td> <td align="left"> <%= custlist.getFname() %> </td> <td align="left"> <%= custlist.getLname() %> </td> </table> <dir> <table border=1> <REPEAT index="j"> <%= custlist.getAccount(j) %> <tr> <td align=left> <%= custlist.getType() %> </td> <td align=left> <%= custlist.getAccid() %> </td> <td align=right> <%= custlist.getBalance() %> </td> </REPEAT> </table> <br> Total balance: <%= custlist.getTotal() %> </dir> </REPEAT> <h3> END </h3> </BODY> </HTML> Figure 232. Customer List Formatting JSP Source (ItsobankCustbean.jsp) Note that this JSP produces the same output as the JSP program (see Figure 230 on page 380). JSP Account Listing We implemented another JavaBean and a formatting JSP to list all the accounts in the bank. The design is the same as for the customer listing. We created an AccountList bean with methods to retrieve all accounts into a vector, to retrieve an individual account, to return the account data, and to return the total balance. Here is the list of methods: public AccountList() public void execute() // constructor // initialize, get all accounts Chapter 20. ATM Application Using Java Server Pages 385 public public public public public public public public String String String String String String String String getAccount(int i) getAccid() getBalance() getType() getMinamt() getOverdraf() getCustomer() getTotal() // // // // // // // // get one account account ID account balance account type account minimum amount account overdraft amount account owner (greeting) total bank balance Figure 233 shows the JSP source that produces the account listing. <HTML><HEAD><TITLE> ITSO BANK Account Listing </TITLE></HEAD><BODY> <image src="/itso/image/itso.gif"> <h1> ITSO BANK Account Listing </h2> <BEAN name="accountlist" type="itso.bank.persistence.jsp.AccountList"> </BEAN> <table BORDER=3> <tr> <th align="left"> Type </th> <th align="left"> Number </th> <th align="left"> Balance </th> <th align="left"> Overdraft </th> <th align="left"> Min-Amount </th> <th align="left"> Customer </th> <REPEAT index="i"> <tr> <td align=left> <%= accountlist.getAccount(i) %> </td> <td align=left> <%= accountlist.getAccid() %> </td> <td align=right> <%= accountlist.getBalance() %> </td> <td align=right> <%= accountlist.getOverdraf() %> </td> <td align=right> <%= accountlist.getMinamt() %> </td> <td align=left> <%= accountlist.getCustomer() %> </td> </REPEAT> </table> <br> Total balance: <%= accountlist.getTotal() %> <h3> END </h3> </BODY> </HTML> Figure 233. Account Listing JSP Source Figure 234 shows the resulting output. A Web page designer could improve on the layout and produce a better output using the same data from the account bean. 386 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 234. Account Listing JSP Output Compile and Run JSPs To work with JSPs in VisualAge for Java Enterprise we use the WebSphere Test Environment and the JSP Execution Monitor. Chapter 20. ATM Application Using Java Server Pages 387 Configuring the WebSphere Test Environment With VisualAge for Java Version 3, HTML and JSP files are stored in: \IBMVJava\ide\project_resources\IBM WebSphere Test Environment\ hosts\default_host\default_app\web\ JSP Source File Location We suggest that you keep your JSP source files in a subdirectory of the main HTML directory. We created an ITSO subdirectory for the JSP files. Prepare VisualAge for Java Load the JSP Execution Monitor feature into the Workbench. To successfully test JSPs you should see the following projects in the Workbench: ❑ IBM JSP Execution Monitor ❑ IBM WebSphere Test Environment ❑ JSP Page Compile Generated Code (see Note) Note: The last project listed is automatically generated when you execute the first JSP. It contains packages with the servlets generated from compiled JSPs. For example, if you run a JSP named Test.jsp, in subdirectory itso of the main HTML directory, you will see a package named pagecompile._itso with a class named _Test_xjsp. The generated class contains a service method that contains the logic and output generation. Start the WebSphere Test Environment Follow the instructions in “Starting WebSphere” on page 13. Open a Web Browser Open a browser and point to your WebSphere server and the JSP file: http://127.0.0.1:8080/itso/Test.jsp http://host-name:8080/itso/Test.jsp <=== on same machine <=== from other machine By default, the WebSphere Test Environment in VisualAge for Java uses port 8080. JSP Debugging Once the JSP is compiled you can set breakpoints in the code and use the standard VisualAge for Java debugger to step through the code. Alternatively you can use the JSP Execution Monitor. 388 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Setup the JSP Execution Monitor Select Workspace -> Tools -> JSP Execution Monitor and, after waiting for the Option window, activate the Enable monitoring JSP execution checkbox (Figure 235). Figure 235. JSP Execution Monitor Option Run the JSP When you run a JSP with the Monitor activated, the compiled servlet is stored in a debug package, for example, pagecompile._itso_debug. After compilation the JSP Execution Monitor starts and displays the JSP source code, the compiled java servlet source code, and the HTML output as it is generated when executing the code (Figure 236). Debug the JSP The JSP Execution Monitor enables you to step through the JSP code line by line, study the generated Java servlet code, and watch the HTML as it is generated. Note that the HTML source text is not visible in the Java code, all strings are written from a large array that contains the source lines. Using the push buttons in the tool bar, you can step through the code (red arrow), run the code and watch its execution (green arrow), fast forward to the end (double arrow), or terminate the monitor (black square). Terminating the monitor does not stop the servlet, it stops the monitoring but lets the servlet run to completion at full speed. Chapter 20. ATM Application Using Java Server Pages 389 Figure 236. JSP Execution Monitor You can optionally use the VisualAge for Java debugger in addition to the JSP Execution Monitor, for example, when executing code in the JavaBeans that are used by the JSP code. Compile Errors If the compilation of the JSP produces errors, either you end up in the VisualAge for Java debugger, or you can retrieve errors by selecting the Retrieve syntax error information checkbox in the monitor options (Figure 235 on page 389). In the latter case, error messages are displayed in the Web browser. Stop JSP Monitoring To stop the monitoring you start the JSP Execution Monitor Option dialog and deselect the Enable monitoring JSP execution checkbox (Figure 235 on page 389). 390 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Implement the ATM Application with JSPs Now that we understand the power of JSPs we can design an implementation of the ATM application. The first decision is to create a controller servlet for all the processing. This servlet can keep the application controller (see “Application Controller” on page 310) with the interface to the persistent datastore alive. The servlet interacts with a series of JSPs that perform the formatting of the individual pages. Each JSP produces HTML output, which invokes the controller servlet for further processing (Figure 237). Servlets JSPs JSPController Servlet Controller ATM Application Controller 1 HTML 4 2 Card callPage PIN Account 5 3 Trans. Business Model Classes 5 Message Card Account Beans Accessed from JSP Service Classes Figure 237. ATM Application: JSP Flow Chapter 20. ATM Application Using Java Server Pages 391 The basic JSP application flow consists of these steps: ❑ An HTML page with a form invokes the controller servlet (1). ❑ The JSP controller servlet interacts with the ATM application controller to retrieve the requested object (card, account) from the database (2). The application controller interacts with the persistence framework. ❑ The JSP controller servlet prepares JavaBeans that provide methods to retrieve the properties from the business objects of the persistence framework (3). One JavaBean is used to pass a message to the JSPs. ❑ The JSP controller servlet calls the next JSP depending on the success or failure of the database interaction (4). ❑ The JSP formats the HTML output with data from the JavaBeans (5). We use the itso.bank.persistence.jsp package for all the code and we store the JSP source in an itso subdirectory of the main HTML directory. Design the JavaBeans We decided to implement three JavaBeans to hold the data for the JSPs that produce the HTML output. JSPMessage The JSPMessage bean contains one field, an error message. The class implementation is straightforward: public class JSPMessage { private String fieldMessage = null; } public JSPMessage() { super(); } public String getMessage() { return fieldMessage; } public void setMessage(String message) { fieldMessage = message; } JSPCard The JSPCard bean contains one property, a Card business object, and methods to enable the JSP to access the card information. Through the JSPCard bean, a JSP can retrieve the card ID, the greeting of the customer, and the list of accounts that can be accessed from the card. 392 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs public class JSPCard { itso.bank.persistence.model.Card card = null; java.util.Vector accounts = null; } public JSPCard() { super(); } public itso.bank.persistence.model.Card getCard() { return card; } public void setCard(itso.bank.persistence.model.Card aCard){ card = aCard; } public String getCardid() { try { return card.getCardid(); } catch (Exception e) { return ""; } } public String getGreetings() { try { return card.getGreetings(); } catch (Exception e) { return ""; } } public String getAccount(int i) throws java.lang.ArrayIndexOutOfBoundsException { try { if (accounts == null) accounts = card.getAccounts(); itso.bank.persistence.model.Account acct = (itso.bank.persistence.model.Account)accounts.elementAt(i); return acct.getAccid() + " " + acct.getAccountType(); } catch (Exception e) { throw new java.lang.ArrayIndexOutOfBoundsException ("...."); } } JSPAccount The JSPAccount bean contains one property, an Account business object, and methods to enable the JSP to access the account information. Through the JSPAccount bean, a JSP can retrieve the account ID, account type, balance, old balance, and the list of transaction records of the account. public class JSPAccount { itso.bank.persistence.model.Account account = null; } public JSPAccount() { super(); } public itso.bank.persistence.model.Account getAccount(){ return account; } Chapter 20. ATM Application Using Java Server Pages 393 public void setAccount(itso.bank.persistence.model.Account aAccount){ account = aAccount; } public String getAccid() { try { return account.getAccid(); } catch (Exception e) { return ""; } } public String getAccountType() { try { return account.getAccountType(); } catch (Exception e) { return ""; } } public String getBalance() { try { return account.getBalance().toString(); } catch (Exception e) { return ""; } } public String getOldBalance() { try { return account.getOldBalance().toString(); } catch (Exception e) { return ""; } } public String getTransrecord(int i) throws java.lang.ArrayIndexOutOfBoundsException { try { itso.bank.persistence.model.TransrecordImpl tr = (itso.bank.persistence.model.TransrecordImpl) account.getTransactions().elementAt(i); return tr.getTransid()+" "+tr.getTranstype()+" "+tr.getTransamt(); } catch (Exception e) { throw new java.lang.ArrayIndexOutOfBoundsException ("...."); } } Design the JSPs The design of the JSPs is quite simple if we assume that the data is ready in the JavaBeans, and if we do not code elaborate HTML pages. This exercise is not about complex Web pages with animation and sound, but to illustrate how a real application is implemented with JSPs. The appearance of the JSPs is the same as for the visual servlets we designed in Chapter 18, “ATM Application Using Servlets” on page 333. However, this time we code the appearance in HTML, with the dynamic data coming from the JavaBeans. The JSP controller servlet must know which JSP provided the input. We use a hidden input field called FROMJSP that contains the name of the JSP. The action of the forms points to the JSPController class (we have not yet coded that class). 394 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Card View Figure 238 shows the source of the card view JSP and Figure 239 shows the output produced by the card view JSP. The only dynamic data is the error message from the JSPMessage bean. <HTML> <HEAD> <TITLE> ITSO Bank ATM Card - JSP </TITLE> </HEAD> <BODY> <IMG SRC="/itso/image/itso.gif" BORDER="0"> <H1> Welcome to the ITSO ATM - JSP </H1> <BEAN name="messageBean" type="itso.bank.persistence.jsp.JSPMessage"></BEAN> <FORM METHOD="Post" ACTION="/servlet/itso.bank.persistence.jsp.JSPController" NAME="form"> <INPUT VALUE="CardView" TYPE="HIDDEN" NAME="FROMJSP"> <TABLE> <TR><TD>Please enter your card number: </TD> <TD><INPUT SIZE="10" TYPE="TEXT" NAME="cardId"></TD></TR> </TABLE> <P> <FONT COLOR="#FF0000"> <b> <%= messageBean.getMessage() %> </b> </FONT> <HR> <TABLE> <TR><TD><INPUT VALUE="Ok" TYPE="SUBMIT" NAME="okButton"> </TD> <TD><INPUT VALUE="Exit" TYPE="SUBMIT" NAME="exitButton"></TD></TR> </TABLE> </FORM> </BODY> </HTML> Figure 238. Card View JSP Figure 239. Card View JSP Output Chapter 20. ATM Application Using Java Server Pages 395 PIN View Figure 240 shows the source of the PIN view JSP. In the PIN view we use the message and the card bean for dynamic data. We retrieve the customer greeting and the card ID from the card bean and the error message from the message bean. <HTML> <HEAD> <TITLE> ITSO Bank ATM PIN - JSP </TITLE> </HEAD> <BODY> <IMG SRC="/itso/image/itso.gif" BORDER="0"> <H1> Welcome to the ITSO ATM - JSP </H1> <BEAN name="messageBean" type="itso.bank.persistence.jsp.JSPMessage"></BEAN> <BEAN name="cardBean" type="itso.bank.persistence.jsp.JSPCard"> </BEAN> <TABLE> <TR><TD><FONT SIZE="5"> <%= cardBean.getGreetings() %> </FONT></TD></TR> </TABLE> <P> Please verify your card and enter your PIN. <FORM METHOD="Post" ACTION="/servlet/itso.bank.persistence.jsp.JSPController" NAME="form"> <INPUT VALUE="PinView" TYPE="HIDDEN" NAME="FROMJSP"> <TABLE> <TR><TD>Card ID</TD> <TD> <%= cardBean.getCardid() %> </TD> <TD> </TD> <TD>PIN </TD> <TD> <INPUT SIZE="5" TYPE="PASSWORD" NAME="pin"> </TD> </TR> </TABLE> <P> <FONT COLOR="#FF0000"> <b> <%= messageBean.getMessage() %> </b> </FONT> <HR> <TABLE> <TR><TD><INPUT VALUE="Ok" TYPE="SUBMIT" NAME="okButton"> </TD> <TD><INPUT VALUE="Cancel" TYPE="SUBMIT" NAME="cancelButton"></TD></TR> </TABLE> </FORM> </BODY> </HTML> Figure 240. PIN View JSP Account View Figure 241 shows the source of the account view JSP. In the account view we use the message and the card bean for dynamic data. We retrieve the customer greeting from the card bean and the error message from the message bean. We also use a <REPEAT> loop to fill a drop-down list with all the accounts associated with the card. 396 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs <HTML> <HEAD> <TITLE> ITSO Bank ATM Account - JSP </TITLE> </HEAD> <BODY> <IMG SRC="/itso/image/itso.gif" BORDER="0"> <H1> Welcome to the ITSO ATM - JSP </H1> <BEAN name="messageBean" type="itso.bank.persistence.jsp.JSPMessage"></BEAN> <BEAN name="cardBean" type="itso.bank.persistence.jsp.JSPCard"> </BEAN> <TABLE> <TR><TD> <FONT SIZE="5"> <%= cardBean.getGreetings() %> </FONT> </TD></TR> </TABLE> <P> <FONT SIZE="4"> Please select an account. </FONT> <FORM METHOD="Post" ACTION="/servlet/itso.bank.persistence.jsp.JSPController" NAME="form"> <INPUT VALUE="AccountView" TYPE="HIDDEN" NAME="FROMJSP"> <TABLE> <TR><TD> <SELECT SIZE="4" NAME="AccountList"> <REPEAT index="i"> <OPTION> <%= cardBean.getAccount(i) %> </REPEAT> </SELECT> </TABLE> <P> <FONT COLOR="#FF0000"> <b> <%= messageBean.getMessage() %> </b> </FONT> <HR> <TABLE> <TR><TD><INPUT VALUE="Ok" TYPE="SUBMIT" NAME="okButton"> </TD> <TD><INPUT VALUE="Cancel" TYPE="SUBMIT" NAME="cancelButton"></TD></TR> </TABLE> </FORM> </BODY> </HTML> Figure 241. Account View JSP Transaction View Figure 242 shows the source of the transaction view JSP. In the transaction view we use the message, the card, and the account bean for dynamic data. We retrieve the customer greeting from the card bean, the account information from the account bean, and the error message from the message bean. We also use a <REPEAT> loop to fill a drop-down list with the transaction records of the account. Chapter 20. ATM Application Using Java Server Pages 397 <HTML> <HEAD> <TITLE> ITSO Bank ATM Transaction - JSP </TITLE> </HEAD> <BODY> <IMG SRC="/itso/image/itso.gif" BORDER="0"> <H1> Welcome to the ITSO ATM - JSP </H1> <BEAN name="messageBean" type="itso.bank.persistence.jsp.JSPMessage"></BEAN> <BEAN name="cardBean" type="itso.bank.persistence.jsp.JSPCard"> </BEAN> <BEAN name="accountBean" type="itso.bank.persistence.jsp.JSPAccount"></BEAN> <TABLE> <TR><TD> <FONT SIZE="5"> <%= cardBean.getGreetings() %> </FONT></TD></TR> </TABLE> <P> <FONT SIZE="4"> Please perform a transaction. </FONT> <FORM METHOD="Post" ACTION="/servlet/itso.bank.persistence.jsp.JSPController" NAME="form"> <INPUT VALUE="TransactionView" TYPE="HIDDEN" NAME="FROMJSP"> <TABLE> <TR><TD> Account ID: </TD> <TD> <%= accountBean.getAccid() %> </TD> <TD> <%= accountBean.getAccountType() %> </TD> <TD><BR></TD> </TR> <TR><TD> Old balance:</TD> <TD> <%= accountBean.getOldBalance() %> </TD> <TD><BR></TD> <TD> Amount: </TD> </TR> <TR><TD> New balance: </TD> <TD> <FONT SIZE="4"> <%= accountBean.getBalance() %> </FONT> </TD> <TD><BR></TD><TD><INPUT SIZE="8" TYPE="TEXT" NAME="amount"></TD></TR> </TABLE> Transaction history: <BR> <SELECT SIZE="4" NAME="TransactionList"> <REPEAT index="i"> <OPTION> <%= accountBean.getTransrecord(i) %> </REPEAT> </SELECT> <P> <FONT COLOR="#FF0000"> <b> <%= messageBean.getMessage() %> </b> </FONT> <HR> <TABLE> <TR><TD><INPUT VALUE="Deposit" TYPE="SUBMIT" NAME="depositButton"> </TD> <TD><INPUT VALUE="Withdraw" TYPE="SUBMIT" NAME="withdrawButton"></TD> <TD><INPUT VALUE="History" TYPE="SUBMIT" NAME="historyButton"> </TD> <TD><INPUT VALUE="Cancel" TYPE="SUBMIT" NAME="cancelButton"> </TD> <TD><INPUT VALUE="Exit" TYPE="SUBMIT" NAME="exitButton"> </TD> </TR> </TABLE> </FORM> </BODY> </HTML> Figure 242. Transaction View JSP Thank You View The thank you view JSP says good-bye to the user and allows a new session to be started (Figure 243). 398 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs <HTML> <HEAD> <TITLE> ITSO Bank ATM Good-bye - JSP </TITLE> </HEAD> <BODY> <H1> ITSO ATM - Thank you! - JSP </H1> <FORM METHOD="Post" ACTION="/servlet/itso.bank.persistence.jsp.JSPController" NAME="form"> <INPUT VALUE="ThankyouView" TYPE="HIDDEN" NAME="FROMJSP"> <INPUT VALUE="Restart Transaction" TYPE="SUBMIT" NAME="restartButton"> </FORM> </BODY> </HTML> Figure 243. Thank You View JSP Implement the JSP Controller Servlet The last step is to implement the JSP controller servlet (JSPController class) that accepts the input from all the HTML pages and calls the appropriate JSP to format the next Web page. We implement the controller servlet as a subclass of the PageListServlet that provides a method to call a JSP. The JSP controller servlet instantiates the ATM application controller and keeps it alive in a static variable: import import import public itso.bank.persistence.model.*; javax.servlet.http.*; com.ibm.vap.Transactions.*; class JSPController extends com.ibm.servlet.PageListServlet implements DBOutOfSynchListener, LimitExceededListener { private static ATMApplicationController applicationController = null; JSPMessage message = null; // message for user } public JSPController() { super(); if (applicationController == null) { applicationController = new ATMApplicationController(); applicationController.addLimitExceededListener(this); applicationController.addDBOutOfSynchListener(this); } // set transaction to thread binding policy Transaction.setBindingPolicy(new TransactionToThreadBindingPolicy()); } Note the setting of the transaction binding policy, which is necessary for multithreaded servlet applications. Chapter 20. ATM Application Using Java Server Pages 399 Retrieve Form Values The getParameter method retrieves the value of a field or button in the form: public java.lang.String getParameter(HttpServletRequest request, java.lang.String parameterName) { java.lang.String[] parameterValues = null; java.lang.String paramValue = null; parameterValues = request.getParameterValues(parameterName); if (parameterValues != null) paramValue = parameterValues[0]; return paramValue; } Get or Post? Initially the JSP controller servlet can be called directly using an URL; this invokes the doGet method. When called from the JSP forms, the doPost method is invoked. We code these methods to call a performTask method. public void doGet(HttpServletRequest request, HttpServletResponse response) { performTask(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) { performTask(request, response); } Select the Processing Method The performTask method starts a session or retrieves the existing session, disables caching of the generated HTML (see “Disable Caching of the Output HTML” on page 363), prepares the JSPMessage bean, and calls the appropriate processing routine, depending on the hidden field that contains the name of the calling JSP. We use the session object to store the JSP JavaBeans for the next step of the dialog with the user. Notice now the JSPMessage bean is registered with the request, and stored in the session object. public void performTask(HttpServletRequest request, HttpServletResponse response) { try { HttpSession session = request.getSession(true); // disable caching response.setHeader("Pragma","no-cache"); response.setHeader("Cache-Control","no-cache"); response.setDateHeader("Expires",0); JSPMessage message = new JSPMessage(); setRequestAttribute("messageBean", message, request); session.putValue("message",message); 400 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs String fromJsp = getParameter(request, "FROMJSP"); if (fromJsp == null || fromJsp.equals("CardView")) { performCardView(request,response); return; if (fromJsp.equals("PinView")) { performPinView(request, response); return; if (fromJsp.equals("AccountView")) { performAccountView(request, response); return; if (fromJsp.equals("TransactionView")) { performTransactionView(request, response); return; if (fromJsp.equals("ThankyouView")) { performThankyouView(request, response); return; performThankyouView(request, response); } catch (Exception theException) { handleError(request, response, theException); } } } } } } } Card Processing The performCardView method processes the input of the card view JSP. To start, the JSPMessage bean is accessed and the form values are retrieved. If the exit button was clicked, the thank you JSP is called. If no card ID was given, or on initial call, the card view JSP is called. Now we retrieve the card using the application controller. For an invalid card ID, we call the card view JSP again with an error message. For a valid card ID, we construct the JSPCard bean, register it, store it in the session object, and call the PIN view JSP. public void performCardView(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { HttpSession session = request.getSession(false); message = (JSPMessage)session.getValue("message"); String cardId = getParameter(request, "cardId"); String okButton = getParameter(request, "okButton"); String exitButton = getParameter(request, "exitButton"); String fromJsp = getParameter(request, "FROMJSP"); if (exitButton != null) { session.removeValue("atmcard"); session.removeValue("atmacct"); callPage("ThankyouView", request, response); return; } if (cardId == null || fromJsp == null) { callPage("CardView", request, response); return; } Card card = applicationController.getCard(cardId); Chapter 20. ATM Application Using Java Server Pages 401 if (card == null) { message.setMessage("Invalid card number, please reenter"); callPage("CardView", request, response); return; } JSPCard jspCard = new JSPCard(); jspCard.setCard(card); setRequestAttribute("cardBean", jspCard, request); session.putValue("atmcard", jspCard); callPage("PinView", request, response); return; } PIN Processing The performPinView method processes the input of the PIN view JSP. To start, the JSPMessage bean is accessed and the form values are retrieved. If the cancel button was clicked, the card view JSP is called. Now we retrieve the JSPCard bean from the session object. If none exists, the card view JSP is called, otherwise we register the bean. Next we check the PIN entered by the user. For an invalid PIN we call the PIN view JSP again with an error message. For a valid PIN we call the account view JSP. public void performPinView(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { HttpSession session = request.getSession(false); message = (JSPMessage)session.getValue("message"); String pin = getParameter(request, "pin"); String okButton = getParameter(request, "okButton"); String cancelButton = getParameter(request, "cancelButton"); if (cancelButton != null) { session.removeValue("atmcard"); callPage("CardView", request, response); return; } JSPCard jspCard = (JSPCard) session.getValue("atmcard"); if (jspCard == null) { message.setMessage("No session with valid ATM card"); callPage("CardView", request, response); return; } Card card = jspCard.getCard(); setRequestAttribute("cardBean", jspCard, request); if ( !applicationController.checkPin(card,pin) ) { 402 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs message.setMessage("Invalid PIN, please reenter"); callPage("PinView", request, response); return; } else { callPage("AccountView", request, response); return; } } Account Processing The performAccountView method processes the input of the account view JSP. To start, the JSPMessage bean is accessed and the form values are retrieved. Now we retrieve the JSPCard bean from the session object. If none exists, the card view JSP is called, otherwise we register the bean. If the cancel button was clicked, the PIN view JSP is called. If no account was selected the account view JSP is called again. We look for the selected account in the card business object. For an invalid account we call the account view JSP. For a valid account, we construct the JSPAccount bean, register it, store it in the session object, and call the transaction view JSP. public void performAccountView(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { HttpSession session = request.getSession(false); message = (JSPMessage)session.getValue("message"); String selaccount = getParameter(request, "AccountList"); String okButton = getParameter(request, "okButton"); String cancelButton = getParameter(request, "cancelButton"); JSPCard jspCard = (JSPCard) session.getValue("atmcard"); Account account; if (jspCard == null) { message.setMessage("No session with valid ATM card"); callPage("CardView", request, response); return; } Card card = jspCard.getCard(); setRequestAttribute("cardBean", jspCard, request); if (cancelButton != null) { callPage("PinView", request, response); return; } if (selaccount == null) { message.setMessage("Select an account please"); Chapter 20. ATM Application Using Java Server Pages 403 callPage("AccountView", request, response); return; } String accountId = selaccount.substring(0, selaccount.indexOf(" ") ); try { account = card.getAccount(accountId); } catch (Exception e) { account = null; } if (account == null) { message.setMessage("Invalid account, please reenter"); callPage("AccountView", request, response); return; } JSPAccount jspAccount = new JSPAccount(); jspAccount.setAccount(account); setRequestAttribute("accountBean", jspAccount, request); session.putValue("atmacct", jspAccount); callPage("TransactionView", request, response); return; } Transaction Processing The performTransactionView method processes the input of the transaction view JSP. To start, the JSPMessage bean is accessed and the form values are retrieved. If the exit button was clicked we call the thank you JSP. Now we retrieve the JSPCard and the JSPAccount beans from the session object. If either does not exist, the card view JSP is called, otherwise we register the JSPCard bean. If the cancel button was clicked we call the account view JSP. For the deposit, withdraw, and history buttons we call the appropriate method of the application controller. Deposit and withdraw may fire the LimitExceeded or DBOutOfSynch events. These events will be handled by appropriate methods that change the response message. Finally we store the changed account object in the JSPAccount bean, register the bean, and call the transaction view JSP. public void performTransactionView(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { HttpSession session = request.getSession(false); message = (JSPMessage)session.getValue("message"); String amount = getParameter(request, "amount"); String depositButton = getParameter(request, "depositButton"); String withdrawButton = getParameter(request, "withdrawButton"); String historyButton = getParameter(request, "historyButton"); String cancelButton = getParameter(request, "cancelButton"); 404 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs String exitButton = getParameter(request, "exitButton"); if (exitButton != null) { session.removeValue("atmcard"); session.removeValue("atmacct"); callPage("ThankyouView", request, response); return; } JSPCard jspCard = (JSPCard) session.getValue("atmcard"); JSPAccount jspAccount = (JSPAccount) session.getValue("atmacct"); if (jspCard == null | jspAccount == null) { session.removeValue("atmcard"); session.removeValue("atmacct"); message.setMessage("No session with valid ATM card"); callPage("CardView", request, response); return; } Card card = jspCard.getCard(); Account account = jspAccount.getAccount(); Account acct2 = null; setRequestAttribute("cardBean", jspCard, request); if (cancelButton != null) { session.removeValue("atmacct"); callPage("AccountView", request, response); return; } if (depositButton != null) { message.setMessage("Account updated!"); acct2 = applicationController.deposit(account,amount); } if (withdrawButton != null) { message.setMessage("Account updated!"); acct2 = applicationController.withdraw(account,amount); } if (historyButton != null) { acct2 = applicationController.getTransactions(account); message.setMessage("Account history retrieved!"); } jspAccount.setAccount(acct2); setRequestAttribute("accountBean", jspAccount, request); callPage("TransactionView", request, response); } Chapter 20. ATM Application Using Java Server Pages 405 Thank You Processing The performThankyouView method removes the beans from the session object and calls the card view JSP. public void performThankyouView(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { HttpSession session = request.getSession(false); session.removeValue("atmcard"); session.removeValue("atmacct"); callPage("CardView", request, response); } Event Handling The deposit and withdraw method may fire the LimitExceeded or DBOutOfSynch events. We handle these events in appropriate methods and set an error message text. public void handleLimitExceeded(LimitExceededEvent event) { message.setMessage("Withdraw failed - not enough funds"); } public void handleDBOutOfSynch(DBOutOfSynchEvent event) { message.setMessage("DB out of synch - account refreshed!"); } Servlet Configuration File A servlet that is a subclass of the PageListServlet class must have a configuration file with the name of the class and an extension .servlet. For our JSPController the file is called JSPController.servlet (Figure 244). The configuration file contains the name of the class and references to all the JSPs that are called by the servlet. These references relate the short name used in the servlet callPage method (for example, “CardView”) to a relative path in the HTML directory. This indirect notation enables you to move the JSP source into another directory, without having to change the coding in the calling servlet. Where to Put the Configuration File? For the test environment of WebSphere running within VisualAge for Java, the configuration is stored in the project resources directory: \IBMVJava\ide\project_resources\ITSO SG245426 ITSOBANK ATM\ \itso\bank\persistence\jsp\ 406 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Within the project resources subdirectory you use the package name structure of the servlet class. For the real WebSphere execution environment, the configuration file is stored in the directories: ❑ WebSphere Version 2.0: WebSphere\AppServer\servlets\itso\bank\psersistence\jsp ❑ WebSphere Version 3.0 WebSphere\AppServer\hosts\default_host\default_app\servlets\itso\bank\persistence\jsp See “Deployment of JSPs” on page 424 for more information. <?xml version="1.0"?> <servlet> <page-list> <default-page> <uri>/itso/CardView.jsp</uri> </default-page> <page> <uri>/itso/CardView.jsp</uri> <page-name>CardView</page-name> </page> <page> <uri>/itso/PinView.jsp</uri> <page-name>PinView</page-name> </page> <page> <uri>/itso/AccountView.jsp</uri> <page-name>AccountView</page-name> </page> <page> <uri>/itso/TransactionView.jsp</uri> <page-name>TransactionView</page-name> </page> <page> <uri>/itso/ThankyouView.jsp</uri> <page-name>ThankyouView</page-name> </page> </page-list> <code>itso.bank.persistence.jsp.JSPController</code> <description></description> </servlet> Figure 244. JSP Controller Servlet Configuration File Chapter 20. ATM Application Using Java Server Pages 407 Test the ATM JSP Application Start the WebSphere test environment (see “Starting WebSphere” on page 13). If you want to debug the JSP, start the JSP Execution Monitor (see “Setup the JSP Execution Monitor” on page 389). Make sure that the underlying database system is started as well. We suggest that you compile each JSP by pointing to the JSP file from a browser: http://127.0.0.1:8080/itso/CardView.jsp The JSPs create the necessary beans by calling the bean’s constructor. All our methods were coded so that in case of exceptions (null pointer) an empty string is returned. Therefore, the JSPs display an HTML page without any dynamic data. After compiling the JSPs, start the application by pointing to the JSP controller servlet: http://127.0.0.1:8080/servlet/itso.bank.persistence.jsp.JSPController By default, the JSP controller servlet calls the card view JSP to display the initial HTML page. 408 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs 21 Deployment of Applications, Applets, Servlets, and Java Server Pages In this chapter we cover the general process of deployment of applications, applets, servlets, and JSPs. You can apply this process to deploy the different versions of the ATM application. © Copyright IBM Corp. 2000 409 Deployment of Applications Applications run in a platform Java Virtual Machine (JVM) and have access to the machine they run on and to other machines on the network. Applications developed with enterprise access builders of VisualAge for Java must have access to the run-time libraries of VisualAge for Java (Figure 245). d:\IBMVJava\eab\runtime20\ ivjdab.jar - data access beans ivjj2cpp.jar - c++ access builder d:\IBMVJava\eab\runtime30 ivjpb30.jar - persistence builder ivjsb30.jar - servlet builder eablib.jar - enterprise access builder library recjava.jar - record framework ivjdab30.jar - data access builder Figure 245. VisualAge for Java Jar Files Add the necessary jar files to the class path environment variable. Export an Application from VisualAge for Java The steps to export an application are: ❑ Create a master directory for the exported classes (d:\Export). ❑ Select the classes in the Workbench and export the class files to the master directory. Subdirectories for the packages are automatically created. Deployment Process for Applications Copy the exported directories to the machine where the applications will run. Set up a master directory that is in the class path and copy the package subdirectories into the master directory. Figure 246 shows the process of deploying applications from a VisualAge for Java development machine to an execution machine. 410 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Development Machine d:\Export VisualAge for Java Export PackageA A1.class A2.class PackageB B1.class B2.class B3.class Execution Machine CLASSPATH=.;.....; d:\JavaApp; ... d:\JavaApp PackageA A1.class A2.class PackageB B1.class B2.class B3.class PackageC C1.class RUN Java Virtual Machine Figure 246. Deployment Process for Applications This setup guarantees that the JVM will find all classes. A proper setup that follows the Java class naming rules is required for successful operation. Run an application with this command: java PackageA.A2 Chapter 21. Deployment of Applications, Applets, Servlets, and Java Server Pages 411 Deployment of Applets Applets run in a Web browser on a client machine and are downloaded from a Web Server. They have limited access to the client machine and can only access the server machine from which they come. The biggest advantage of applets is that they are automatically distributed to the client machine. There is no maintenance burden; new versions of applets are installed on Web servers. Export Applets from VisualAge for Java The steps to export an applet are the same as for exporting an application: ❑ Create a master directory for the exported classes. ❑ Select the classes in the Workbench and export the class files to the master directory. Deployment Process for Applets Copy the exported directories to the Web server machine. Set up a master directory where the Web server looks for HTML files and applets and copy the package subdirectories into the master directory. Expand the VisualAge for Java enterprise library (see Figure 245 on page 410) and the DB2 JDBC library (d:\SQLLIB\db2java.zip) into a subdirectory called COM\ibm of the master directory. In the master directory, create an HTML file for each applet that points to the applet’s class. Note that export can create a skeleton applet file for each applet. Figure 247 shows the process of deploying applets from a VisualAge for Java development machine to a Web server and a Web browser. 412 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Development Machine d:\Export VisualAge for Java Export PackageX X1.class X2.class PackageY Y1.class Y2.class Y3.class Web Server Machine d:\IBM Http Server/htdocsl Web Server X1.html X2.html <HTML> ...... http: <applet code=PackageX.X1.class ...> ... PackageX X1.class X2.class PackageY Y1.class Y2.class Y3.class COM.ibm.... aa.class download Web Browser X1.html X1 Applet Web Browser Client Figure 247. Deployment Process for Applets Chapter 21. Deployment of Applications, Applets, Servlets, and Java Server Pages 413 Deployment of Servlets Most Web servers contain a servlet run-time facility that enables the execution of servlets. Some Web servers have their own JVM, others use the JVM of the underlying operating system. Modern Web servers have optimized servlet run-time facilities that cache servlet code and keep servlets active after their first use. This optimization improves performance considerably compared to CGI programs that are loaded at each invocation. We cover only the Windows NT platform and the WebSphere Application Server. WebSphere can run on top of a number of Web servers, we used the new IBM HTTP Server, which is based on the Apache Web server. Because servlets run on the Web server in a trusted environment, they have no real restrictions about what they can access and with which other machines they can communicate. Communication with other servers is more a question of which protocols and products are installed on the Web server. The basic deployment process is the same with or without WebSphere installed: ❑ Export the servlet classes ❑ Copy the exported files to a suitable target directory on the Web server ❑ Set up the Web server class path ❑ Optionally tailor the WebSphere execution environment. We used both Version 2.0 and Version 3.0 of WebSphere for deployment. 414 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Deployment of Servlets for WebSphere Figure 248 shows the process of deploying servlets from a VisualAge for Java development machine to WebSphere. Development Machine d:\Export VisualAge for Java Export PackageX X1.class X2.class PackageY Y1.class Y2.class Y3.class WebSphere d:\..target..directory.. Web Server http: <html> <form ......... action= "/servlet/PackageX.X1" </form> ...... input PackageX X1.class X2.class PackageY Y1.class Y2.class Y3.class generated HTML output Web Browser Title -------------------------------Input: Result: -------------------------------- X1 Applet Submit Web Browser Client Figure 248. Deployment Process for Servlets to WebSphere Chapter 21. Deployment of Applications, Applets, Servlets, and Java Server Pages 415 WebSphere Version 2 Deployment In this section we describe the deployment process for WebSphere Version 2.0. Target Location The target location for servlets in WebSphere Version 2.0 is either of: d:\WebSphere\AppServer\classes d:\WebSphere\AppServer\servlets Servlets deployed into the servlets subdirectory are reloaded automatically when new code is deployed. The classes subdirectory is suitable for more static servlets that do not change any more. We exported the class files of the model, services, servlet, and jsp packages into the WebSphere servlets subdirectory and ended up with this directory structure: d:\WebSphere\AppServer\servlets\itso\bank\persistence\model d:\WebSphere\AppServer\servlets\itso\bank\persistence\services d:\WebSphere\AppServer\servlets\itso\bank\persistence\servlet d:\WebSphere\AppServer\servlets\itso\bank\persistence\jsp Class Path Setting for WebSphere Version 2 The most important configuration activity for servlet deployment is the setup of the class path. Every Web server has its unique way of defining its class path. Because many servlets use some of the Enterprise Access Builder classes of VisualAge for Java, it is mandatory that the jar files of the Enterprise Access Builders are deployed to the Web server and added to the class path. WebSphere does not have a configuration file where you can specify the class path. All configuration activity is performed using an administration applet. WebSphere Administration Applet The administration applet is started through a special HTTP command: http://...hostname....:9527/ The port number of the administration server can be tailored; 9527 is the default value. Figure 249 shows the logon form for the WebSphere Application Server. 416 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 249. WebSphere Application Server Version 2: Administration Introduction After a successful logon WebSphere displays the Application Server Introduction form (Figure 250). Chapter 21. Deployment of Applications, Applets, Servlets, and Java Server Pages 417 Figure 250. WebSphere Application ServerVersion 2: Introduction Most of the configuration activities involve the basic setup and the servlet run-time engine of WebSphere. Setup Java Engine We do not go through all of the WebSphere administration pages. For now the most important setting to get servlets created with VisualAge for Java to work is the class path setting, and that is on the Setup -> Java Engine page (Figure 251). 418 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Figure 251. WebSphere Application Server Version 2: Java Engine Setup It is difficult to edit the class path setting in that small field, so we suggest copying the text into an editor. We used the following class path: D:\jdk1.1.6\lib\classes.zip; D:\WebSphere\AppServer\classes; D:\WebSphere\AppServer\web\classes; D:\SQLLIB\java\db2java.zip; D:\IBMVJava\eab\runtime30\ivjsb30.jar; D:\IBMVJava\eab\runtime30\ivjpb30.jar; D:\IBMVJava\hpj\lib\swingall.jar; <=== JDK (or jdk1.1.7) <=== export directory <=== <=== <=== <=== DB2 JDBC Drivers servlet builder persistence builder swing To simplify the class path, we copied the jar files into the directory D:\WebSphere\AppServer\lib. Jar files in the lib subdirectory are automatically added to the WebSphere class path and do not have to be specified in the administration setup. Chapter 21. Deployment of Applications, Applets, Servlets, and Java Server Pages 419 There is, however, a special case to be considered: ❑ The ivjpb30.jar file contains the same classes as the ejs.jar file provided by WebSphere, plus extra classes. To make sure that the ivjpb30.jar file is loaded before the ejs.jar file, we added it to the class path. Our final class path was: D:\jdk1.1.7\lib\classes.zip; D:\WebSphere\AppServer\classes; D:\WebSphere\AppServer\web\classes; D:\SQLLIB\java\db2java.zip; D:\WebSphere\AppServer\lib\ivjpb30.jar; <=== JDK <=== export directory <=== DB2 JDBC Drivers <=== persistence builder Note that in VisualAge for Java Version 2 the ivjpb20.jar does not contain the classes of the VisualAge Persistence Extras project with the additional classes to interact with AWT and Swing from Persistence Builder home and relationship collections. You have to build a jar file yourself by exporting the project. Tailor and Manage Servlet On the Servlets pages of the administration applet you can control individual servlets: ❑ ❑ ❑ ❑ Add a servlet Assign an alias (short name) Preload Specify parameters For our simple configuration we did not tailor any servlets. WebSphere Version 3 Deployment In this section we describe the deployment process for WebSphere Version 3.0. WebSphere Version 3.0 can run multiple copies (clones) within one machine. Servlets can be grouped into WebSphere applications that can be configured individually. We used the configuration provided by the installation procedure: the Default Server with the default_app application. We do not describe the installation of WebSphere Version 3.0. We assume that WebSphere was installed successfully and was started at least once to have all the initial setup performed. 420 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs WebSphere Version 3.0 is started as a service, manually or automatically. We assume that these two services have been started: ❑ IBM HTTP Server ❑ IBM WS AdminServer Target Location for the Default Application The target location for servlets in the default application of WebSphere Version 3.0 is: d:\WebSphere\AppServer\hosts\default_host\default_app\servlets We exported class files of the model, services, servlet, and jsp packages into the WebSphere servlets subdirectory and ended up with this directory structure: d:\WebSphere\AppServer\hosts\default_host\default_app\servlets\ itso\bank\persistence\xxxxx Class Path Setting for WebSphere Version 3 The most important configuration activity for servlet deployment is the setup of the class path. Every Web server has its unique way of defining its class path. Because many servlets use some of the Enterprise Access Builder classes of VisualAge for Java, it is mandatory that the jar files of the Enterprise Access Builders are deployed to the Web server and added to the class path. WebSphere does not have a configuration file where you can specify the class path. All configuration activity is performed using an administration client. WebSphere Administration Client The administration client is started from the program icon named Administrators’s Console in the folder IBM WebSphere -> Application Server V3.0. Select the Topology page and find the Default Server under the host name of your machine (Figure 252). If this server is not started (a button with a green light is active in the task bar) start it now. Chapter 21. Deployment of Applications, Applets, Servlets, and Java Server Pages 421 Figure 252. WebSphere Application Server Version 3: Administration Client To set up the WebSphere application expand the Default Server -> servletEngine and locate the default_app Advanced page (Figure 253). Figure 253. WebSphere Application Server Version 3: Application Setup 422 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Tailor the document root and the class path as follows: ❑ Set the document root to point to the web subdirectory of the default application. This is where you will copy the HTML and JSP files. from: d:\IBMHTT~1\htdocs to : d:\WebSphere\AppServer\hosts\default_host\default_app\web ❑ Add the VisualAge for Java jar files to the class path: d:\WebSphere\AppServer\lib\ivjpb30.jar d:\WebSphere\AppServer\lib\ivjsb30.jar Copy these two files from d:\IBMVJava\eab\runtime30 to the WebSphere lib subdirectory. Note that the swingall.jar file is in the WebSphere class path by default. Click on the Apply button when done. You have to stop the Default Server and restart it to activate the changes. Tailor the Web Server To direct HTML requests to the default application subdirectory tailor the configuration file of the Web server. For the IBM HTTP Server, edit the http.conf file in d:\IBM HTTP Server\conf and set up an alias: Alias /itso/ d:/WebSphere/AppServer/hosts/default_host/default_app/web/itso/ You have to stop and start the Web server to activate this change. Testing the ITSO Bank Application The setup is now complete. The main directory of the default application is: d:\WebSphere\AppServer\hosts\default_host\default_app The servlets subdirectory contains the exported class files from VisualAge for Java. The web subdirectory contains the HTML and JSP files. You can copy the itso subdirectory from the sample code (Va3PersBk\sampcode\itso) to the web subdirectory. Start a browser and enter http://...yourhostname.../itso/itsobank.html From this HTML file you can access the servlet and JSP applications. Chapter 21. Deployment of Applications, Applets, Servlets, and Java Server Pages 423 Deployment of JSPs JSPs are part HTML and part Java servlet code. The JSP source files are deployed to the same place as HTML files. Maybe it is good practice to use a subdirectory with a name indicating that they are JSPs and not just HTML; on the other hand the .jsp extension already makes this clear. Because JSPs are compiled into servlets, all the classes that are used in the JSP code or by the beans that are accessed, must be accessible through the class path of WebSphere. In summary, JSP source files are deployed in the same way as HTML files, but the class path must be tailored in the same way as for servlets. The JSP configuration file of servlets that are subclasses of the PageListServlet (.servlet extension) is deployed into the same location as servlets. For our ITSO Bank JSP implementation we copied the JSPController.servlet file into the subdirectory: d:\WebSphere\AppServer\....\servlets\itso\bank\persistence\jsp JSP Deployment for WebSphere Version 2 HTML and JSP files: d:\IBM HTTP Server\htdocs d:\IBM HTTP Server\htdocs\itso <== our subdirectory Servlets, JavaBeans, JSP configuration files: d:\WebSphere\AppServer\servlets\...package-subdirectories d:\WebSphere\AppServer\servlets\itso\bank\persistence\xxxx JSP Deployment for WebSphere Version 3 HTML and JSP files: d:\WebSphere\AppServer\hosts\default_host\default_app\web\ d:\WebSphere\AppServer\hosts\default_host\default_app\web\itso <== our subdirectory Servlets, JavaBeans, JSP configuration files: d:\WebSphere\AppServer\hosts\default_host\default_app\servlets\...package-subdir. d:\WebSphere\AppServer\hosts\default_host\default_app\servlets\itso\bank\persistence\ 424 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Deployment of Applications with Swing Many new applications use Swing functionality. Even if you do not use a Swing GUI, there is a good chance that your application uses Swing functionality; for example, data access beans and visual servlets with HTML result tables use the Swing table model. If you use any part of Swing, you must make sure that the Swing classes are in the class path. Swing provides several jar files with subsets of the classes and one file with all the classes. This complete file, swingall.jar, is also provided with VisualAge for Java in the IBMVJava\hpj\lib directory. To make it simple, add the swingall.jar files to every class path. Tailor the Web Browser Web browsers find classes in several ways. First they look through their own directory of classes, then they check the class path of the platform, and last they ask the Web server for classes. The jar files of VisualAge for Java are quite large and, instead of exploding them in the Web server, you can install the jar files in the browser’s directory. Netscape The Netscape browser locates jar files in this directory: d:\...NetscapeInstallDirectory...\Program\Java\Classes for example: c:\Program Files\Netscape\Program\Java\Classes Microsoft Internet Explorer Internet Explorer locates jar files in this directory: c:\Winnt\Java\classes c:\Windows\java\classes <=== Windows NT <=== Windows 95 Chapter 21. Deployment of Applications, Applets, Servlets, and Java Server Pages 425 426 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Appendixes © Copyright IBM Corp. 2000 427 428 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs A Special Notices This publication is intended to help VisualAge for Java developers develop enterprise applications with VisualAge for Java Enterprise Version 2 and Version 3. The information in this publication is not intended as the specification of any programming interfaces that are provided by VisualAge for Java Enterprise. See the PUBLICATIONS section of the IBM Programming Announcement for VisualAge for Java Enterprise 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 © Copyright IBM Corp. 2000 429 any license to these patents. You can send license inquiries, in writing, to the IBM Director of Licensing, IBM Corporation, North Castle Drive, Armonk, NY 10504-1785. 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 information about non-IBM ("vendor") products in this manual has been supplied by the vendor and IBM assumes no responsibility for its accuracy or completeness. 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 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. Any performance data contained in this document was determined in a controlled environment, and therefore, the results that may be obtained in other operating environments may vary significantly. Users of this document should verify the applicable data for their specific environment. This document contains examples of data and reports used in daily business operations. To illustrate them as completely as possible, the examples contain the names of individuals, companies, brands, and products. All of these names are fictitious and any similarity to the names and addresses used by an actual business enterprise is entirely coincidental. Reference to PTF numbers that have not been released through the normal distribution process does not imply general availability. The purpose of including these reference numbers is to alert IBM customers to specific information relative to the implementation of the PTF when it becomes available to each customer according to the normal IBM PTF distribution process. 430 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs The following terms are trademarks of the International Business Machines Corporation in the United States and/or other countries: AIX AT CT IBM MQ Netfinity NetView OS/390 RS/6000 SanFrancisco System/390 ThinkPad WebSphere 400 AS/400 CICS DB 2 IMS MQSeries NetRexx OS/2 OS/400 S/390 SP TeamConnection VisualAge XT The following terms are trademarks of other companies: Tivoli, Manage. Anything. Anywhere.,The Power To Manage., Anything. Anywhere.,TME, NetView, Cross-Site, Tivoli Ready, Tivoli Certified, Planet Tivoli, and Tivoli Enterprise are trademarks or registered trademarks of Tivoli Systems Inc., an IBM company, in the United States, other countries, or both. In Denmark, Tivoli is a trademark licensed from Kjøbenhavns Sommer - Tivoli A/S. C-bus is a trademark of Corollary, Inc. in the United States and/or other countries. Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and/or other countries. Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the United States and/or other countries. PC Direct is a trademark of Ziff Communications Company in the United States and/or other countries and is used by IBM Corporation under license. ActionMedia, LANDesk, MMX, Pentium and ProShare are trademarks of Intel Corporation in the United States and/or other countries. (For a complete list of Intel trademarks see www.intel.com/tradmarx.htm) UNIX is a registered trademark in the United States and other countries licensed exclusively through The Open Group. Chapter A. Special Notices 431 SET and the SET Logo are trademarks owned by SET Secure Electronic Transactions LLC. (For further information, see www.setco.org/aboutmark.html.) Other company, product, and service names may be trademarks or service marks of others. 432 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs B Related Publications The publications listed in this section are considered particularly suitable for a more detailed discussion of the topics covered in this redbook. © Copyright IBM Corp. 2000 433 International Technical Support Organization Publications For information on ordering these ITSO publications see “How to Get IBM Redbooks” on page 437. ❑ IBM WebSphere and VisualAge for Java Database Integration with DB2, Oracle, and SQL Server, SG24-5471 ❑ Developing an e-business Application for the IBM WebSphere Application Server, SG24-5423 ❑ Enterprise JavaBeans Development Using VisualAge for Java, SG24-5429 ❑ Using VisualAge Smalltalk ObjectExtender, SG24-5258 ❑ VisualAge for Java Enterprise Version 2: Data Access Beans - Servlets - CICS Connector, SG24-5265 ❑ Programming with VisualAge for Java Version 2, SG24-5264, published by Prentice Hall, ISBN 0-13-021298-9, 1999 (IBM form number SR23-9016) ❑ VisualAge for Java Enterprise Version 2 Team Support, SG24-5245 ❑ Using VisualAge for Java Enterprise Version 2 to Develop CORBA and EJB Applications, SG24-5276 ❑ VisualAge Java-RMI-Smalltalk, The ATM Sample from A to Z, SG24-5418 ❑ Using VisualAge UML Designer, SG24-4997 ❑ Application Development with VisualAge for Java Enterprise, SG24-5081 ❑ Creating Java Applications with NetRexx, SG24-2216 ❑ Unlimited Enterprise Access with Java and VisualAge Generator, SG24-5246 ❑ From Client/Server to Network Computing, A Migration to Java, SG24-2247 ❑ CBConnector Overview, SG24-2022 ❑ CBConnector Cookbook Volume 1, SG24-2033 ❑ Connecting the Enterprise to the Internet with MQSeries and VisualAge for Java, SG24-2144 ❑ Internet Application Development with MQSeries and Java, SG24-4896 434 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Redbooks on CD-ROMs Redbooks are also available on the following CD-ROMs. Click the CD-ROMs button at http://www.redbooks.ibm.com/ for information about all the CD-ROMs offered, updates and formats. CD-ROM Title System/390 Redbooks Collection Networking and Systems Management Redbooks Collection Transaction Processing and Data Management Redbook Lotus Redbooks Collection Tivoli Redbooks Collection AS/400 Redbooks Collection Netfinity Hardware and Software Redbooks Collection RS/6000 Redbooks Collection (BkMgr) RS/6000 Redbooks Collection (PDF Format) Application Development Redbooks Collection IBM Enterprise Storage and Systems Management Solutions Collection Kit Number SK2T-2177 SK2T-6022 SK2T-8038 SK2T-8039 SK2T-8044 SK2T-2849 SK2T-8046 SK2T-8040 SK2T-8043 SK2T-8037 SK3T-3694 Other Publications These publications are also relevant as further information sources: ❑ Developing JavaBeans with VisualAge for Java Version 2, SC34-4735 ❑ Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, published by Addison-Wesley Professional Computing Series, ISBN 0-201-63361, 1995 (IBM form number SR28-5629) Chapter B. Related Publications 435 436 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs How to Get IBM Redbooks This section explains how both customers and IBM employees can find out about IBM Redbooks, redpieces, and CD-ROMs. A form for ordering books and CD-ROMs by fax or e-mail is also provided. • Redbooks Web Site http://www.redbooks.ibm.com/ Search for, view, download or order hardcopy/CD-ROM redbooks from the redbooks web site. Also read redpieces and download additional materials (code samples or diskette/CD-ROM images) from this redbooks site. 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. • E-mail Orders Send orders via e-mail including information from the redbooks fax order form to: In United States Outside North America e-mail address [email protected] Contact information is in the “How to Order” section at this site: http://www.elink.ibmlink.ibm.com/pbl/pbl/ • Telephone Orders United States (toll free) Canada (toll free) Outside North America 1-800-879-2755 1-800-IBM-4YOU Country coordinator phone number is in the “How to Order” section at this site: http://www.elink.ibmlink.ibm.com/pbl/pbl/ • Fax Orders United States (toll free) Canada Outside North America 1-800-445-9269 1-403-267-4455 Fax phone number is in the “How to Order” section at this site: http://www.elink.ibmlink.ibm.com/pbl/pbl/ This information was current at the time of publication, but is continually subject to change. The latest information for customer may be found at the Redbooks Web site. IBM Intranet for Employees IBM employees may register for information on workshops, residencies, and redbooks by accessing the IBM Intranet Web site at http://w3.itso.ibm.com/ and clicking the ITSO Mailing List button. Look in the Materials repository for workshops, presentations, papers, and Web pages developed and written by the ITSO technical professionals; click the Additional Materials button. Employees may access MyNews at http://w3.ibm.com/ for redbook, residency, and workshop announcements. © Copyright IBM Corp. 2000 437 IBM Redbook Fax Order Form Please send me the following: Title Order Number First name Quantity Last name Company Address City Postal code Country Telephone number Telefax number VAT number Card issued to Signature Invoice to customer number Credit card number Credit card expiration date 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. 438 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs List of Abbreviations API application programming interface ASP Active Server Pages ATM automated teller machine AWT Abstract Windowing Toolkit CGI Common Gateway Interface CORBA Common Object Request Broker Architecture DBMS database management system DLL dynamic link library GUI graphical user interface HTML Hypertext Markup Language HTTP Hypertext Transfer Protocol IBM International Business Machines Corporation IDE integrated development environment ITSO International Technical Support Organization JAR Java archive JDBC Java Database Connectivity JDK Java Developer’s Kit JFC Java Foundation Classes JSDK Java Servlet Development Kit JSP Java Server Pages JVM Java Virtual Machine ODBC Open Database Connectivity PIN personal identification number RDBMS relational database management system RMI Remote Method Invocation SQL structured query language TCP/IP Transmission Control Protocol/Internet Protocol © Copyright IBM Corp. 2000 UOW unit of work URL uniform resource locator WWW World Wide Web 439 440 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs Index A account JSP 396 panel 326 selection 326, 357 servlet 341 table 271 AIX 9 allInstances method 84 allInstancesQuery method 225 allInstancesSqlString method 223 applet deployment 412 application controller see controller deployment 410 layers 24, 282 Association Editor 27, 45, 105, 115, 168 associations see relationships ATM application applet 330 business model 281 business model extension 302 concurrent processing 367 controller 283, 310 database 270 DDL 274 domain classes 298 flow 268, 350 GUI 319 JSP 375 JSP design 394 JSP implementation 391 JSP JavaBeans 392 JSP test 408 load SQL 279 panels 320, 321 requirements 268 run GUI 331 sample data 276 service classes 299 © Copyright IBM Corp. 2000 servlets 333 test servlet 364 ATMApplet 330 ATMApplicationController 313 ATMServletController 334, 352 Attribute Editor 45, 60 AWT 91, 97 AwtListModel 98 B backward development see bottom-up development bank account 285 beginChild method 144 beginReadOnly method 144 beginReadOnlyChild method 144 bottom-up development 38, 86, 114, 286 broken map 116 business model 284 rule implementation 124 transaction 146, 147 C caching 363 canMerge method 137 card class 303 JSP 395 layout 320 panel 323 servlet 334 table 272 cardinality 105 CGI 414 checkPin method 314 class ATMApplet 330 ATMApplicationController 313 ATMServletController 334 Card 303 CheckingAccount 308 Customer 302 SavingsAccount 309 class diagram 23 Class Editor 44, 60, 169 441 class path 410, 419 compute 94 WebSphere 416, 421 ClearCase 6 Column Editor 49, 74 column identifiers 101, 120 ColumnIdentifiers Editor 101 commit 33, 131 exception 255 complex attribute 215 Component Broker 9 composer 212 concurrent processing 367 transactions 132 conflict detection 136 Console 78 controller 283, 310, 321, 351 events 311 methods 310 servlet 333, 352 converter 217 custom methods 173 queries 221, 254 customer table 271, 272 verification 354 D data access beans 5 data store framework 35 database create 76 VAPSAMPL 76 Database Connection Info 77 dataFrom method 214 datastore activate 70 memory 71 reset 120 DB2 Java daemon 331, 353 JDBC drivers 14, 72, 77 trigger 140 user ID 276 DB2ADMIN 76 DB2JSTRT 77, 331, 353 442 DB2START 76 DB2STOP 76 DDL 77, 111, 273 delete cascade 112 deployment 409 applet 412 application 410 JSP 424 servlet 414 Swing 425 deposit 315 deposit method 315 development environment 25 paths 36 disabling caching 363 discriminator value 193 domain classes 65, 108 E ejbCreate method 123 Encina 7 Enterprise Access Builder 7 enterprise beans 8 Enterprise Toolkit 7 enterprise update 12 event aboutToGenerateOrTransfer 337 componentShown 323 execution context 71, 86 export 410, 412 F findByKeyQuery method 227 fixed custom query 231 foreign key 107, 114, 186, 200, 289 Foreign Key Relationship Editor 28, 49, 110, 121, 171, 200 forward development see top-down development framework 34, 283 G generate database schema 72 DDL 76 domain classes and interfaces 64 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs model and map 88 schema 109 schema and map 46 service classes 67, 79 getAccounts method 315 getCard method 314 getTransaction method 146 getTransactions method 317 GUI 282 H hardware 11 hidden field 347 high-performance compiler 7 home classes 66 I IDL 8 IIOP 8 import schema 86 inheritance 185, 292 initialize beans 123 initialize method 93 installation instructions 11 integrated development environment 4 Internet Explorer 425 isolation policies 132 ITSO ATM application 17 sample applications 14 ITSOBANK database 270 J JApplet 330 jar 416, 421 Internet Explorer 425 Netscape 425 Java Development Kit see JDK Java Server Pages see JSP JavaScript 341, 343 JDBC drivers 72 JDK 4 JFC see Swing JList 326 JPort 7 JSP 375 compile 388 deployment 424 examples 379 Execution Monitor 387, 389 program 381 tags 377 using a JavaBean 382 JSPController 399 JTable 328 K key class 65 L layout 320 list model 326 Lite Collections 46, 244 locking 132 Lotus Notes 9 M many-to-many relationships 165 Map Browser 50, 75, 88 broken mapping 116 introduction 29 window 51 mapping complex 205 composer 212 converter 217 framework 35 multiple table inheritance 198 root/leaf inheritance 201 secondary table 205 single table inheritance 187, 294 memory datastore 71, 120 merge method 137 metadata 35 Model Browser 43 introduction 26 new model 59 window 43 modeling framework 34 multithreading support 259 Index 443 N R nested transaction 31, 129, 154 Netscape 425 new model 59 nulls 74 read-only transaction 31, 71, 92 refresh from database 255 relationship framework 35 relationships 103 many-to-many 165 one-to-many 103 remote debugger 7 repeatable read 133, 142 repository 16 requestFocus method 323 reverse-engineer 286 RMI 7 rollback 33, 131 Root/Leaf Inheritance Table Map 202 runtime environment 26 O object identifier 62 object model 284 enhance 123 simple 57 object models 26 objectFrom method 214 one-to-many relationship 103 optimistic locking 132 optimistic predicate 138, 371 outside-in development 39 S P parameter markers 224, 226 performance 243 Persistence Builder concepts 21 frameworks 34 maps 28 tools 41 persistence layers 25, 81 personal identification number see PIN pessimistic locking 132, 142, 230, 237, 370 PIN 268, 314 JSP 396 panel 324 servlet 338 verification 356 preload path 251 promotion 322 property ATM business model 286 Property Map Editor 29, 52 PVCS 6 Q queries 223 query method 225 pool 221 444 sample code 14 SanFrancisco 9 SAP R/3 7 schema workspace 67 Schema Browser 47, 72 export schema 77 import 86 introduction 27 window 47 scrapbook 70, 318 Secondary Table Map 210 service classes 68, 81 servlet 333 ATM application 333 configuration file 406 controller 352 deployment 414 transaction binding policy 261 Servlet Builder 8 Servlet Runner 13 session data 340 shared transaction 30 Single Table Inheritance Map 193 SmartGuide generate options 64, 79 New Event Listener 313 save model 63 software 12 source code management 6 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs SQL Query Tool 53, 84, 196 window 53 SQL string method 224 SQL trace 55 Status Tool 54, 85, 153, 161 trace 55, 94 window 54 supportRepeatableReads method 133 supportUnrepeatableReads method 133 Swing 4, 91, 100, 319 deployment 425 T Table Editor 48, 73 team programming 6 TeamConnection 6 timestamp 140 tool integration 6 tools 41 top-down development 36, 58, 104 top-level transaction 31, 95, 130, 148 tracing 55 transaction deposit 358 framework 35 history 361 JSP 397 panel 328 servlet 345 table 272 withdraw 360 transactional thread 259, 261 transactions 129 binding policies 260 business 146 commit and rollback 34 concurrent 132 facts 144 introduction 30 nested 31, 129, 154 read-only 31 shared 30 top-level 31 variables 32 transient data 126 trigger 140 U UML 23 unrepeatable read 133, 142 user interface layer 282 V VapAttributeComposer 213 VapConverter 219 VapDefaultListModel 100 VapDefaultRelationshipTableModel 119, 158 VapDefaultTableModel 100, 148, 164, 240 VapLocalObjectLockedException 142, 143 VapMergeFailureException 137 VAPSAMPL database 76 VapTransactionRequiredException 96 VapTrimStringConverter 74, 297 variable custom query 235 Version 3 Enhancements 253 Visual Composition Editor 4 controller servlet 352 visual programming 91, 118, 180, 239 VisualAge for Java debugger 94 Enterprise 6 installation 12 Professional 4 Version 2 3 W Web browser 412, 425 server 412 WebSphere 8, 364, 416, 421 administration applet 416 administration client 421 class path 416, 421 connection pools 255 Java engine 418 servlet location 416, 421 test environment 12, 387 withdraw method 316 withdrawal 316 Workbench 410 workspace schema 67, 68, 113 Index 445 446 VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and JSPs IBM Redbooks Evaluation VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and Java Server Pages SG24-5426-01 Your feedback is very important to help us maintain the quality of IBM 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 _ None of the above _ IBM employee 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: © Copyright IBM Corp. 2000 (THANK YOU FOR YOUR FEEDBACK!) 447 SG24-5426-01 ® VisualAge for Java Version 3: Persistence Builder with GUIs, Servlets, and Java Server Pages SG24-5426-01 Printed in the U.S.A.