I should say that we are still working on theVanatec 4.4.0.48. I know, you promised to renew SQL 2000 support in April. I hope we will upgrade our SQLs before.Anyhow I’m interested about the issues described below.
Do the issues exist in the latest version? And if they are then, maybe you’ll be too kind so you will find it useful to amend the T ORM behavior in the next release, and we could be forced with the SQL upgrade very much. :-)
Okay, some words as preface… Our team is working on a property management system. Every property could be a home, or estate, or any other building. The system is in a maintaining phase. And I’m keen to re-factor it to TOA completely.
Probably about 7% of the system business transactions look like batch processing. 93% are not and we’re glad to use TOA. :-)
Well, an Automatic Demands Generator module is an example of batch processing.
We need to generate a lot of period demands to tenants of our properties. The process needs for a lot of data: budget, previously generated demands, etc, etc. During a re-factoring from storage procedures, I tried to use OQL. You know, it is not a very complex query language, and it forced to load a half of the DB to my scope. :-) Lazy loading works extremely slowly.
So, I found that I should use a stored procedure.
The idea was so a new stored procedure will prepare demands for the most data heavy processing, as because I wasn’t going to move whole the Demand class logic into the stored procedure. Thus the procedure returned a list of partially filled Demand objects with fake IDs.I intended to generate real demands by those templates. Like in the Prototype pattern. Unfortunately the Vanatec 4.4.0.48 didn’t work with it very well. From time to time it tried to check the Demand with any visible reason. One time the generate button worked just fine, next time we got something like this:
#1 25/12/2005..24/03/2006 SC [Amount/Paid: £0.01/£0.00] OUTSTANDING
#2 01/02/2002..28/02/2002 AST Monthly Demand [Amount/Paid: £575.00/£575.00] PAID
#3 01/02/2002..28/02/2002 AST Monthly Demand [Amount/Paid: £575.00/£575.00] PAID
#4 02/02/2002..01/03/2002 AST Monthly Demand [Amount/Paid: £475.00/£475.00] PAID
#5 26/01/2002..25/02/2002 AST Monthly Demand [Amount/Paid: £495.00/£495.00] PAID
#6 28/01/2002..27/02/2002 AST Monthly Demand [Amount/Paid: £680.00/£680.00] PAID
#7 02/02/2002..01/03/2002 AST Monthly Demand [Amount/Paid: £730.00/£730.00] PAID
#8 01/02/2002..28/02/2002 AST Monthly Demand [Amount/Paid: £460.00/£460.00] PAID
#9 04/02/2002..03/03/2002 AST Monthly Demand [Amount/Paid: £680.00/£680.00] PAID
#10 02/02/2002..01/03/2002 AST Monthly Demand [Amount/Paid: £680.00/£680.00] PAID
#11 01/02/2002..28/02/2002 AST Monthly Demand [Amount/Paid: £500.00/£500.00] PAID
OpenAccess.Exceptions.NoSuchObjectException: No row for DataDomain.Accounting.PMSDemand ('RECEIVABLES') GenericOID@2e008e7 PMSDemand DEMANDID=12
at OpenAccess.SPI.Backends.ThrowException(Exception e)
at OpenAccess.RT.ExceptionWrapper.Throw()
at
com.versant.core.jdo.VersantPersistenceManagerImp.handleException(Throwable x)
at com.versant.core.jdo.VersantPersistenceManagerImp.getObjectById(Object oid, Boolean validate)
at com.versant.core.jdo.UnsynchronizedPMProxy.getObjectById(Object o, Boolean b)
at com.versant.core.jdo.QueryResultBase.resolveRow(Object row, VersantPMProxy pm)
at com.versant.core.jdo.ForwardQueryResult.getNextData(Int32 windowIndex)
at com.versant.core.jdo.ForwardQueryResult.internalGet(Int32 requestedIndex, Int32 maxAvailableIndex)
at
com.versant.core.jdo.ForwardQueryResult.get(Int32 index)
at OpenAccess.RT.ListEnumerator.setCurrent(Int32 _pos)
at OpenAccess.RT.ListEnumerator.Move(Int32 relative)
at OpenAccess.RT.ListEnumerator.MoveNext()
at
OpenAccess.RT.TypedEnumerator`1.MoveNext()
at
DataDomain.DataAccessorEngine.Demands_GetAutomaticDemandTemplatesNEW(IList`1 leaseTypes, Boolean scGenerateDemands, DateTime date) in DataAccessor.cs: line 556
at
DataDomain.DataAccessor.Demands.GetAutomaticDemandTemplatesNEW(IList`1 leaseTypes, Boolean scGenerateDemands, DateTime date) in DataAccessor.cs: line 238
Next time it was demand #34, etc. It is because there are really no 12, 34 or 1..11 demands in the DB. I know you fully reprogrammed the engine; however could you please confirm that this approach will work in the current version?
Another idea could be about using a transfer object.
Look, the Demand objects that the stored procedure returned were just partially filled. The data domain logic is responsible to finish them. So, fortunately I had some fields to pass some messages from the stored procedure, like an exception message. However it looks as a poor idea. (And this generally didn’t work as I explained above.)
So, I used the second approach, and got scalars, then built a transient object to use in the code. But I thought… Could TOA help me with it?...
Could you please explain, is it possible to define a persistent class with no corresponding table? So a stored procedure could return them?
Moreover, we could continue with the transfer objects…
What if we will have an interface to send a transfer object *to* a stored procedure? So the engine could parse the stored procedure parameters according to the object fields: @CustomerID <=> int customerID or even @CustomerID <=> OurCustomerClass customer? It would be just fine if the engine will not ask to have every ‘persistent’ class field as the procedure parameter. Well, it might be the stored procedure could need just for a single ID field. Actually it could look like the MS built in DataAdapter does with DataTable.
This way we could get a transient object directly from a SP, put it to another SP… Well, I’m not sure that is too much required, but hey, we would be happy with it. :-)
Okay…
Two more questions.
I used a stored procedure that returned a lot of scalars. I.e. some records. To build domain objects. During the building TOA lazy loaded referenced objects. As I wrote above, it took extremely long time. I wasn’t able to use any fetch groups of course. And I used another approach:
Create procedure ADG_PrepareDueDemands |
… |
@ResultType int |
as |
--<SOME CODE TO BUILD THE DEMANDS - #Demands> |
declare @RTDemands int, @RTTenants int, @RTUnits int, @RTProperties int, @RTPreviousDemands int, @RTBankAccounts int |
select @RTDemands = 0, @RTTenants = 1, @RTUnits = 2, @RTProperties = 3, @RTPreviousDemands = 4, @RTBankAccounts = 5 |
if @ResultType = @RTDemands |
begin |
select * |
from #Demands |
return |
end |
if @ResultType = @RTDemands |
begin |
select * |
from TENANT |
where TKEY in (select distinct TKEY from #Demand) |
return |
end |
… |
raiserror('Unknown result type', 16, 1) |
And so, the code executes the stored procedure six times to load all the most diverse objects into the scope. That works rather quickly. But it could work much quicker if you could resolve two problems:
1) the T-SQL <SOME CODE TO BUILD THE DEMANDS - #Demands> works six times and it is rather heavy code. How would it be good to run it one time and keep the result in a temporary table accessible to other 5 passes. It would be easy to do with T-SQL but Vanatec executes my SQL with BEGIN TRANSACTION … ROLLBACK bracket. Another idea could be in getting a list of object lists from the stored procedure at once. E.g.: ArrayList{IList<Demands>, IList<Tenants>,…}
2) Unfortunately my Vanatec version wasn’t able to return any multiply fields ID object. It looks like a bug, and I’m not sure have you fixed it or not. Could you please check this out?
Hopes I will be happy to check the latest version soon.