BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Object Relational Mapping problem

 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> Delphi OO design
View previous topic :: View next topic  
Author Message
Leonardo M. Ramé
Guest





PostPosted: Mon Apr 09, 2007 8:12 am    Post subject: Object Relational Mapping problem Reply with quote



I'm facing a problem when trying to optimize an ORM library I wrote for internal use at our company aparently resolved by Hibernate thanks to its "join fetch" feature.

An example of the problem is this:

Person object contains a collection of Sales, a typical one to many relationship currently resolved by two queries, one to retrive Person object and a second one to retrieve its Sales collection. It's fast, but involves two queries turning somewhat inefficient if the database must deal with large recordsets.

According to this site http://www.realsolve.co.uk/site/tech/orm-performance.php Hibernate resolves the problem by creating a query similar to this:

Select person.*, s.* from person p
inner join sales s on s.idperson=p.idperson

And fill the data from person table to the Person object and Person.Sales with sales data extracted from the same resultset.

Creating a similar mapping isn't very difficult with our ORM, the problem is how to handle relations between objects that aren't mapped to tables but queries. To clarify my point, nothing more clear than an example:

We use a similar to Hibernate XML definition of mapped classes. In my example, the mapping should be something like this:

Mapping of classes Person and Sales:

<class name="TPerson">
<table name="persons"/>
<primary key="idperson"/>
<many-to-one class="sales" key="idperson"/>
</class>

<class name="TSales">
<table name="sales"/>
<primary key="idsale"/>
</class>

TPerson and TSales classes:

TSale = class(TCollectionItem)
private
FIdSale: Integer;
FIdPerson: Integer;
FAmount: Double;
published
property IdSale: Integer read ...
...
end;

TSales = class(TCollection)
end;

TPerson = class(TPersistent)
private
FIdPerson: Integer;
FName: string;
FSales: TSales;
published
property IdPerson: Integer read ...write ...
property Sales: TSales read ... write ... // <-- note the TSales instance
...
end;

Well, the problem for us is, in some circunstances there's not such Sales table, but a standard sql query in charge of retriving tabular information our ORM maps to a TSales object. The modified map of TSales should be like this:

<class name="TSales">
<table name="sales"/>
<query>
select sum(amount) as amount, idperson, date from sales
group by sales.date
<query>
<primary key="idsale"/>
</class>

Note that I used a query to retrieve the Sum of "amount" column (or property in TSales class) grouped by date.

If the ORM tryes to create the SQL for the one-to-many relationship, something like this will be created:

Select person.*, s.* from person p
inner join
(select sum(amount) as amount, idperson, date from sales
inner join sales s on s.idperson=p.idperson) s on s.idperson=p.idperson

Of course, no one sql engine will accept that statement.

Any of you knows a solution for my problem?

Thanks in advance,
Leonardo M. Ramé
http://leonardorame.blogspot.com
Back to top
Joanna Carter [TeamB]
Guest





PostPosted: Mon Apr 09, 2007 1:43 pm    Post subject: Re: Object Relational Mapping problem Reply with quote



Leonardo M. Ramé a écrit :

Quote:
An example of the problem is this:

Person object contains a collection of Sales...

I really have to ask why ???

OO modelling is meant to reflect real life and this kind of relationship
is far from real life, it is a cotrived relationship arising out of
relational modelling influences.

Think about it : Does a real Person hold a list of Sales ? Or is the
record of Sales held by the Company ? If a list of Sales is held in the
Person class, it indicates that the Person knows abot all its sales for
all Companies of which it has ever been a Customer.

A better design is :

Person
Name
Address
...

OrderLine
Qty
Product
...

SalesOrder
Person
Date
...

SalesLedger
SalesForCustomer(Person) : List of SalesOrder
SalesForCustomer(Person, Date) : List of SalesOrder
...

Quote:
a typical one to many
relationship currently resolved by two queries, one to retrive Person
object and a second one to retrieve its Sales collection. It's fast,
but involves two queries turning somewhat inefficient if the database
must deal with large recordsets.

