Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » in memory rollback
in memory rollback [message #384249] Fri, 12 December 2008 08:54 Go to next message
Tom Eugelink is currently offline Tom Eugelink
Messages: 806
Registered: July 2009
Senior Member
Ok, I know these are complex questions, but I just throw them in the group to see if anyone has a bright idea.

Again this is about the entities that are created/modified/remove based on changes made in other entities (read: stock modifications).

The scenario I'm trying to solve is this:
- An "transfer" entity has two changes; two amounts were altered, that belong to each other (like a transfer between bank accounts).
- Based on amount 1 a stock change "A" is made
- Based on amount 2 another stock change "B" is attempted, however the validation throws an exception because the change is illegal (would cause a negative stock).

The exception correctly is shown and change "B" is not made, however change "A" still exists in memory.
If the user navigates away from the screen, he is notified that change "A" is still pending and is asked to persist it: only a partial transaction is performed!

Now, I can solved this from within a closed situation (with just the two related amounts as described above) by holding off on applying the change until the second one also turns out ok. But this also can happen in multiple amount-pairs. E.g. pair 1,2,3 are ok and pair 4 fails. Basically what I need is in-memory transactions. I need to be able to snapshot a memory state and all changes on entities after that must be rollbacked if an exception occurs.

Now, my business model is fully property-change-event supporting, that means that every property, even collections, fire PCE's. I could record all the PCE's and try to roll them back... But it seems like quite a thing to build.

Any suggestions?

Tom
Re: in memory rollback [message #384251 is a reply to message #384249] Fri, 12 December 2008 09:07 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom Eugelink
Messages: 806
Registered: July 2009
Senior Member
> Again this is about the entities that are created/modified/remove based
> on changes made in other entities (read: stock modifications).


Maybe I should stop trying to do this through JPA and use direct SQL...
Re: in memory rollback [message #384253 is a reply to message #384249] Fri, 12 December 2008 10:13 Go to previous messageGo to next message
Gordon Yorke is currently offline Gordon Yorke
Messages: 77
Registered: July 2009
Member
I don't see how SQL would solve your problem. You have user requested
changes to Entity instances that are not valid. You will need to develop
some mechanism in your application to notify the user and refresh the
user's view. EM.refresh() can rollback the changes to the Entities within
the Persistence Context if that is all you need.
--Gordon
Re: in memory rollback [message #384255 is a reply to message #384253] Fri, 12 December 2008 11:34 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom Eugelink
Messages: 806
Registered: July 2009
Senior Member
Gordon Yorke wrote:
> I don't see how SQL would solve your problem. You have user requested
> changes to Entity instances that are not valid. You will need to
> develop some mechanism in your application to notify the user and
> refresh the user's view. EM.refresh() can rollback the changes to the
> Entities within the Persistence Context if that is all you need.
> --Gordon
>

Not quite. I have user made changes and system made changes (based on the user changes), I only need to rollback the latter. By using SQL for the system changes, I can use the database rollback to roll that back, while keeping the user made changes in memory.

Say there is a "transfer" entity moving 10 dollars from account A to account B. That is the instruction: "this must happen". Now the system starts actually implemeting that order, resulting in an "substraction" entity on account A and "addition" entity on account B. The addition may fail and both have to be rolled back, but the transfer entity should remain in memory. I cannot have a "transfer" entity in my system, without corresponding sub and add.

An approach would be to mark a transfer as "unbooked", store it, commit, and only then start creating the sub/add entities. Upon success the transfer is marked "booked" and all is committed, or on fail you can indeed clear all entities. That may work.


Tom
Re: in memory rollback [message #384356 is a reply to message #384249] Mon, 15 December 2008 06:45 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom Eugelink
Messages: 806
Registered: July 2009
Senior Member
> Any suggestions?

I'm trying another approach: instead of rolling back, continue where you failed.

If change 4 failed and you press save again, change 3 does not need to be "redone", only change 4 is retried (and probably will fail again). But as long as it keeps failing when it should, all is consistent.
Re: in memory rollback [message #384357 is a reply to message #384255] Mon, 15 December 2008 19:21 Go to previous messageGo to next message
Doug Clarke is currently offline Doug Clarke
Messages: 155
Registered: July 2009
Senior Member
I would vote for your solution where the transfer is committed first in
its own transaction with booked=false. That makes sure the must have
operation is written and cannot be lost.

Doug
Re: in memory rollback [message #384358 is a reply to message #384357] Tue, 16 December 2008 02:36 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom Eugelink
Messages: 806
Registered: July 2009
Senior Member
> I would vote for your solution where the transfer is committed first in
> its own transaction with booked=false. That makes sure the must have
> operation is written and cannot be lost.

Yes. Well. That works kinda acceptable when a new entity is created, but when the entity already exists, and is changed, this is no good.

If a new entity is created, you end up with an entity marked as "dirty" but no allocations, that is acceptable. But when an entity is changed, the old allocations are not changed. So you end up with an inconsistent situation. What when the user then decides to navigate away? So I really really want to commit all or nothing.

By retrying during save I can prevent this as a result of a change. The problem was with removing. By using my application level duringDeletePreRemove event I can force all to-be-removed entities to set the values to zero, thus causing the allocations to be removed and validated. This may fail, but since it is BEFORE remove, that works out well so far.

This approach needs a shake down in real life.

Tom
Re: in memory rollback [message #384359 is a reply to message #384358] Tue, 16 December 2008 05:26 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom Eugelink
Messages: 806
Registered: July 2009
Senior Member
> using my application level duringDeletePreRemove event

BTW, this is another example where I need to use application level events. The initial idea was to have a closed business model that I can use in the 2-tier application, but also in SOAP and EDIFACT alike exchange, and naturally in webbased applications. But that would require the correct events to be fired in the BM layer or lower.

I still need to examine what events the EM session fires exactly (you suggested that), but I have a problem fitting the EM-event concept into the BM. Suppose the EM indeed fires the correct "duringSavePostMerge" and "duringDeletePreRemove" alike events, then I still do not want to handle those in the application layer, but all must be done in the BM, so I need a way to have a entity react to that.

First I though that I would need to extend all queries that return the entity (that needs to listen to these events) with code to also add that entity as a listener to the EM that the query is executed upon. But when lazy loading comes into play, that won't work.

Another approach is that I maybe could use the "postLoad" event of the entity, and bind it to the EM there (I have code that "automagically" can determine the active EM). I cannot use the constructor to bind newly created objects, because I'm not certain the object will be persisted. So I have to extend the persist method of the EM to also add the entity as a listener. And that is a bit weird; extend the persist method to add listeners. I do not like that. I already have an extended EM to fix JPA/Eclipselink issues, but this is an application concern moving into the persistence layer.

Or I have to that take approach one level up and introduce my own JPA event annotations. That would allow me to cleanly extend the EM, to support the new events. But then the whole new-event-entity-binding thing conceptually should move to the extended EM, and should not rely on the @PostLoad event code of my business model.

Again, I haven't examined the EM events yet, but I also don't have a clear picture on how I can do this correctly.

Tom

BTW: if you are interested in the extensions I made to the EM, I would be pleased to provide the sources for your review.
Re: in memory rollback [message #384360 is a reply to message #384359] Fri, 19 December 2008 10:53 Go to previous messageGo to next message
Doug Clarke is currently offline Doug Clarke
Messages: 155
Registered: July 2009
Senior Member
Tom,

It would be great to see your enhancements. Could you log enhancement
requests describing the work and attach your code to the bug?

Thanks,

Doug
Re: in memory rollback [message #384362 is a reply to message #384360] Sat, 20 December 2008 02:56 Go to previous message
Tom Eugelink is currently offline Tom Eugelink
Messages: 806
Registered: July 2009
Senior Member
> It would be great to see your enhancements. Could you log enhancement
> requests describing the work and attach your code to the bug?

#259432
So. Let me know if I chose a stupid solution. It works for me, so I can handle criticism ;-).

I have another thing laying around: I use my BM as a black box. For example: you get the class and simply call findByPk or findByDescription and you get your entities. Or you call a "duplicate" method and the entity is duplicated. Than you call the JpaUtil.save() method and everything is persisted.
Now, somewhere underneath all this EntityManagers are used. The BM must be able to find what EM to use in order to query or persist. So I've written a number of EntityManagerFinders. One is a thread based EM, which is intended for web based applications (in the service method you bind an EM to a thread).
Another one is the Swing version; that one determines the correct EM based on the component that currently has focus (it's a bit more complex than that, but that is the essence). It assumes each JFrame has an EM associated, so you have different entities per JFrame, but it is fairly flexible (one EM for a number of frames, etc).
Again, this works fine for me (in a production application). In the Swing version you only have to be careful when spawning tasks off to worker threads (the Swing EMFinder extends the Thread version and you need to use that there). But for 99% of all code "it just works", including JDialogs.

And while I'm at it, I also have a database reverse engineer tool (one class per table) laying around. It analyzes a database and generates a three level BM. Deepest level is optional and is intended for a generic "Bean" class that provides stuff like property change listener code, etc. The middle level is the actual reverse engineered class. The top level is an initially empty class that only defines the table that is persisted to and is never overwritten by the tool, here you can put custom logic.
Oh, and I'm one of the "configuration by code" guys, so I generate a number of constants to support that.
Oh2, and I generate a XML, as a resource to the entity's class, where you can place translations for the properties in multiple languages, so the BM comes with build-in multi language support.
The reverse engineering does require the use of one-field-primary keys, even in N-M link tables.

Last but not least is a Swing query builder. It analyzes -at runtime- an entity and constructs a swing panel with fields for all properties. Relations are fold-out panels and in the end you end up with a JPA query. It does require a number of special fields in the entities to enable two-way searching, and assumes that JPA is based on the fields and not the setters and getters. The reverse engineering class naturally generates all this, so if both are used, it is not an issue.
And once an search panel has been created, it is cached (max one per entity) to speed things up.

I chose to use fields for JPA initially and had some doubts, but it turned out quite well on a number of occasion; this search panel, but also no conflict for including AspectJ.

If you want I can provide you with sources and or screenshots. I get a free persistency layer, so this could be my contribution back.
Please note that the classes have been developed as I go along, especially the reverse engineering tool; they work just fine, but would need an overhaul before being made public.

Tom
Previous Topic:mappingworkbench private owned vs. cascadetype ?
Next Topic:Dali JPA Tools 2.1 now available for download
Goto Forum:
  


Current Time: Sun Apr 20 17:56:42 EDT 2014

Powered by FUDForum. Page generated in 0.01862 seconds