Developers Manual 1.20

1. Defining the Object Model

1.7 Creating and Maintaining Tables

Once you have successfully defined the classes forming your object model, you are ready to begin working with ReStore. Firstly, you will need to create a new, empty database - exactly how this is done will depend on your chosen brand of database; refer to its documentation for further details. 

The next step is to create an ODBC connection to that database. Again, the details of this will be dependent on your chosen database; from ReStore's point of view the most important thing to note is the Data Source Name or DSN you assign to the ODBC connection.

 

SSWReStore
An instance of SSWReStore is the heart of the ReStore system - it controls access to the database, and handles the creation, maintenance and recovery of database objects. To begin, you will need to create an SSWReStore instance and connect it to your new database: 

reStore := SSWReStore new.
reStore useTransactionsWithoutVersioning.    "See later for details of this"
reStore dsn: '<your chosen DSN>'
reStore connect

Once you have created your ReStore and connected, then you will need to tell it which classes are to be persistent - these are the classes for which you have defined class definition methods. To do this, you will use the methods addClass: and, to add a whole hierarchy of classes, addClassWithSubclasses:. Example (from the Entertainment Shop package):

reStore
        addClass: CDTrack;
        addClass:
Customer;
   
     addClass: Order;
   
     addClass: PostalAddress;
        addClass:
OrderItem;
        addClassWithSubclasses: StockItem

When a class is added to ReStore, the names of the database tables and columns are created automatically, based on the given class and instance variable names; e.g.

Smalltalk Name Database Name
Customer CUSTOMER
        surname SURNAME
        firstName FIRST_NAME
StockItem STOCK_ITEM
        title TITLE
        numberInStock NUMBER_IN_STOCK

Unfortunately, not all derived names are valid - in the Entertainment Shop example, the class Order would normally have a derived table name of ORDER. However, this is a reserved word in most databases, and so ReStore will automatically change it to ORDER_TABLE. 

As the list of reserved words varies from database to database, it is important to add the classes after you have connected to the database. This means that ReStore can check each derived name against the list of reserved words for that particular database, and modify names where required.

Limits on Size of Names   
Most databases place a limit on the size of table names and field names. Usually this is not a problem (64 characters is a common limit), however on the rare occasions when it does occur, ReStore allows you to overcome the limit.

Table Names
In the case of oversize table names, ReStore will automatically substitute a shorter table name in a similar manner to how Windows generates an MS-DOS compatible 8 character filename from a long filename. Thus 'OVERSEASCUSTOMER' might become 'OVERSEAS_1', if it were subject to a 12 character limit. ReStore keeps track of substituted table names within a special database table called NEXT_ID. Each table created by ReStore has an entry in this table, whose primary purpose is to maintain the unique ID sequence for each table. In the entry for this table, ReStore will record the full-length table name, plus the assigned (shorter) substitute.

Note: this functionality is new in version 1.20 and requires a change to the NEXT_ID table compared to previous versions. For this reason, support for long table names is not enabled by default. This allows existing users to choose whether to enable it: to do so, the following message should be sent to a connected ReStore:

aReStore enableFullTableRecords

This will update the existing NEXT_ID table. 

For new development, it is suggested that full table name support is enabled when initially defining your database. To do so, the basic ReStore initialisation script becomes:

reStore := SSWReStore new.
reStore useTransactionsWithoutVersioning.
reStore dsn: '<your chosen DSN>'
reStore supportsFullTableRecords: true.
reStore connect

 

Field Names
In the case of oversize field names, ReStore will not attempt to generate a unique field name (keeping track of these would be much more complex compared to table names). Instead, ReStore will report a diagnostic error when you attempt to addClass: for a Class whose instance variable(s) generate an oversize field name. To resolve this, you will be asked to provide a shorter abbreviation for the offending name(s). This is done within the class definition method. Example (with a theoretical limit of 12 characters):

define: #numberOfChildren as: Integer

Error (received when adding the class): 
Column NUMBER_OF_CHILDREN' for #numberOfChildren is larger than max length of 12.
Define a suitable abbreviation

define: #numberOfChildren abbreviation: #childCount as: Integer

 

Creating Tables
You now have a ReStore object, connected to the database and containing all required classes and equivalent table names. However, none of these tables exist in the database. Fortunately, ReStore can create all the tables for you with one simple instruction:

reStore synchronizeAllClasses

 

Maintaining Tables    
One of the advantages of Smalltalk is its highly interactive nature, which allows you to rapidly develop and refine applications. Over time, your classes are likely to change significantly as you redevelop and refactor your code. Unfortunately, this evolutionary development process can leave stored data (in files, or a relational or object database) in an incompatible state, requiring manual intervention or additional coding to bring it up to date with your current object model.

ReStore helps overcome these problems and preserve the rapid development benefits of Smalltalk by automatically redesigning your database tables to match your object model. When you have made changes to your object classes, simply re-add them to your ReStore (as above) and re-evaluate

reStore synchronizeAllClasses

Resynchronizing in this way allows ReStore to create new tables (for new classes), add new columns (for new instance variables) and remove redundant columns (where you have removed instance variables). Importantly, these changes are carried out in the database without the loss of your existing data. 

 

Renaming a Class    
A change that ReStore cannot handle automatically with synchronizeAllClasses is where you have renamed a class. In this case, if you were to use synchronizeAllClasses ReStore would simply add an empty table with a name based on the new name of the class, leaving the old table (and its data) in the database, but inaccessible. To overcome this, ReStore allows you to explicity state when you have renamed a class. As an example, let's say you renamed the class PostalAddress to Address:

reStore renamedClass: Address from: #PostalAddress

Using this technique, the original table for PostalAddress would simply be renamed to match Address, preserving all its data.

 

Removing Classes
Similar to renaming a class, you must use a specific message to tell ReStore that you no longer require a particular class in your data model (and that its corresponding table can be removed from the database):

reStore destroyClass: <redundant class>

In more drastic situations (e.g. to purge all data from a database), you may evaluate:

reStore destroyAllClasses

Note that this will only remove from the database tables associated with classes known to that ReStore instance (those that have been added with addClass:/addClassWithSubclasses:).

 

Renaming an Instance Variable     
A further change that cannot by handled directly via synchronizeAllClasses is where an instance variable has been renamed. Using synchronizeAllClasses in this case would cause ReStore to add a new (empty) column for the new instance variable, and delete the previous column - with the loss of all data contained in that column. 

Similar to renaming a class, ReStore offers a simple message which can be used to inform it of the change of instance variable name and instruct it to update the database structure accordingly. Let's say you have renamed the Customer instance variable firstName to forename; you would inform ReStore of this change as follows:

reStore renamedInstVar: #forename from: #firstName in: Person

This method will add a new column for forename, copy over all values from the firstName column, then finally remove the firstName column. This replaces the suggested workaround used in version 1.00 of ReStore.

 

Setting Default Values
ReStore provides a powerful but efficient mechanism for performing bulk updates of data - the method modify:,  discussed in more detail here. A use for modify: in maintaining your data model is in setting default values for new instance variables. Let's say you have a large database of existing Customer objects. You have just extended your Customer class to include a new instance variable isDeceased, and have resynchronized your classes to include a column for this in the database. However, your existing Customers will all have isDeceased equal to nil. To set these to false, you can simply say:

(reStore instancesOf: Customer) modify: [ :each | each isDeceased: false]

 

1. Defining Classes

2000-2003 Solutions Software Ltd.

Home | Index | Chapter 2