If you want to retrieve a list of Sales for a given Person, you would
usually start by having a browser from which you would chose the Person,
so you already have an instance of the Person. Now all you have to do is
to ask the SalesLedger for a list of Sales passing in the Person as
parameter.

So you will not be creating two queries to retrieve a list of sales;
assuming you already have the Person object, the query generated byt the
SaleLedger will be a simple single table retrieval.

As long as the tables involved are indexed with non-meaningful integer
keys, two single table queries on a single key should be faster than
asking the database to create a complex join.

Quote:
According to this site
http://www.realsolve.co.uk/site/tech/orm-performance.php Hibernate
resolves the problem by creating a query similar to this:

Select person.*, s.* from person p inner join sales s on
s.idperson=p.idperson

And fill the data from person table to the Person object and
Person.Sales with sales data extracted from the same resultset.

IMO this is not good design, period.

Quote:
Creating a similar mapping isn't very difficult with our ORM, the
problem is how to handle relations between objects that aren't mapped
to tables but queries.

You simply dont need this kind of mapping, Correct your OO design
instead :-)

Joanna

--
Joanna Carter [TeamB]
Consultant Software Engineer
Back to top
Leonardo M. Ramé
Guest





PostPosted: Mon Apr 09, 2007 4:48 pm    Post subject: Re: Object Relational Mapping problem Reply with quote



Joanna, thanks for your response. Maybe my example is not good enough to
explain my point. What I try to do is to replicate Hibernate's "fetch
join" feature.

Using your example, suppose you have to retrive a SalesOrder including
its Person data and show it on screen. Actually, to resolve such cases,
we use a new class, lets call it "ExtendedSalesOrder" wich includes the
person data, then we map it to a query like this:

select SalesOrder.*, Person.*
from SalesOrders
inner join Person on SalesOrders.PersonId = Person.PersonId

The benefit of a "fetch join" is that this type of queries could be
created automatically by the ORM.

Quote:
Person object contains a collection of Sales...

I really have to ask why ???
...
So you will not be creating two queries to retrieve a list of sales;
assuming you already have the Person object, the query generated byt the
SaleLedger will be a simple single table retrieval.
...
You simply dont need this kind of mapping, Correct your OO design
instead :-)

Joanna
Back to top
Joao Morais
Guest





PostPosted: Mon Apr 09, 2007 6:09 pm    Post subject: Re: Object Relational Mapping problem Reply with quote

Leonardo M. Ramé wrote:

Quote:
Joanna, thanks for your response. Maybe my example is not good enough to
explain my point. What I try to do is to replicate Hibernate's "fetch
join" feature.

Using your example, suppose you have to retrive a SalesOrder including
its Person data and show it on screen. Actually, to resolve such cases,
we use a new class, lets call it "ExtendedSalesOrder" wich includes the
person data, then we map it to a query like this:

select SalesOrder.*, Person.*

Always use field names in order to get a more efficient query.

Quote:
from SalesOrders
inner join Person on SalesOrders.PersonId = Person.PersonId

The benefit of a "fetch join" is that this type of queries could be
created automatically by the ORM.

I am writing a LGPL OPF and am using the following approach:

1. Query the first table (SalesOrder) and instantiate SalesOrder objects;

2. Use a proxy to reference another object, so the SalesOrder.Person
will receive the PersonId value at this moment;

3. List all PersonId you will need to use, create a Query to fetch all
of them at once (using the in operator). Instantiate such Person objects.

So:

1. You will have two queries, ok -- but -- two simple queries and you
will not retrieve the same person more than once, so less traffic;

2. When you need to use an object via a proxy, the proxy will query the
object cache and will find all the PersonId.

Hope that it helps you.

Regards from Brazil,
--
Joao Morais


Quote:
Person object contains a collection of Sales...

