Doubts on OPF using TPersistenceBroker collaborate with OO object

Doubts on OPF using TPersistenceBroker collaborate with OO object

Post by Chau Chee » Wed, 24 May 2006 16:27:12


Hi,

I have some question regarding the OPF. I attempt to categorize 3 kinds
of OPF operations:

1. One to One relation

This is the most simple persistence operation. It use the following mapping:

Class -> Table
Attributes -> Columns
Instances -> Rows
Attribute Value -> Column Data


2. Master-Detail relation

A good example for this is Invoice and Items:

IInvoiceItem = interface
property Qty: currency;
property UnitPrice: currency;
property Amount: currency;
end;

IInvoice = interface
function AddItem: IInvoiceItem;
function DeleteItem(const aItem: IInvoiceItem);
function GetItems: IInvoiceItems;

property Items: IInvoiceItems read GetItems;
property Total: currency;
end;

3. Custom relation

This relation may persist an object to more than one table base on some
rules that doesn't falls to above 2 categories.

We aren't encourage to add methods like store/delete/retrieve to BO
class and are advise to use a PersistenceBroker:

TPersistenceBroker = class
class function Store(const aObj: IPersistable): boolean;
class function Retrieve(const aObj: IPersistable): boolean;
class function Delete(const aObj: IPersistable): boolean;
end;

My question:

1. However, I find it difficult to use the TPersistenceBroker for
category 2 and 3. Any ideas on how to perform the design?

2. For category 2, how can IInvoice.Total get notified if
InvoiceItem.Amount changed in order to perform necessary update?

--
Best regards,
Chau Chee Yang

E Stream Software Sdn Bhd
URL: www.sql.com.my
SQL Financial Accounting
 
 
 

Doubts on OPF using TPersistenceBroker collaborate with OO object

Post by Gerar » Wed, 24 May 2006 17:06:50

i,

En/na Chau Chee Yang ha escrit:

Unless you try to store detail objects in a field in the master's table
(like InstantObjects does for what it calls "parts" references), invoice
items should be stored in their own table following the mapping
described in 1, the only difference being the existence of a foreign key
(Invoice ID).


>
> 1. However, I find it difficult to use the TPersistenceBroker for
> category 2 and 3. Any ideas on how to perform the design?
>


In the framework I am working on (very experimental for now) , I have
metadata for object relations:

TOOPSRelationEndMultiplicity = (emOne, emZeroOne, emZeroMany, emOneMany);

TOOPSRelationEndAssociationType = (atAggregation, atComposition);


TOOPSRelationMetadata = class(TPersistent)
private
...
published
property End1: TOOPSRelationEndMetadata read GetEnd1 write SetEnd1;
property End2: TOOPSRelationEndMetadata read GetEnd2 write SetEnd2;
property RelationName: string read GetRelationName write
SetRelationName;
end;

TOOPSRelationEndMetadata = class(TPersistent)
private
...
public
property AssociationType: TOOPSRelationEndAssociationType read
GetAssociationType write SetAssociationType;
property Multiplicity: TOOPSRelationEndMultiplicity read
GetMultiplicity write SetMultiplicity;
published
property EndName: string read GetEndName write SetEndName;
property ReferencedObjectMetadata: TOOPSObjectMetadata read
GetReferencedObjectMetadata write SetReferencedObjectMetadata;
property RelationMetadata: TOOPSRelationMetadata read
GetRelationMetadata write SetRelationMetadata;
end;


TOOPSSimpleRelationEndMetadata = class(TOOPSRelationEndMetadata)
private
..
public
constructor Create(aKeyAttributesMetadata: Array of
TOOPSAttributeMetadata);
property LocalKeyAttributesCount: Integer read
GetLocalKeyAttributesCount;
property LocalKeyAttributesMetadata[Index: Integer]:
TOOPSSimpleAttributeMetadata read GetLocalKeyAttributesMetadata;
end;

TOOPSMultipleRelationEndMetadata = class(TOOPSRelationEndMetadata)
private
...
public
constructor Create(aKeyAttributesMetadata: Array of
TOOPSAttributeMetadata);
property ForeignKeyAttributesCount: Integer read
GetForeignKeyAttributesCount;
property ForeignKeyAttributesMetadata[Index: Integer]:
TOOPSSimpleAttributeMetadata read GetForeignKeyAttributesMetadata;
end;

Each Attribute has Metadata associated to it.
For complex attributes, like references to simple objects
(TInvoice.Customer) or to lists of objects (TInvoice.InvoiceItems), the
associated metadata has a link to a RelationEndMetadata.
When a object or object list property is accessed, it is loaded using
the relation end metadata.
The fields used for foreign keys have an attribute, but no published
property, to avoid inconsistencies, or the work to update the associated
object.
So I cannot write Invoice.CustNo := 3, because the CustNo property
doens't exist.
Instead, I write Invoice.Customer := aCustomer, and the internal foreign
key attribute (CustNo) is set to the value of the key in aCustomer,
using the Relation metadata.


