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 Database
4odb 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.odl
module 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.py
import 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