Java ACDP Tutorial

Opening

A database must be opened before it can be used and it must be closed when it is no longer used.

Creating a database by invoking the constructor of a class that extends the CustomDatabase class opens the database as a strongly typed database whereas creating an instance of the Database interface by invoking the Database.open method opens the database as a weakly typed database. For example, suppose our sample database which we created in Section Creating the Database of Chapter Creation is still saved in the dbDir directory. Then, with

   Path path = Paths.get("dbDir/layout");
and applying the try-with-resources statement
   try (EduDB db = new EduDB(path, -1, false, 0)) {
      // do something with db
   }
opens and closes the sample database as a strongly typed database, whereas
   try (Database db = Database.open(path, -1, false, null)) {
      // do something with db
   }
opens and closes the same database as a weakly typed database. (The parameters of the methods are explained in Section Parameters.)

The remaining sections of this page are

Strongly and Weakly Typed

Database queries make references to tables and columns and the result is typically a collection of values, each value being of a particular data type.

A strongly typed database has well-defined fields that refer to instances of strongly typed tables which in turn refer to instances of strongly typed columns. For example, in Chapter Design we defined the field TEACHER_TABLE in the EduDB class and the field POSTAL_CODE in the ContactTable class. So

   TeacherTable t = EduDB.TEACHER_TABLE;
   Column<Integer> c = ContactTable.POSTAL_CODE;
are valid assignments. Note that the value of a postal code is an instance of the Integer class. Any typos or wrong assumptions about the type of a column's value are reported at compile-time. (Try it by replacing Integer with a different type.) Moreover, the program code is completely independent of the table and column names so that these can be changed later without affecting client code. Changing the type of a column is no problem either because the compiler signals this change to the programmer by reporting a compilation error each time this column or a value of this column is used in the program code.

Unfortunately, we can't use these strongly typed fields if we open the database as a weakly typed database. In a weakly typed database, tables and columns are referenced by name and a value must often be cast to its assumed real type. For example, assume that the sample database was opened as a weakly typed database. Best we can do for assigning the Teacher table and the Postal Code column is:

   Table t = db.getTable("Teacher");
   Column<?> c = db.getTable("Contact").getColumn("PostalCode");
These methods do not return instances of the strong types TeacherTable and Column<Integer> but of the weak types Table and Column<?>, respectively. Note how the real type of a postal code is left open. You won't see any errors at compile-time if you misspelled the name of a table or column or if you did cast a postal code to a wrong type. Only at runtime will you notice sooner or later that your code does not behave as you expect. Even worse, the program code depends on table and column names, so any change in the name of a referenced table or column will silently break your code. The same applies if you change the type of a column and somewhere in your code lurks a cast of a value of that column to its real type.

Another point concerns the limitation to meet constraints resulting from the domain-specific data model. For example, the insertion of an arbitrary integer into the Postal Code column of the Contact table should be avoided, because not every arbitrary integer represents a postal code. Another example is the handling of contacts: As the UML diagram suggests, contacts should only be inserted into and removed from the Contact table in connection with students and teachers.

Such domain-specific constraints can be programmed in the custom database class and the custom table classes or outside of these classes, but using these classes. On the other hand, it is not possible to enforce domain-specific constraints if the database is opened as a weakly typed database.

After all, the reader might wonder why ACDP allows a database to be opened as a weakly typed database. The reason is that there are use cases that require reading and manipulating the data stored in a database in a general context that is not specific to its domain. One such use case is, for example, an application that outputs the content of any table in a human-readable manner.

Opening a database as a weakly typed database can be done from the information saved in the database layout only. (The database layout of a WR database is stored in the layout file whereas the database layout of an RO database is contained in the one and only one RO database file.) Therefore, ACDP does not need to know of a custom database class or of any custom table classes in order to open a database as a weakly typed database.

Parameters

The signatures of the methods for opening a database as a strongly typed database and a weakly typed database are
   CustomDatabase.open(Path mainFile, int opMode, boolean writeProtect,
                  ICipherFactory cipherFactory, int consistencyNumber,
                                           CustomTable... customTables)
and
   Database.open(Path mainFile, int opMode, boolean writeProtect,
                                    ICipherFactory cipherFactory)
respectively. The set of parameters of the latter method is a subset of the set of parameters of the first method. The semantic of each parameter is:
mainFile
The path—including the file name—of the main database file. The main database file of a WR database is the layout file whereas the main database file of an RO database is the one and only RO database file.
opMode
The mode of operation: -3 (RO only), -2 (RO only), -1, 0, > 0.
If the value is equal to zero then the backing table files (only one file if the database is an RO database) are immediately closed as soon as they become idle. If the value is positive and equal to n then the backing table files are closed max(10, n) milliseconds after they become idle or when the database is closed. If the value is equal to -1 then the backing table files once opened remain open until the database is closed. If the value is equal to -2 then the zipped and encrypted (if encryption is turned on) content of an RO database is completely mapped into memory. If the value is equal to -3 then the unzipped and decrypted (if encryption is turned on) content of an RO database is completely mapped into memory.
A value equal to zero is recommended only if the database is a WR database and the operating system can't sustain a bunch of files all being opened at the same time. In a server environment, a positive value is recommended or, provided that the database is an RO database and there is enough memory available, a value equal to -2 (less memory required, slower) or -3 (more memory required, faster).
writeProtect
Protects a WR database from being modified. If set to true then no data of the database can be inserted, deleted or updated. This parameter has no effect if the database is an RO database.
cipherFactory
The cipher factory or null if no encryption is required. See Section Coding a Cipher Factory of Chapter Design for an example of a cipher factory.
consistencyNumber
The consistency number. The consistency number is stored in the database layout. If the value of this parameter does not match the stored value then opening the database fails. The purpose of this parameter is to link a specific version of the domain-specific database logic to a matching version of the data in the database. The Setup Tool which we learned about in Chapter Creation sets the value of the consistency number to zero when it creates the layout file.
customTables
The list of custom tables. The value of this parameter is predefined by the concrete custom database class and is not intended to be chosen by the database user.
Back To Top