Either you make Invoice.Total readonly, and in the Invoice.getTotal
getter method you just walk through it's invoice items and add the
totals and return the sum, or you keep an internal field,
 
 
 

Doubts on OPF using TPersistenceBroker collaborate with OO object

Post by Gerar » Wed, 24 May 2006 17:08:42

I forgot to add that I am working on adding metadata in relations for
cases where there is an intermediate table and many-to-many relationships.

Regards,

Gerard.
 
 
 

Doubts on OPF using TPersistenceBroker collaborate with OO object

Post by Chau Chee » Wed, 24 May 2006 18:12:13

> In the framework I am working on (very experimental for now) , I have

I feel this is not easy to do. I have diff thought. For those custom
object/persistence relation, I wrote have a persistence handler to
handler to storage. Would that be nice?

For example, I want to persistence an object A. I wrote a persistence
handler for object A. To persist object A, I still write this:

TPersistenceBroker.Store(A);

But the TPersistenceBroker.Store is implement as:

procedure TPersistenceBroker.Store(A);
var IID: TGUID;
H: IPersistenceHandler;
begin
IID := o.GetType;
H := FindHandler(IID);
if H <> nil then
H.Store(o)
else
DefaultHandler(o);
end;

I would then need to register a pair of (TBOClass.GUID,
TBOClass_PersistenceHandler) for the usage of FindHandler.




That may have some performance impact if invoice items are many.

--
Best regards,
Chau Chee Yang

E Stream Software Sdn Bhd
URL: www.sql.com.my
SQL Financial Accounting
 
 
 

Doubts on OPF using TPersistenceBroker collaborate with OO object

Post by Gerar » Wed, 24 May 2006 18:26:39

En/na Chau Chee Yang ha escrit:

Certainly much simpler than my solution. But i guess you have to write
persistence handlers for every class and relation?
Everybody has it's goals, requirements and priorities, I guess that's
why most people end up writing their own framework (An for the fun of it
<g>).
True, but I don't see many solutions for this.

Regards,

Gerard.
 
 
 

Doubts on OPF using TPersistenceBroker collaborate with OO object

Post by Chau Chee » Wed, 24 May 2006 19:24:52


We may have common object-persistence handler that perform one to one
mapping operation. For those complex persistence strategy, it will
perform the transformation and will finally get to the common one to one
operation.

For example:

A complex persistence operation on a single object involve updating to 2
tables:

TComplextPersistenceHandler = class
procedure Store(o: IPersistable);
end;

procedure TComplextPersistenceHandler.Store(o: IPersistable);
var c: IComplexObject;
begin
c := o as IComplexObject;
TPersistenceBroker.Store(c.Member1);
TPersistenceBroker.Store(c.Member2);
end;

To store a IComplexObject o, we use

TPersistenceBroker.Store(o as IPersistable);

procedure TPersistenceBroker.Store(o as IPersistable);
var H: IPersistenceHandler;
begin
H := FindHandler(o);
if H <> nil then
H.Store(o)
else
CommonHandler.Store(o);
end;

CommonHandler would be a simple one to one mapping that can be used for
many such persistence operation like Member1 and Member2 of IComplexObject.




A solution appear in my mind would be something like this:

Invoice -> Items -> Item -> (Qty, UnitPrice, Amount)

Invoice is observer of Items.
Items is observer of Item
Item is observer of Qty, UnitPrice and Amount.

If the Amount get changed, it will notify "Item", then "Items" and
finally Invoice. Thus Invoice will perform the necessary calculation on
Total value.

The notification may be perform as such:

procedure TCurrencyValue.Notify;
var s: IInterface;
begin
s := TValueChange.Create(Self, OldValue, NewValue);
for i := 0 to FObservers.Count - 1 do begin
FObservers.Update(s);
end;
end;

procdure TInvoice.update(subject: IInterface);
var c: IValueChange;
begin
c := (s as IValueChange);
SetTotal(GetTotal - c.OldValue - c.NewValue);
end;


--
Best regards,
Chau Chee Yang

E Stream Software Sdn Bhd
URL: www.sql.com.my
SQL Financial Accounting
 
 
 

Doubts on OPF using TPersistenceBroker collaborate with OO object

Post by Gerar » Wed, 24 May 2006 19:39:41

En/na Chau Chee Yang ha escrit:
Yes, it a solution.
If there are many Invoice Items, or not all items are loaded in memory
(objectspace), that's probably better.

Regards,

Gerard.