Download VisualAge for Java Version 3: Persistence

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Computational fluid dynamics wikipedia , lookup

Entity–attribute–value model wikipedia , lookup

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