I really have to ask why ???
...
So you will not be creating two queries to retrieve a list of sales;
assuming you already have the Person object, the query generated byt
the SaleLedger will be a simple single table retrieval.
...
You simply dont need this kind of mapping, Correct your OO design
instead :-)

Joanna
Back to top
Joanna Carter [TeamB]
Guest





PostPosted: Tue Apr 10, 2007 4:51 pm    Post subject: Re: Object Relational Mapping problem Reply with quote

Leonardo M. Ramé a écrit :

Quote:
Joanna, thanks for your response. Maybe my example is not good enough to
explain my point. What I try to do is to replicate Hibernate's "fetch
join" feature.

My point was, from experience, don't bother using this kind of thing, it
can be slower to execute a join and it simply isn't necessary.

Quote:
Using your example, suppose you have to retrive a SalesOrder including
its Person data and show it on screen. Actually, to resolve such cases,
we use a new class, lets call it "ExtendedSalesOrder" wich includes the
person data, then we map it to a query like this:

Why would you create an extend class ? The SalesOrder will contain a
reference to the Person and the Person object can be retrieved in a lazy
fashion if you need to examine it. This way, you only retrieve the
Person if and when its state is required.

Two separate queries on properly indexed tables like this are usually
faster than any join. The first query will return a list of SalesOrder;
the second query to retrieve the Person could be short-circuited by
caching the Person that was passed to the SalesLedger method to retrieve
the list of SalesOrders and simply assigning the state, not the object,
to the Person property of every SalesOrder retrieved, before passing the
list back from the method.

Quote:
select SalesOrder.*, Person.*
from SalesOrders
inner join Person on SalesOrders.PersonId = Person.PersonId

The benefit of a "fetch join" is that this type of queries could be
created automatically by the ORM.

I would still argue, strongly, that, from experience, this is
unnecessary optimization and bad design :-)

Joanna

--
Joanna Carter [TeamB]
Consultant Software Engineer
Back to top
Joanna Carter [TeamB]
Guest





PostPosted: Tue Apr 10, 2007 4:58 pm    Post subject: Re: Object Relational Mapping problem Reply with quote

Joao Morais a écrit :

Quote:
I am writing a LGPL OPF and am using the following approach:

1. Query the first table (SalesOrder) and instantiate SalesOrder objects;

2. Use a proxy to reference another object, so the SalesOrder.Person
will receive the PersonId value at this moment;

3. List all PersonId you will need to use, create a Query to fetch all
of them at once (using the in operator). Instantiate such Person objects.

If the initial query to retrieve the SalesOrders was based on a single
Person, then point 3 is not strictly necessary.

Quote:
1. You will have two queries, ok -- but -- two simple queries and you
will not retrieve the same person more than once, so less traffic;

Agreed

Quote:
2. When you need to use an object via a proxy, the proxy will query the
object cache and will find all the PersonId.

Our base business class has the idea of lazy-fetch objects which contain
a property bag which is observed by the OPF. When any property that
holds an object is first retrieved, the property object for the object
property is created, the ID is assigned and the property name is added
to the lazy object list. But the object is only retrieved when one of
its properties is first read. This is similar to your idea of a proxy
object and can, as you point out, save a lot of database fetches.

Joanna

