1. Include <odmg/odmg.h>
.
2. Link with a C++/VERSANT library.
The following methods are changed to follow the ODMG standard.
|
|
|
|
|
|
|
|
|
|
|
|
int
instead of o_bool
:
int is_null();
int operator!();
LinkAny(d_Object*);
LinkAny &operator=(d_Object *);
LinkAny(d_Object* baseLink );
There may be some conflicts with the old code due to this constructor. Code such as:
LinkAny link=NULL;
will no longer work, because the compiler cannot resolve the overloaded functions.
This means that you will either have to be more specific in specifying the constructor or change the previous code to:
LinkAny link;
In the above, the default constructor will set the value to NULL
anyway.
ODMG C++/VERSANT also incorporates the ODMG 2.0 C++ binding standard for the d_Database
and d_Transaction
classes. The ODMG 2.0 C++ binding standard does not specify how a transaction is associated with a database, thereby allowing access to only one database at a time. The VERSANT implementation provides multi-threaded transactions capabilities with access to single and multi-databases.
ODMG C++/VERSANT is a thin layer on top of C++/VERSANT which implements the ODMG object model. When you use ODMG C++/VERSANT, you can still use C++/VERSANT and/or C/VERSANT methods not provided by the ODMG-93 standard.
Although most VERSANT classes are supported, we suggest that you follow ODMG standards and use VERSANT classes as extensions only as needed.
The signatures of ODMG methods may differ from those of equivalent VERSANT methods.
|
|
PClass
|
Yes
|
PDOM
|
Yes
|
PObject
|
No — use d_Object
|
VSession
|
Yes
|
BiLink<type>
|
Yes — or use d_Rel_Ref<type>
|
BiLinkVstr<type>
|
Yes — or use d_Rel_Set<type> or d_Rel_List<type>
|
Link<type>
|
Yes — alias for d_Ref<type>
|
LinkAny
|
Yes — alias for d_Ref_Any
|
LinkVstr<type>
|
Yes
|
LinkVstrAny
|
Yes
|
PVirtual
|
Yes — alias for d_Object
|
VCursor
|
Yes
|
Vstr<type>
|
Yes
|
VstrAny
|
Yes
|
PList<type>
|
Yes
|
|
|
Vstr<d_Ref><type>>>
|
Yes — all vstr methods
|
|
|
PClassObject<type>
|
Yes
|
PClassObjectIterator
|
Yes
|
PCODIterator
|
Yes
|
PError
|
Yes — alias for d_Error
|
PLock
|
Yes
|
|
|
o_1b
|
Yes
|
o_2b
|
Yes
|
o_4b
|
Yes
|
o_8b
|
Yes
|
o_bcd
|
Yes
|
o_be_profile
|
Yes
|
o_bool
|
Yes
|
o_clsname
|
Yes
|
o_clsname_const
|
Yes
|
o_connectinfo
|
Yes
|
o_date
|
Yes
|
o_dblistdesc
|
Yes
|
o_dbname
|
Yes
|
o_dbname_const
|
Yes
|
o_double
|
Yes
|
o_envinfo
|
Yes
|
o_event_msg
|
Yes
|
o_fe_alias
|
Yes
|
o_fe_profile
|
Yes
|
o_float
|
Yes
|
o_indextype
|
Yes
|
o_interval
|
Yes
|
o_lockmode
|
Yes
|
o_opts
|
Yes
|
o_time
|
Yes
|
o_timestamp
|
Yes
|
o_u1b
|
Yes
|
o_u2b
|
Yes
|
o_u4b
|
Yes
|
o_u8b
|
Yes
|
o_udllockmode
|
Yes
|
o_userdesc
|
Yes
|
o_vsnstatus
|
Yes
|
|
|
AS()
|
Yes
|
assertClass()
|
Yes
|
assertSpecies()
|
Yes
|
EOS
|
Yes
|
L_AS()
|
Yes
|
NULL
|
Yes
|
NULL_LINK
|
Yes
|
NULL_PREDICATE
|
Yes
|
NULL_VSTR
|
Yes
|
O_COMBINE_LOCK()
|
Yes
|
O_EXTERNAL_LOCK()
|
Yes
|
O_INTERNAL_LOCK()
|
Yes
|
O_IS_EXPLICIT_LOCK()
|
Yes
|
O_NEW_PERSISTENT()
|
Yes
|
O_NEW_PERSISTENT1()
|
Yes
|
O_NEW_PERSISTENT2()
|
Yes
|
O_SET_EXPLICIT_LOCK()
|
Yes
|
VPP_CLASS_CONSTRUCTOR1()
|
Yes
|
VPP_CLASS_CONSTRUCTOR2()
|
Yes
|
|
|
Error Functions | |
set_panic()
|
Yes
|
set_terminate()
|
Yes
|
set_unexpected()
|
Yes
|
terminate()
|
Yes
|
unexpected()
|
Yes
|
vpp_break()
|
Yes
|
|
|
Error Macros | |
|
|
Error Types | |
o_err
|
Yes
|
|
|
Error Variables | |
VPP_PANIC_HANDLE
|
Yes
|
VPP_PFV
|
Yes
|
vpp_terminate_where
|
Yes
|
|
|
d_VQL_Query
|
Yes — similar to OQL
|
PAttribute
|
Yes
|
PBuffer
|
Yes
|
PPredicate
|
Yes
|
|
|
!=()
|
Yes
|
<()
|
Yes
|
<=()
|
Yes
|
==()
|
Yes
|
>()
|
Yes
|
|
|
PString
|
Yes — alias for d_String
|
VString
|
Yes
|
VDate
|
Yes — or use d_Date
|
|
|
VElemental
|
Yes
|
Container
|
Yes
|
V*Iterator<type>
|
Yes
|
V??Dictionary<k,v>
|
Yes
|
V??DictionaryIterator<k,v>
|
Yes
|
V?Array<type>
|
Yes — or use d_Varray<type>
|
V?ArrayIterator<type>
|
Yes — or use d_Varray<type>
|
V?List<type>
|
Yes — or use d_List<type>
|
V?ListIterator<type>
|
Yes — or use d_List<type>
|
V?Set<type>
|
Yes — or use d_Set<type>
|
V?SetIterator<type>
|
Yes — or use d_Set<type>
|
|
|
PCustomizeFundamental
|
Yes
|
VRTAttr
|
Yes
|
VRTAttrDesc
|
Yes
|
VRTBufDesc
|
Yes
|
VRTClass
|
Yes
|
VRTClassDesc
|
Yes
|
|
|
The ODMG classes have all ODMG specific methods and may have additional VERSANT methods. The signatures of ODMG methods may differ from those of equivalent VERSANT methods.
|
|
d_Database
|
|
d_Object
|
Aliased to PVirtual
|
|
|
d_Ref_Any
|
Aliased to LinkAny
|
d_Ref<type>
|
Aliased to Link<type>
|
d_Rel_List<type, member>
|
Extended by VERSANT
|
d_Rel_Ref<type, member>
|
Extended by VERSANT
|
|
|
|
|
d_Date
|
— or use VDate
|
d_Time
|
— or use VTime
|
d_Timestamp
|
— or use VTime
|
d_String
|
Aliased to PString
|
|
|
d_Bag<type>
|
Extended by VERSANT
|
d_Iterator<type>
|
Extended by VERSANT
|
d_List<type>
|
Extended by VERSANT
|
|
|
You can invoke standard VERSANT database management methods through the pointer returned from the get_session()
method in the d_Transaction
class.
You can use extended VERSANT methods in otherwise standard ODMG classes.
You can also use C++/VERSANT specific classes.
You can invoke standard VERSANT database methods defined on PDOM
by using the global variable ::dom
.
VSession
by using a get_session()
method on d_Transaction
.For example:
d_Database *db1 = new d_Database;
db1->open("mydb");
d_Transaction *txction = new d_Transaction;
txction->begin();
/* create a Vstr of 5 elements */
LinkVstr<ClassA> v;
d_Ref<ClassA> r;
for (int i = 0; i < 5; ++i)
{ r = new (db1, "ClassA") ClassA();
v.add(r);
}
/* use PDOM::gwriteobjs() */
txction->get_session()->gwriteobjs(v, O_FREE_OBJECTS);
txction->checkpoint();
/* use PDOM::gdeleteobjs() */
LinkVstrAny failed, timestamp;
txction->get_session()->gdeleteobjs(
v,
db1->get_this_db_name(),
&failed, ×tamp, TRUE);
txction -> commit();
delete txction;
db1->close();
select()
method, which is not defined in ODMG but is similar to the select()
method in the VERSANT collection classes.The following example shows use of VERSANT extended methods.
#include "ClassA.h"
#include <odmg/set.h>
#include <iostream.h>
extern "C" void exit(int);
int ClassA::count = 0;
main(int argc, char *argv[])
{ if ( argc < 2 )
{ cout << "Usage: "
<< argv[0]
<< " dbname [ message ... ]"
<< endl;
exit(1);
}
d_Database test_db;
test_db.open (argv[1]);
d_Transaction *txction = new d_Transaction ();
txction->begin ();
/* create a set with 5 elements
*/
d_Ref<d_Set<d_Ref<ClassA> > > setRef =
new (test_db, "d_Set<d_Ref<ClassA>>") d_Set<d_Ref<ClassA> >();
for (i = 0; i < 5; ++i)
{ d_Ref<ClassA> r = new (test_db, "ClassA") ClassA;
setRef->insert_element(r);
}
/* commit the transaction
*/
txction->commit();
d_Set<d_Ref<ClassA> > set2;
/* do a select on the d_Set<d_Ref<ClassA> > object
and put the results into another d_Set<d_Ref<ClassA> > object
*/
set2 = (VstrAny) setRef->select(&test_db, 0, 0, NULL_PREDICATE);
iter = set2.create_iterator();
for (; iter.not_done(); iter++)
{ cout << (*iter)->id << endl;
}
txction -> commit();
test_db.close ();
delete txction;
}
For example, to iterate through the COD table:
PCODIterator cod_iter;
int no_of_cod = cod_iter.getnumberofcods();
cout << "# of PCOD: " << no_of_cod << endl;
for (i = 0; i < no_of_cod; ++i)
(void) cod_iter.get_next();
cod_iter.reset();
PDOM
by using a global variable name ::dom
. The ::dom
global variable is created for you the first time you call d_Transaction::begin()
. However, if you are using multiple sessions and multiple threads, always use the get_session()
method instead of the ::dom
global variable for thread protection.For example:
d_Database *db1 = new d_Database;
db1->open("mydb");
d_Transaction *txction = new d_Transaction;
txction->begin();
/* create a Vstr of 5 elements */
LinkVstr<ClassA> v;
d_Ref<ClassA> r;
for (int i = 0; i < 5; ++i)
{ r = new (db1, "ClassA") ClassA();
v.add(r);
}
/* use PDOM::gwriteobjs() */
::dom->gwriteobjs(v, O_FREE_OBJECTS);
txction->checkpoint();
/* use PDOM::gdeleteobjs() */
LinkVstrAny failed, timestamp;
::dom->gdeleteobjs(
v,
db1->get_this_db_name(),
&failed, ×tamp, TRUE);
txction -> commit();
delete txction;
db1->close();
begin()
is called for the first time, a hidden VSession
object is created for the transaction to allow multiple threads to operate on the transaction. This means that each transaction has its own object cache. Each thread joining the same transaction shares the same VERSANT session. When a d_transaction
is destroyed, the session is also ended.To support multiple databases with multiple transactions, we have introduced the concept of a process/thread default database:
The first database opened in a process becomes the process default database.
The first database opened in a thread becomes the thread default database.
When a d_Transaction::begin()
is called without any arguments, the hidden session begins using the thread default database as the seesion database. If the thread default database is not found, the process default database then is used. Users can also specify a d_Database
as the session database by passing its reference to the d_Transaction
constructor.
A transaction can associate itself with another database by calling the connect()
method and disassociate with it by calling disconnect()
. However the default database can never be disconnected because it is the session database.
The following graph shows the physical relationship between d_Database
, d_Transaction
, and VSession
.
d_Object
rather than the VERSANT PObject
class.
Although the ODMG class d_Object
is aliased to the VERSANT class PVirtual
, which is a subclass of PObject
, in ODMG C++, you should never directly use class PObject
.
d_Database*
, as their first argument rather than a database name, const char*
.The following methods are affected:
d_Set::select()
d_Bag::select()
d_List::select()
d_Varray::select()
d_Refs
to transient objects to the null value." ODMG C++/VERSANT currently doesn't support this. In fact, an application will crash when this happens.
Vstr<type>
d_Set<type>
d_List<type>
d_VArray<type>
d_Bag<type>
type
, key
, or value
substitution parameters:
Elemental Types |
|
Link Types |
|
d_Rel_List<type>
, d_Rel_Ref<type>
, and d_Rel_Set<type>
, you must define the variables used to refer to the other end of the relationship in a .imp
schema implementation file. This will let the schema compiler know the values of the two variables while parsing the class definitions.For example:
a.h
extern const char _ra[], _rb[];
class A : public d_Object { d_Rel_Ref<B,_ra> rb; };
class B : public d_Object { d_Rel_Ref<A,_rb> ra; };
schema.imp
#include "a.h"
const char _ra[] = "ra";
const char _rb[] = "rb";
O_CAPTURE_SCHEMA(A);
O_CAPTURE_SCHEMA(B);
O_CAPTURE_SCHEMA(d_Rel_Ref<A,_rb>);
O_CAPTURE_SCHEMA(d_Rel_Ref<B,_ra>);
d_Ref<type>
and d_Ref_Any
are not implemented, because they conflict with the original C++/VERSANT implementation. For each of these methods, the same functionality is provided in C++/VERSNT.
Not implemented on d_Ref<type>
:
boolean predicate to check reference:
operator const void *() const;
friend int operator == ( const d_Ref<type>& refL,
const d_Ref<type>& refR );
friend int operator == ( const d_Ref<type>& refL,
const type* ptrR );
friend int operator == ( const type* ptrL,
const d_Ref<type>& refR );
friend int operator == ( const d_Ref<type>& refL,
const d_Ref_Any& anyR );
friend int operator == ( const d_Ref_Any& anyL,
const d_Ref<type>& refR );
friend int operator != ( const d_Ref<type>& refL,
const d_Ref<type>& refR );
friend int operator != ( const d_Ref<type>& refL,
const type* ptrR );
friend int operator != ( const type* ptrL,
const d_Ref<type>& refR );
friend int operator != ( const d_Ref<type>& refL,
const d_Ref_Any& anyR );
friend int operator != ( const d_Ref_Any& anyL,
const d_Ref<type>& refR );
d_Ref_Any
:boolean predicate to check reference:
operator const void *() const;
friend int operator == ( const d_Ref_Any& anyL,
const d_Ref_Any& anyR );
friend int operator == ( const d_Ref_Any& refL,
const d_Object* ptrR );
friend int operator == ( const d_Object* ptrL,
const d_Ref_Any& anyR );
friend int operator != ( const d_Ref_Any& anyL,
const d_Ref_Any& anyR );
friend int operator != ( const d_Ref_Any& refL,
const d_Object* ptrR );
friend int operator != ( const d_Object* ptrL,
const d_Ref_Any& anyR );
private
to preserve referential integrity.
The following methods in d_Rel_Ref<T,
M>
are private:
d_Rel_Ref ( d_Rel_Ref<T, M>& );
d_Rel_Ref ( const d_Ref_Any& );
d_Rel_Ref<T, M>& operator = ( const d_Rel_Ref<T, M>& );
d_Rel_List<T,
M>
are private:
d_Rel_List ( o_u4b );
d_Rel_List ( o_vstr );
d_Rel_List<T, M>& operator = ( const d_Rel_List<T, M>& list );
d_Rel_List<T, M>& operator = (o_vstr);
d_List<d_Ref<T> >& operator += ( const d_List<d_Ref<T> > &ListR)
d_List<d_Ref<T> >& append ( const d_List<d_Ref<T> >&);
void set ( const d_List<d_Ref<T> >&);
d_Rel_Set<T,
M>
are private:
d_Rel_Set ( const d_Rel_Set<T,M>& );
d_Rel_Set ( VstrAny );
d_Rel_Set<T, M>& operator = ( VstrAny );
d_Rel_Set<T, M>& operator = ( const d_Rel_Set<T, M>& oth );
d_Set<d_Ref<T> >& operator += ( const d_Set<d_Ref<T> >& s2);
d_Set<d_Ref<T> >& operator -= ( const d_Set<d_Ref<T> >& s2);
d_Set<d_Ref<T> >& operator *= ( const d_Set<d_Ref<T> >& s2);
void set ( const d_Set<d_Ref<T> > &);
d_Set<d_Ref<T> >& difference_of ( const d_Set<d_Ref<T> >& l,
const d_Set<d_Ref<T> >& r);
d_Set<d_Ref<T> >& difference_with ( const d_Set<d_Ref<T> >& s2);
d_Set<d_Ref<T> >& intersection_of ( const d_Set<d_Ref<T> >& l,
const d_Set<d_Ref<T> >& r);
d_Set<d_Ref<T> >& intersection_with( const d_Set<d_Ref<T> >& s2);
d_Set<d_Ref<T> >& union_of ( const d_Set<d_Ref<T> >& l,
const d_Set<d_Ref<T> >& r);
d_Set<d_Ref<T> >& union_with ( const d_Set<d_Ref<T> >& s2);
hash()
and compare()
virtual methods in a persistent class to expedite ODMG collection operations.
Default hash()
and compare()
methods are defined in d_Object
which performs the hash and comparisons based on addresses and cached object descriptor entries. In collection methods which involve searches, you may want to implement your own hash()
and compare()
methods.
d_Object
, you must add the macro VPP_CLASS_CONSTRUCTOR
macro to your constructors.
For example, you can use d_Database::set_object_name()
to bind a name to an object. VERSANT will then create a hidden VAssociate
object which contains the name and reference to the object. These VAssociate
objects are then used when you search for named objects.
When a named object is migrated to another database, the VAssociate
objects which refer to it are not migrated.
If you want to migrate the names, you can use the VAssociate::find_link()
method to find all VAssociate
objects which refer to the migrated object and then migrate them.
VERSANT automatically creates and maintains a unique B-tree index on the VAssociate::name
attribute in order to guarantee unique object naming.
Object names are created within the current transaction when you invoke the method d_Database::set_object_name()
. Internally, the VAssociate
object, which contains the name and reference to the named object, is created within the current transaction. If the current transaction is not connected to the d_Database
object where the method set_object_name()
is invoked, an exception will be thrown.
As in OQL, you can use VQL to construct a d_VQL_Query
query object, use parameter substitutions, and then call the function d_oql_execute()
to execute the query.
The only major difference between OQL and VQL is that, in VERSANT, the d_oql_execute()
function only returns results to types Vstr<type>
, LinkVstr<type>
, or LinkVstrAny
. By comparison, in OQL, d_oql_execute()
can return a collection or a d_Ref<type>
.
For a reference to VQL, see the chapter "Queries with VERSANT Query Language" in the VERSANT Database Fundamentals Manual.
PDOM
methods, such as beginsession()
, commit()
, and abort()
, do not make sense in ODMG, since in the ODMG object model they belong to the d_Database
or d_Transaction
class. You should be aware of them and use corresponding methods in d_Database
or d_Transaction
instead.
The following PDOM
methods should not be used in ODMG C++ applications.
abort()
beginsession()
commit()
connectdb()
detachsession()
disconnectdb(),
endsession()
joinsession()
rollback()
xact()
ODMG header files are in ..h/odmg
.
..h/odmg
. To refer to this directory, you can invoke the oscp
utility with the following:
`oscp -p`/h/odmg
odmg.h
.
<odmg/odmg
.h>
in your application.
Including odmg.h
will include all ODMG classes. If you do not want all of them, you can include the header files one by one.
schcomp
and sch2db
, behave the same as in C++/VERSANT.
Hello
which is derived from d_Object
. It has a d_String
as its data member and access methods. The d_activate()
and d_deactivate()
methods of Hello
are also defined.File: hello.h
#ifndef HELLO_H
#define HELLO_H
#include <odmg/object.h>
#include <odmg/string.h>
#include <iostream.h>
class Hello : public d_Object {
private:
PString note;
public:
// construct given const char pointer
Hello (const char*);
// copy constructor
Hello (const Hello& );
// access functions
void setNote(PString );
PString getNote();
void d_activate();
void d_deactivate();
};
#endif
File: hello.cxx
#include "hello.h"
// constructor, given pointer to const char
Hello::Hello(const char *s) : note(s)
{
VPP_CLASS_CONSTRUCTOR1(Hello);
}
// copy constructor
Hello::Hello(const Hello& h) : note(h.note)
{
VPP_CLASS_CONSTRUCTOR1(Hello);
}
// access functions
void Hello::setNote(PString s)
{
dirty();
note = s;
}
PString Hello::getNote()
{
return note;
}
void Hello::d_activate()
{
cout << "Hello::d_activate()" << endl;
}
void Hello::d_deactivate()
{
cout << "Hello::d_deactivate()" << endl;
}
File: runhello.cxx
#include <iostream.h>
#include <odmg/odmg.h>
#include <odmg/set.h>
#include "hello.h"
extern "C" void exit(int);
main(int argc, char** argv)
{
if ( argc != 2 )
{
cout << "Usage: " << argv[0] <<" dbname " << endl;
exit(1);
}
d_Database* database = new d_Database;
database->open(argv[1]);
d_Transaction tran;
tran.begin();
char* str[6] =
{
"Hello", "World", "Versant",
"Object", "Technology", "Corporation"
};
cout << "create Hello object" << endl;
Hello *h;
for ( o_4b i = 0; i < 3; i++ )
{
h= new(database, "Hello") Hello(str[i]);
}
cout << "\nTesting checkpoint..." << endl;
tran.checkpoint();
h= new(PClassObject<Hello>::Pointer()) Hello(str[3]);
for ( i = 4; i < 6; i++ )
{ // use operator new(size_t, const d_Ref_Any&, const char*)
h= new(h->cod(), "Hello") Hello(str[i]);
}
cout << "\nTesting commit..." << endl;
tran.commit();
tran.begin();
d_Set<d_Ref<Hello> > a =
(VstrAny)PClassObject(Hello)::Object().select(argv[1],
FALSE,
NULL_PREDICATE);
d_Iterator<d_Ref<Hello> > iter = a.create_iterator();
d_Ref<Hello> ref;
for ( ; iter.not_done(); iter.advance() )
{
ref = iter.get_element();
cout << "\ndefrefence an object..." << endl;
cout << (const char *)ref->getNote() << endl;
}
cout << "\nsecond time defefence the same objects" << endl;
iter.reset();
for ( ; iter.not_done(); iteradvance )
{ ref = iter.get_element();
cout << "\ndefrefence an object..." << endl;
cout << (const char *)ref->getNote() << endl;
}
cout << "\nTesting abort..." << endl;
tran.abort();
database->close();
delete database;
exit(0);
}
File: Demo.h
// Schema Definition in C++ ODL
#include <odmg/odmg.h>
class City; // forward declaration
struct Address
{ d_UShort number;
d_String street;
d_Ref<City> city;
Address();
Address(d_UShort, const char*, const d_Ref<City> &);
};
extern const char _spouse [ ], _parents [ ], _children [ ] ;
class Person : public d_Object
{
public:
// Attributes (all public, for this example)
d_String name;
Address address;
// Relationships
d_Rel_Ref<Person, _spouse> spouse;
d_Rel_List<Person, _parents> children;
d_Rel_List<Person, _children> parents;
// Operations
Person(const char * pname);
void birth(const d_Ref<Person> &child); // a child is born
void marriage(const d_Ref<Person> &to_whom);
d_Ref<d_Set<d_Ref<Person> > > ancestors() const;
// returns ancestors
void move(const Address &); // move to a new address
// Extent
static d_Ref<d_Set<d_Ref<Person> > > people;
// a reference to class extent
static const char * const extent_name;
};
class City : public d_Object
{
public:
// Attributes
d_ULong city_code;
d_String name;
//d_Ref<d_Set<d_Ref<Person> > > population;
d_Set<d_Ref<Person> > population;
// the people living in this City
// Operations
City(d_ULong, const char*);
// Extension
static d_Ref<d_Set<d_Ref<City> > > cities;
// a reference to the class extent
static const char * const extent_name;
};
File: Demo.cxx
// Classes Implementation in C++
#include "demo.h"
// Address structure:
Address::Address( d_UShort pnum,
const char* pstreet,
const d_Ref<City> &pcity )
: number(pnum),
street(pstreet),
city(pcity)
{ }
Address::Address()
: number(0),
street((char *)0),
city(0)
{ }
// Person Class:
const char * const Person::extent_name = "people";
d_Ref<d_Set<d_Ref<Person> > > Person::people;
Person::Person(const char * pname)
: name(pname)
{
people->insert_element(this);
// Put this Person in the extension
}
void Person::birth(const d_Ref<Person> &child)
{ // Adds a new child to the children list
children.insert_element_last(child);
if(spouse)
spouse->children.insert_element_last(child);
}
void Person::marriage(const d_Ref<Person> &to_whom)
{ // Initializes the spouse relationship
spouse = to_whom;
// with->spouse is automatically set to this Person
}
d_Ref<d_Set<d_Ref<Person> > > Person::ancestors() const
{
// Constructs the set of all ancestors of this Person
d_Ref<d_Set<d_Ref<Person> > >
the_ancestors = new d_Set<d_Ref<Person> >;
//int i;
d_Iterator<d_Ref<Person> > i ;
for( i=parents.begin(); i != parents.end(); i.advance())
//for( i=parents.begin(); i.not_done(); i.advance())
{
// The ancestors = parents union ancestors(parents)
the_ancestors->insert_element(i.get_element());
d_Ref<d_Set<d_Ref<Person> > > grand_parents=
i.get_element()->ancestors();
//parents[i]->ancestors();
the_ancestors->union_with(*grand_parents);
grand_parents.delete_object();
}
return the_ancestors;
}
void Person::move(const Address &new_address)
{
// Updates the address attribute of this Person
if(address.city)
address.city->population.remove_element(this);
new_address.city->population.insert_element(this);
mark_modified();
address = new_address;
}
// City class:
const char * const City::extent_name = "cities";
d_Ref<d_Set<d_Ref<City> > > City::cities;
City::City(d_ULong code, const char * cname)
: city_code(code), name(cname), population()
{
cities->insert_element(this);
// Put this City into the extension
}
#include <iostream.h>
#include "demo.h"
#include <cxxcls/vql.h>
#include <cxxcls/oqlexec.h>
static d_Database dbobj;
static d_Database * database = &dbobj;
void Load()
{
// Transaction which populates the database
d_Transaction load;
load.begin();
// Create both Persons and Cities extensions, and name them.
Person::people = new( database, "d_Set<Link<Person > >")
d_Set<d_Ref<Person> >;
City::cities = new(database, "d_Set<Link<City > >" )
d_Set<d_Ref<City> >;
database->set_object_name(Person::people, Person::extent_name);
database->set_object_name(City::cities, City::extent_name);
// Construct 3 persistent objects from class Person.
d_Ref<Person> God, Adam, Eve;
God = new(database, "Person") Person("God");
Adam = new(database, "Person") Person("Adam");
Eve = new(database, "Person") Person("Eve");
// Construct an Address structure, Paradise,
// as (7 Apple Street, Garden)
// and set the address attributes of Adam and Eve.
Address Paradise( 7,
"Apple",
new(database, "City") City(0, "Garden"));
Adam -> move(Paradise);
Eve -> move(Paradise);
// Define the family relationships.
God->birth(Adam);
Adam->marriage(Eve);
Adam->birth(new(database, "Person") Person("Cain"));
Adam->birth(new(database, "Person") Person("Abel"));
load.commit(); // Commit transaction, putting objects
// into the database.
}
static void print_persons(const d_Collection<d_Ref<Person> >& s)
{
// A service function to print a collection of Persons
d_Ref<Person> p;
d_Iterator<d_Ref<Person> > it = s.create_iterator();
while( it.next(p) )
{ cout << "--- " << p->name << " lives in ";
if (p->address.city)
cout << p->address.city->name;
else
cout << "Unknown";
cout << endl;
}
}
void Consult()
{
// Transaction which consults and updates the database
d_Transaction consult;
d_List<d_Ref<Person> > list;
consult.begin();
// Static references to objects or collections must be recomputed
// after a commit
Person::people = database->lookup_object(Person::extent_name);
City::cities = database->lookup_object(City::extent_name);
// Now begin the transaction
cout << "All the people ....:" << endl;
print_persons(*Person::people);
cout << "All the people name ....:" << endl;
d_VQL_Query query("select SelfOID from Person");
LinkVstr<Person> personVstr;
d_oql_execute(query, personVstr);
list = (VstrAny) personVstr;
print_persons(list);
// Adam and Eve are moving ...
Address Earth( 13,
"Macadam",
new(database,
"City") City(1,
"St-Croix"));
LinkVstr<Person> Adams; query.set_query_string(
"select SelfOID from Person where name = \"Adam\"" );
d_oql_execute(query, Adams);
d_Ref<Person> Adam = Adams[0];
Adam->move(Earth);
Adam->spouse->move(Earth);
cout << "Cain's ancestors ...:" << endl;
d_Ref<Person> Cain = Adam->children.retrieve_element_at(0);
print_persons(*(Cain->ancestors()));
consult.commit();
}
main()
{
database->open("family");
Load();
Consult();
database->close();
}
File: schema.imp
// This file is required for VERSANT schema compiler
// to create database schema.
#include "demo.h"
const char _spouse [ ] = "spouse";
const char _parents [ ] = "parents";
const char _children [ ] = "children";
O_CAPTURE_SCHEMA(Person);
O_CAPTURE_SCHEMA(Address);
O_CAPTURE_SCHEMA(City);
O_CAPTURE_SCHEMA(d_Collection<d_Ref<Person> >);
O_CAPTURE_SCHEMA(d_Collection<d_Ref<City> >);
O_CAPTURE_SCHEMA(d_List<d_Ref<Person> >);
O_CAPTURE_SCHEMA(d_Rel_Ref<Person, _spouse>);
O_CAPTURE_SCHEMA(d_Rel_List<Person, _parents>);
O_CAPTURE_SCHEMA(d_Rel_List<Person, _children>);
O_CAPTURE_SCHEMA(d_Set<d_Ref<Person> >);
O_CAPTURE_SCHEMA(d_Set<d_Ref<City> >);
File: demo.h
As above example
File:demo.cxx
As above example
File: schema.imp
As above example
File: main.cxx
#include <stdlib.h>
#include <unistd.h>
#include <iostream.h>
#include "demo.h"
#include <cxxcls/vql.h>
#include <cxxcls/oqlexec.h>
void *thr_main(void *);
void Load();
void print_persons(const d_Collection<d_Ref<Person> >& s);
void Consult();
d_Database *database;
int main(int argc, char** argv)
{
// the first opened database will become the process default
// database
database = new d_Database();
database->open("family");
pthread_t th;
if (pthread_create(&th, NULL, thr_main, NULL))
{
cerr << "thread creation failed" ;
throw;
}
if (pthread_join(th, NULL))
{
cerr << "thread join failed";
throw;
}
database->close();
delete database;
return 0;
}
void *thr_main(void *arg)
{
// each thread will find the process default database
Load();
Consult();
return NULL;
}
void Load()
{
// Transaction which populates the database
d_Transaction load;
load.begin();
// Create both Persons and Cities extensions, and name them.
Person::people = new(database, "d_Set<Link<Person>>") d_Set<d_Ref<Person> >;
City::cities = new(database, "d_Set<Link<City>>" ) d_Set<d_Ref<City> >;
database->set_object_name(Person::people, Person::extent_name);
database->set_object_name(City::cities, City::extent_name);
// Construct 3 persistent objects from class Person.
d_Ref<Person> God, Adam, Eve;
God = new(database, "Person") Person("God");
Adam = new(database, "Person") Person("Adam");
Eve = new(database, "Person") Person("Eve");
// Construct an Address structure, Paradise, as (7 Apple Street, // Garden)
// and set the address attributes of Adam and Eve.
Address Paradise(7, "Apple", new(database, "City") City(0,
"Garden"));
Adam->move(Paradise);
Eve->move(Paradise);
// Define the family relationships.
God->birth(Adam);
Adam->marriage(Eve);
Adam->birth(new(database, "Person") Person("Cain"));
Adam->birth(new(database, "Person") Person("Abel"));
load.commit();// Commit transaction, putting objects into the
// database.
}
void print_persons(const d_Collection<d_Ref<Person> >& s)
{
// A service function to print a collection of Persons
d_Ref<Person> p;
d_Iterator<d_Ref<Person> > it = s.create_iterator();
while( it.next(p) )
{
cout << "--- " << p->name << " lives in ";
if (p->address.city)
cout << p->address.city->name;
else
cout << "Unknown";
cout << endl;
}
}
void Consult()
{
// Transaction which consults and updates the database
d_Transaction consult;
d_List<d_Ref<Person> > list;
consult.begin();
// Static references to objects or collections must be recomputed
// after a commit
Person::people = database->lookup_object(Person::extent_name);
City::cities = database->lookup_object(City::extent_name);
// Now begin the transaction
cout << "All the people ....:" << endl;
print_persons(*Person::people);
cout << "All the people name ....:" << endl;
d_VQL_Query query("select SelfOID from Person");
LinkVstr<Person> personVstr;
d_oql_execute(query, personVstr);
list = (VstrAny) personVstr;
print_persons(list);
// Adam and Eve are moving ...
Address Earth(13, "Macadam", new(database, "City") City(1,
"St- Croix"));
LinkVstr<Person> Adams;
query.set_query_string("select SelfOID from Person where name =
\"Adam\"" );
d_oql_execute(query, Adams);
d_Ref<Person> Adam = Adams[0];
Adam->move(Earth);
Adam->spouse->move(Earth);
cout << "Cain's ancestors ...:" << endl;
d_Ref<Person> Cain = Adam->children.retrieve_element_at(0);
print_persons(*(Cain->ancestors()));
consult.commit();
}
A conditional variable, cond, is used to make sure that three transactions successfully begin before threads join each other.
File: main.cxx
#include <stdlib.h>
#include <unistd.h>
#include <iostream.h>
#include <odmg/odmg.h>
#include "number.h"
#define MAX_SESSION3
void *thr_func0(void *);
void *thr_func1(void *);
void *thr_func2(void *);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
d_Transaction *tr[3];
d_Database *db[2];
int session_count=0;
main(int argc, char** argv)
{
if ( argc != 3 )
{
cout << "Usage: " << argv[0] <<" gdb1 " << " gdb2 " << endl;
exit(1);
}
db[0]= new d_Database;
db[1]= new d_Database;
db[0]->open(argv[1]);
db[1]->open(argv[2]);
pthread_t th[3];
if (pthread_create(&th[0], NULL, thr_func0, &tr[0]))
{
cerr << "thread creation failed" ;
throw;
}
if (pthread_create(&th[1], NULL, thr_func1, &tr[1]))
{
cerr << "thread creation failed" ;
throw;
}
if (pthread_create(&th[2], NULL, thr_func2, &tr[2]))
{
cerr << "thread creation failed" ;
throw;
}
for (int i=0; i< MAX_SESSION; ++i)
{
if (pthread_join(th[i], NULL))
{
cerr << "thread join failed";
throw;
}
}
delete db[0];
delete db[1];
return 0;
}
void *thr_func0(void *arg)
{
const int id=0;
d_Database *mydb=db[0];
*(d_Transaction **)arg = new d_Transaction(*mydb);
d_Transaction *tran=*(d_Transaction **)arg;
tran->begin();
cout << "create Number object" << endl;
d_Ref<Number> h;
for ( o_4b i = 0; i < 10; i++ )
{
h= new(db[0], "Number") Number(i+id*10);
}
tran->commit();
pthread_mutex_lock(&mutex);
++session_count;
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
if (session_count != MAX_SESSION )
pthread_cond_wait(&cond, &mutex);
else
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
tr[1]->join();
mydb=db[1];
LinkVstr<Number> vector = PClassObject(Number)::Object().select(
mydb->get_this_db_name(), FALSE, NULL_PREDICATE);
if (vector.size() != 20)
cout << "vector size is not correct!" << endl;
tr[1]->leave();
pthread_mutex_lock(&mutex);
--session_count;
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
if (session_count != 0)
pthread_cond_wait(&cond, &mutex);
else
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
// join back the old transaction
tran->join();
delete tran;
return NULL;
}
void *thr_func1(void *arg)
{
const int id=1;
d_Database *mydb=db[1];
*(d_Transaction **)arg = new d_Transaction(*mydb);
d_Transaction *tran=*(d_Transaction **)arg;
tran->begin();
cout << "create Number object" << endl;
d_Ref<Number> h;
for ( o_4b i = 0; i < 10; i++ )
{
h= new(db[1], "Number") Number(i+id*10);
}
tran->commit();
pthread_mutex_lock(&mutex);
++session_count;
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
if (session_count != MAX_SESSION )
pthread_cond_wait(&cond, &mutex);
else
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
tr[0]->join();
mydb=db[0];
LinkVstr<Number> vector = PClassObject(Number)::Object().select(
mydb->get_this_db_name(), FALSE, NULL_PREDICATE);
if (vector.size() != 10)
cout << "vector size is not correct!" << endl;
if (d_Transaction::current() == tr[0])
cout << "current transaction is correct!" << endl;
if (d_Transaction::current()->get_session() ==
VSession::current())
cout << "current session is correct!" << endl;
tr[0]->leave();
if (d_Transaction::current() == NULL)
cout << "current transaction is correct!" << endl;
pthread_mutex_lock(&mutex);
--session_count;
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
if (session_count != 0)
pthread_cond_wait(&cond, &mutex);
else
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
tran->join();
delete tran;
return NULL;
}
void *thr_func2(void *arg)
{
const int id=2;
d_Database *mydb=db[1];
*(d_Transaction **)arg = new d_Transaction(*mydb);
d_Transaction *tran=*(d_Transaction **)arg;
tran->begin();
cout << "create Number object" << endl;
d_Ref<Number> h;
for ( o_4b i = 0; i < 10; i++ )
{
h= new(db[1], "Number") Number(i+id*10);
}
tran->commit();
pthread_mutex_lock(&mutex);
++session_count;
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
if (session_count != MAX_SESSION )
pthread_cond_wait(&cond, &mutex);
else
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
tr[0]->join();
mydb=db[0];
LinkVstr<Number> vector = PClassObject(Number)::Object().select(
mydb->get_this_db_name(), FALSE, NULL_PREDICATE);
if (vector.size() != 10)
cout << "vector size is not correct!" << endl;
tr[0]->leave();
pthread_mutex_lock(&mutex);
--session_count;
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
if (session_count != 0)
pthread_cond_wait(&cond, &mutex);
else
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
tran->join();
delete tran;
return NULL;
}
File: number.h
#include <odmg/object.h>
class Number :public d_Object
{
public:
Number(o_4b id) :_id(id) {}
operator int() const { return _id; }
private:
o_4b _id;
};
File: schema.imp
#include "number.h"
O_CAPTURE_SCHEMA(Number);
This online documentation is confidential and proprietary to Versant Corporation and is licensed to you, or to your organization, pursuant to the terms of an agreement between you and Versant that restricts the use of this documentation. Please refer to the agreement for more information or call Versant at 510-789-1500 with any questions.