PDA

View Full Version : Designing transaction in the code equals designing problems



kornicameister
1st February 2011, 19:01
I am facing huge problem...
I have to provide that my code will handle transactions and at this particular moment I can not do so.
Particular moment means I've created specialized classes which relates to appropriate tables in the database.
For example class XQuery is to control insert,remove,update queries just for X table.
XQuery inherits from pure abstract class (an interface,declarations of insert,remove,update) and helping class (contains additional information,methods etc), so I have multiple inheritance here.
I just wanted to separate code strictly responsible for executing the queries from the code which works with the results and provide other vital informations.

That's enough for explanation.

What is true nature of the problem is there is no ONE scenario I could follow in the case of executing multiple queries with the transaction mode on. What makes it more complicated is that I want to make it as far automatic as possible - methods knows itself which action must be taken, when there is a lastInsertedId needed and so on.

By know I have class which gathers others XQuery's objects along with the flag describing the action(insert,update,remove).
But I am stuck anyway, so a little tip would be more than helpful.

high_flyer
2nd February 2011, 09:11
But I am stuck anyway,
What are you stuck with?
Can you formulate your problem in one sentence?

marcvanriet
2nd February 2011, 11:32
Hi,

I think it is best anyway to do a 'StartTransaction' in your code somewhere, do database updates (which is done using your specialized classes) and then do a 'CommitTransaction' or rollback or so.

IMO, the decision WHEN a transaction has to be started or committed needs to be taken at a higher level (functional level, application level), and not by the underlying classes that do the database operations. So you cannot make it 'automatic'.

Regards,
Marc

kornicameister
2nd February 2011, 14:24
Thank you guys for the answers...
first of all
@high_flyer - I am stuck with the one class I have which is to control other classes when transaction mode is on... for example I have an object to place in the table A, and than I need lastInsertedId of it to insert some objects to table B... this needs to be done in transaction because the failure anywhere must affect on rolling back everything.

class TransactionHandler : public QObject{
Q_OBJECT
Q_CLASSINFO("version","0.2")
public:
TransactionHandler(QObject *parent = 0);
virtual ~TransactionHandler();

void addQuery(QueryHandler *query);
bool perform() const;
bool status() const;
private:
bool isTranDone;
QExplicitlySharedDataPointer<QueryHandlerData> d};

this is the body of my class which I am trying to implement

One Sentence - inability to come up with something common for all classes which must be controlled if necessary by TransactionHandler

@marcvanriet - this is exactly what I am trying to achieve now by TransactionHandler, because if I need transaction I create an object of TransactionHandler, aggregate all other XQuery objects and simply call for perform() method which does the job, or rather should. Having all the transaction-controlling code in the gui-code is something I don't want to have, and I am trying to separate it. I would like to have anykind of automatic way, any kind of starting point to gather something common... anyway I am still looking :)

high_flyer
2nd February 2011, 14:46
One Sentence - inability to come up with something common for all classes which must be controlled if necessary by TransactionHandler
Well, even though my answer is very general, what I would suggest is to look at all your objects that have transactions, and either find common logic steps that all go through, or force all of them to comply to predefined logic steps.
In other words - define an interface that all these object will implement.
Then, in your handler, call these steps based on the common logic that fits your interface.

I fully agree with you, that this should be encapsulated in its own module, and should not be mixed with GUI code.

Since I am at work, I don't have the time to look deeper in your code, or suggest some code for you.
But sometimes a push in the right direction is all what people need. :)

From what I can see in the header you posted, it looks a step in the right direction, but is a bit different from what I suggested just now.
Your manager doesn't need to implement the interface that your query object do, it should implement the logic of calling the right actions at the right time/sequence.

In other words: you query object should the know the "how".
Your manager should know the "when".


I am stuck with the one class I have which is to control other classes when transaction mode is on...
What exactly are you tuck on? which part or detail?

kornicameister
2nd February 2011, 16:50
I see Your point
I will try to implement it after I solve the problem I posted in the different thread about sharing the data.

marcvanriet
2nd February 2011, 17:27
...transaction-controlling code in the gui-code is something I don't want to have...
Didn't suggest this either. 'functional level' or 'application level' doesn't (always) mean GUI :)

Your handler actually needs to perform "object to place in the table A", because only after performing the it you will know "lastInsertedId". But the handler itself doesn't know anything about "lastInsertedId". It may not even be in the SQL statement if it is auto-generated. And it sure doesn't know that any queries will be executed after that or not, and if they use the value of lastInsertedID.

The only thing I can come up with is to pass the queries to your query handler along with some metadata. So something like addQuery(QueryHandler *query, QString dependencies );. For the first query you should say "begin lastInsertedId". The handler should start a transaction for any query for which you say there are dependencies. The queries are executed immediately. For the following queries you should say "uses lastInsertedId". And then you must say something like 'commit( "end lastInsertedId" )' to let the handler know that no further queries will depend on lastInsertedId. The handler should only really commit the queries if there is an 'end' for each 'begin'.

Regards,
Marc

kornicameister
2nd February 2011, 18:18
That sounds interesting... now I know how step-by-step process can look like more than five minutes, when I was circling around a solution with passing an enum which would determine the decision (insert etc)... thanks for that tip
I will write all steps on the paper... maybe I will see something common like high_flyer suggested

kornicameister
3rd February 2011, 01:11
I was thinking...
QSqlQuery has this feature lastId... is it possible to access it everywhere at anytime ?