--
Joanna Carter [TeamB]
Consultant Software Engineer
Back to top
Peter Morris [Droopy eyes
Guest





PostPosted: Tue Apr 10, 2007 4:58 pm    Post subject: Re: Object Relational Mapping problem Reply with quote

Quote:
I would still argue, strongly, that, from experience, this is unnecessary
optimization and bad design Smile

I haven't read the whole thread. Is this also your opinion when fetching a
single object that is spread out over multiple tables due to inheritance?



--
Pete

Blessed are the geek, for they shall public class GeekEarth : Earth {}
====
Audio compression components, DIB graphics controls, ECO extensions,
FastStrings : http://www.droopyeyes.com
My blog : http://mrpmorris.blogspot.com
Back to top
Joao Morais
Guest





PostPosted: Tue Apr 10, 2007 5:09 pm    Post subject: Re: Object Relational Mapping problem Reply with quote

Joanna Carter [TeamB] wrote:

Quote:
3. List all PersonId you will need to use, create a Query to fetch all
of them at once (using the in operator). Instantiate such Person objects.

If the initial query to retrieve the SalesOrders was based on a single
Person, then point 3 is not strictly necessary.

Yup, I missed the following point -- having two or more IDs that doesn't
exist in the cache and: for some reason they will be needed at once
(like a report) or the user disabled the default lazy loading approach.

--
Joao Morais
Back to top
Leonardo M. Ramé
Guest





PostPosted: Tue Apr 10, 2007 8:03 pm    Post subject: Re: Object Relational Mapping problem Reply with quote

Joanna Carter [TeamB] escribió:

"Our base business class has the idea of lazy-fetch objects which
contain a property bag which is observed by the OPF. When any property
that holds an object is first retrieved, the property object for the
object property is created, the ID is assigned and the property name is
added to the lazy object list. But the object is only retrieved when one
of its properties is first read. This is similar to your idea of a proxy
object and can, as you point out, save a lot of database fetches. "

This sounds interensting, can you point me to url with some examples of
this methodology?
Back to top
Joanna Carter [TeamB]
Guest





PostPosted: Tue Apr 10, 2007 8:23 pm    Post subject: Re: Object Relational Mapping problem Reply with quote

Peter Morris [Droopy eyes software] a écrit :

Quote:
I would still argue, strongly, that, from experience, this is
unnecessary optimization and bad design :-)

I haven't read the whole thread. Is this also your opinion when
fetching a single object that is spread out over multiple tables due to
inheritance?

Not necessarily. my main objection was to the idea of using a join to
retrieve all orders for a customer, just to provide all the data for the
customer object as well as the orders in the one query. The jury is
still out on how best to handle hierarchies :-)

Joanna

--
Joanna Carter [TeamB]
Consultant Software Engineer
Back to top
Joanna Carter [TeamB]
Guest





PostPosted: Tue Apr 10, 2007 8:35 pm    Post subject: Re: Object Relational Mapping problem Reply with quote

Leonardo M. Ramé a écrit :

Quote:
"Our base business class has the idea of lazy-fetch objects which
contain a property bag which is observed by the OPF. When any property
that holds an object is first retrieved, the property object for the
object property is created, the ID is assigned and the property name is
added to the lazy object list. But the object is only retrieved when one
of its properties is first read. This is similar to your idea of a proxy
object and can, as you point out, save a lot of database fetches. "

This sounds interensting, can you point me to url with some examples of
this methodology?

No, I am sorry but I have not written this up in an article.

The basic idea is that all the state of an object is held in a "data
packet" (thanks to Bob Dawson for that idea). The data packet holds all
the state of the object in "property" objects and it is this "packet"
that gets serialised by the persistence mechanism. It is also the
responsibility of the data packet to maintain a list of all properties
that have not been fully loaded when the business object was retrieved.
The property getter for any given property in the business class simply
calls the GetValue(string name) method on the data packet and it is this
GetValue(...) method that checks to see if the property name is in the
lazy-load list, retrieving the data packet for the lazy-load "property"
object. When the business object is first retrieved, the data packet is
created by the broker and the broker is set to be an observer of that
packet; thus, when a lazy load property is detected in the data packet,
the Notify method is called and the broker responds by retrieving the
data packet for the "property" and the retrieved packet is assigned into
the nested "property" object held in the data packet.

Does that help ?

Joanna

--
Joanna Carter [TeamB]
Consultant Software Engineer
Back to top
Peter Morris [Droopy eyes
Guest





PostPosted: Wed Apr 11, 2007 2:28 am    Post subject: Re: Object Relational Mapping problem Reply with quote

Yes, that did not occur to me at first. A hierarchy selection will return 1
row per object, whereas this topic will return 1 row per Sale and also
repeat the same Person information for each sale.
Back to top
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> Delphi OO design All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.