This document will attempt to walk users of 4ODS through many of the common use cases of working with 4ODS. Each section will describe using a different facet of the library. Each will have a coresponding file of example code that will be referenced and explained.
4ODS is an object database wrapper around various forms of back end persistent stores. Someday it will have it own storage capabilities, but until then we build off of some of the excelent RDBMS available. Currently we do support connecting to Postgres, Oracles through optimized adapters, and any ODBC compliant database through the ODBC adapter.
Also, because not everyone has Oracle, or Postgres, 4ODS ships with a Dbm driver. This driver works great for simple tasks, but it is not concurrent so don't use it in a multi-threaded application.
To set which driver 4ODS uses, you define the environment variable FTODS_DB_DRIVER to the name of the database adapter. The default is Dbm. To set the driver to Postgres you would issue the following:
export FTODS_DB_DRIVER=Postgres
or on windows
set FTODS_DB_DRIVER=Postgres
4ODS has a database object that conforms to the ODMG Database interface. To access the database, import the module Database and create an instance of the database. The database instance is basically a transaction factory. In a ODMG compliant database, there is only on active transaction per thread. This can get a bit confusing some times and we will cover it in more detail in a later sections.
from Ft.Ods import Database db = Database.Database() db.open("DBNAME") db.close()
You must be very careful to close your database when you are done with it in Python 1.5.2 because it contains circular references with all of the transactions that it creates. In the above code, we created a Database instance, and opened it. The parameter to the open command is the name of the database. The above code will run even if you do not have a database called "DBNAME". This is because 4ODS does not attempt to connect to the database until you begin a transaction.
to create a transaction, you use the "new" interface on a opened database. At this time you will need to make sure that you don't have the environment adapter envinroment variable set, and the Dbm database directory exists. The location of where Dbm will store you data can be found with the following script. You will need to make sure this directory exists and that you have access to it.
[molson@penny 4Suite]$ python -c "from Ft.Lib import DbmDatabase;print DbmDatabase.DATABASE_DIR"
You will then need to create an empty 4ods database. To do this you can use the command line command. Unless otherwise specified in this tutorial, we will use the name "test" as the name of our database. We will also be reinitializing the database between each demonstration script.
Create a new Database4odb create test
4odb destroy test
With a new database, we can run the tutorial script transaction.py
transaction.pyimport sys from Ft.Ods import Database db = Database.Database() db.open(sys.argv[1]) tx = db.new() tx.begin() #This is where you do all of your transaction processing tx.commit() db.close()
We would then put our code to access and modify the database inbetween the begin and commit of the transaction. If, after doing your processing, you do not want to commit you changes to the persistent store, you can call abort on the transaction.
Before we can use a 4ODS database, we must define what types of objects can be stored in the system. This is very similar to defining your table structure in a RDBMS, or you interfaces in a CORBA system. To perform this task, we use a langauge called ODL. This is very simialr to CORBA's IDL. With it, we can define interfaces, classes, structures, enumerations, unions, collections and more. From this, 4ODS will initialize the database's meta model, and generate a series of "pstubs" (more on these later)
One of the most powerful features of a ODMG compliant database is its ability for introspections. Stored in the database is the entire set of meta relationships defined by the ODL that was used to create the database. A standard set of interfaces is defined for accessing (and on really impressive ODMG systems modifying) the meta model of the system run time. It goes as far as allowing you to use these meta objects in the same transactions as other the pstub objects.
simple.odlmodule Test { class Person { attribute string name; attribute short age; }; };
The above file, simple.odl, is a very simple example of an ODL file, but it will sufice for our current purposes. In it, we define a module and a class. The module is used to scope names, and the class defines an object type that can be stored in the database.
To initialize the "test" database with this ODL, we use the command line script 4odb init. Below is a sample output from a Unix terminal when this is run (Windows output is very similar):
[molson@penny tutorial]$ 4odb destroy test [molson@penny tutorial]$ 4odb create test [molson@penny tutorial]$ 4odb init test simple.odl
4ODS comes with a command line tool that allows you to view a 4ODS databases meta model. The command is called "4odb metadig". It takes one parameter, the name of the database to view. Once run, it will prompt you for a name to resolve in the metamodel, then it will pretty print that object. Now is probably a good time to note that the ODMG specification defines that all user objects (defined through ODL) are in a modele called ODLMetaObjects. There for, when we run the metadig command and query the object "ODLMetaObjects" we see a module, with our module (and class) defined inside of it
Output from 4odb metadig test[molson@penny tutorial]$ 4odb metadig test 4ODS MetaDig Tool Version 0.11.1b2 Copyright Fourthought, Inc 2001 >>> ODLMetaObjects Module: ODLMetaObjects Name: ODLMetaObjects Comment: Created by Initialization of Repo Defined In: Repository id(2) Defines: Module: ODLMetaObjects::Test Name: Test Comment: Defined In: ODLMetaObjects id(21) Defines: Class: ODLMetaObjects::Test::Person Extent: None Keys: None Extender: None Extensions: None Inherits: None Derives: None Of Collection: None Of Specifier: None Of Union: None Of Operation: None Of Property: None Of constant: None Of TypeDefs: None Defines: Attribute: ODLMetaObjects::Test::Person::name is_read_only: 0 type: string Name: name Comment: Defined In: ODLMetaObjects::Test::Person id(23) Attribute: ODLMetaObjects::Test::Person::age is_read_only: 0 type: short Name: age Comment: Defined In: ODLMetaObjects::Test::Person id(24) >>>
You are not limited by the command line if you want to view the meta model of a database. The Database interface defines a method called "schema". We can use it to get the root of the Meta Model. The next example script metadata.py shows how to do this. It then recursively prints out all of the classes and modeules in the system.
metadata.pyimport sys from Ft.Ods import Database from Ft.Ods.MetaData import MetaKind def pprint(ds,indent = ''): print indent + ds.absolute_name() for d in ds.defines: if d.meta_kind in [MetaKind.mk_module, MetaKind.mk_class]: pprint(d,indent + ' ') def run(): db = Database.Database() db.open('test') tx = db.new() tx.begin() repo = db.schema() pprint(repo) tx.abort() db.close() if __name__ == '__main__': run()
[molson@penny tutorial]$ python metadata.py Repository ODLMetaObjects ODLMetaObjects::Test ODLMetaObjects::Test::Person ODLTypes