Skip to main content

DataModels & Transactions

10 replies [Last post]
rbair
Offline
Joined: 2003-07-08
Points: 0

This is kind of a carry-over from the "approaching datamodels" thread. The community mentioned the need for transactions in the DataModel/Binding API, and this thread is intended to address that issue.

Does anybody out there have any experience in the javax.transaction package? I'm wondering if we couldn't leverage the work already done there for transactions. From my initial readings on the subject, it looks like we could use javax.transaction.UserTransaction (I think?).

In simple applications transactions would live only within the current VM. We would therefore need a simple TransactionManager implementation that manages transactions within the context of a single VM. This should be doable, I think.

For complex applications such as those requiring distributed transactions, we could use a remote TransactionManager (for example, an application server) instead.

The thing I'm interested in the most is that the semantics and API for using transactions should be the same regardless of whether a simple transaction scheme is used or a complex distributed transaction scheme. I'm wondering if we can avoid reinventing the wheel by using javax.transaction stuff.

Richard

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Kleopatra

> For what its worth I finally read the JGoodies binding slides.
> I disagree with Karsten over putting a buffer in the Binding
> (since we introduced the Transaction interface which wasn't
> taken into account in Karsten's design), but kind of liked
> the idea of binding to a model instead of directly to a component.

Careful: there is no equivalent to "Binding" in JGoodies Binding
Framework. Everything's a ValueModel chained from the domain data to the
component. A BufferedValueModel can be used anywhere in this chain. The
fun with the buffer is that you can easily hook as many buffered models
to one Trigger as you like - committing/resetting then will be done by
switching the trigger.

Coming back to JDNC context: we agreed that there are different levels
of commit, didn't we? As I understood you, Transaction is responsible
for the commit/reset to/from the backend ("second level"). There is no
easy general solution for handling commit/reset between
form/components/bindings and in-memory data ("first level"), which might
be an area for buffering "goodies style".

Greetings
Jeanette

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rbair
Offline
Joined: 2003-07-08
Points: 0

Well, I've been working over transactions over the past couple of days, and attempting to fit it into my demo. The good news is, I think I've got it. But it sure would be nice to have it worked over by the community. Some hefty criticism now is a lot better than hefty criticism later :)

I added a new data type called "Transaction". The Transaction interface is pretty basic, it contains the following methods:

commit
rollback
setAutoCommit
addDataSource
removeDataSource

and a few less significant methods.

A transaction represents a unit of work that can either be rolled back, or committed. If committed, all of the work is committed or none of it is.

This Transaction interface represents a single transaction in this client VM. The transaction may include multiple DataModels. DataModels bound in a master/detail heiarchy are by nature part of the same transaction. Because it is possible for a master/detail heirarchy to span multiple DataSources (that is, since a master/detail heirarchy can theoretically be established between DataModels tied to different DataSources), a single transaction can span multiple DataSources. However, it is not possible for only part of a DataSource to be involved in a transaction. These considerations must be taken into account when coding a custom DataSource.

The transaction interface supports the concepts of committing a transaction and rolling back a transaction. A transaction is always in progress, except when committing or rolling back. If auto-commit is true, then any time a value change is made in any DataModel within this transaction, that change is propogated to the data store via the DataSource. Also, at any time during program execution the GUI may request that the DataSource save information to the data store [i]while in the current transaction[/i]. If a DataSource was implemented to connect to a JDBC 2.0 compliant database, for example, it could start a transaction with the database and save the information to the database.

If your application supports distributed client transaction functionality, the default implementation of the Transaction interface will not be sufficient, and you will need to write a custom distributed Transaction implementation. That custom implementation should bypass the DataSource objects when communicating with the remote transaction peers. Use of the DataSources should be limited to commit/rollback functionality.

Another very important task of the Transaction is to propogate changes from one DataModel within the Transaction to another. For example, say you have an object of type Person p, and two DataModels called A and B. If both of those DataModels are bound to the same Person (in JavaBeanDataModel speak, A.setJavaBean(p), B.setJavaBean(p)), then when A.setValue("firstName", "Richard") is called the components bound to A will be updated and A must then somehow propogate notice of that change to B so that B may update the components bound to B. However, this should only occur if both A and B are in the same transaction. Thus, the transaction must be taken into account when propogating these changes throughout the DataModels within the active Transaction.

As a side note, the Transaction interface experimentally supports undo/redo functionality. Transactions, therefore, contain a log of what edits were perfomed to which DataModels so that those edits can be repealed or replayed as the situation may demand. Note that some Swing components contain their own Undo framework. This is not an attempt to supercede that framework, but rather, to support it. For example, the user may have added an item to a list, then changed the description of that item, and then made many changes in a JEditorPane for that item. The JEditorPane, using the Swing Undo framework, would allow the user to type 'ctrl-z' repeatedly to undo the changes in that component. However, if the user wanted to undo the changes in that component, and the description component and also wanted to remove the item added to the list, then a more encompassing undo framework must exist that spans components. This Transaction architecture is just such a framework. Operations can be undone up to the last commit point. Thus, if in auto-commit mode, undo/redo are null ops. This has not yet been implemented in the incubator, but has been stubbed out.

The DataSource interface was also changed to support transactions. Transactions can be defined and used from within the gui. At various times during the execution of the gui it may become necessary to temporarily persist some data to the data store. This persistence may occur within the context of a transaction. Therefore, it is necessary for the DataSource API to support the concept of transactions on the data store. The DataSource API has the following four methods:

[b]setAutoCommit[/b] - If set to true, then any "saves" that occur will be committed to the underlying data store immediately. In this mode, transactions in essence do not occur. If set to false, then any "saves" that occur will be within the context of a transaction, which will "live" until either commit or rollback is called.

[b]canCommit[/b] - Returns true if a subsequent call to commit will succeed on the data store. This is part of a two-phase commit process.

[b]commit[/b] - Commits the current transaction in the data store. This call does nothing if autoCommit is true.

[b]rollback[/b] - Rolls back the current transaction in the data store. This call does nothing if autoCommit is true

Well, this is a whole lot to chew on at once, but I'll post a message when the demo is updated with all of these changes and the new source code.

Richard

kleopatra
Offline
Joined: 2003-06-11
Points: 0

Hi Richard,

> Well, I've been working over transactions over the
> past couple of days, and attempting to fit it into my
> demo. The good news is, I think I've got it. But it
> sure would be nice to have it worked over by the
> community. Some hefty criticism now is a lot better
> than hefty criticism later :)
>

You are moving ahead near light velocity - and I'm already panting while trying to catch up :-) So I won't even try to peek into the db-near implementations issues, you are the expert in that realm.

BTW, the master/detail demo still throws lots of exception on startup and all fields bound to rowset/hibernateDM are empty, looks like the connection to the backend is not initialized correctly. Any ideas?

Anyway, I have a few comments to your code (some only loosely related to the transaction handling).

First, I really like the idea of a dedicated Transaction: looks like a clean boundary to fence off modified data from the unchanged and make sure that a commit is all-or-nothing.

As a (temporary? necessary? designed? :) side-effect the form-based commit/reset mechanism is bypassed completely. Until now the "commit-border" is between the DataModel and the Binding and the responsibility of caching non-committed data is left to the Binding (which it doesn't in case of binding collection types - ours at least don't - and the JDNC team was clever enough to side-step the problem by not showing an example ). In this perspective a DataModel is always "unmodified" at least on the level of the UI. Or not? With the Transaction the commit-border is moved into the direction of the backing store - a DataModel seems to "don't care about modified state, Transaction will handle". Hmm ... or will we have multiple commit-boundaries?

Another issue I would like to raise again (yeah - I can be boring in repeating issues with a slightly changed pov :-) - remember that I felt a bit uneasy about moving the adapting of a DataModel to a TableModel into the Binding? As I now see it the problem is not so much the very fact of adapting (formally the TableBinding can use a converter as provided through the MetaData and some nested MetaData that carry a MetaDataProvider to only show a subset of fields as columns) but the inconsistency in how the DataModel is used. As I see it the Binding is obliged to bind to exactly one _field/value_ of a DataModel, not to the complete DataModel (the field can return a value of type DataModel, though:-) - from the api-doc "Class which binds a user-interface component to a specific element in a data model." As always there are several ways out, f.i. change the contract or change the implementation, but I think it has to be a conscious decision before doing to much work with slightly bent contracts (they'll bite us sooner or later :-)

Somewhere (did not try to find the reason) there's a problem with notification after changing the record: There should be no need to make the AbstractBinding listen to recordIndex propertyChanges - it's the DataModel's responsibility to propagate navigational-induced value changes, IMO. If it doesn't I would view it as a red-blinking alarm and try to track it down. Event propagation is inherently difficult to debug, and we'll get into hell anyway as soon as structure modification is added - so the earlier it get's dug out of the dirt the better. (Amen , can't resist some preaching from time to time :-)

Greetings
Jeanette

PS: just in case it did not shine through all this nagging - good work, and I always love a good code-reading!

rbair
Offline
Joined: 2003-07-08
Points: 0

Hey Jeanette,

Thanks for the pat on the back -- its nice to get those every now and then :).

> BTW, the master/detail demo still throws lots of
> exception on startup and all fields bound to
> rowset/hibernateDM are empty, looks like the
> connection to the backend is not initialized
> correctly. Any ideas?

Hmmm.... send me the stack trace you're getting. I take it that you are building from source since I haven't posted the hibernate-enabled demo yet (have I?). I may have missed a jar file or two.

> As a (temporary? necessary? designed? :) side-effect
> the form-based commit/reset mechanism is bypassed
> completely. Until now the "commit-border" is between
> the DataModel and the Binding and the responsibility
> of caching non-committed data is left to the Binding
> (which it doesn't in case of binding collection types
> - ours at least don't - and the JDNC team was clever
> enough to side-step the problem by not showing an
> example ). In this perspective a DataModel is
> always "unmodified" at least on the level of the UI.
> Or not? With the Transaction the commit-border is
> moved into the direction of the backing store - a
> DataModel seems to "don't care about modified state,
> Transaction will handle". Hmm ... or will we have
> multiple commit-boundaries?

Something to consider is change propogation within the transaction. If we have two components bound to the same field on a DataModel (or perhaps the same underlying JavaBean!) then we will want to notify the second component when a change is made via the first component. This sort of implies two separate commits as well. The "first level" commit would be from the bound component to the DataModel. The "second level" commit would be to commit the transaction to the data store.

The first level commit happens when the component loses focus, the user hits "enter", or selects a checkbox, or perhaps in some cases on every keystroke. Change propogation occurs after every first level commit.

The second level commit happens either every time a first level commit is made to a DataModel outside of a transaction or within a transaction that is in auto-commit mode, or is done when the transaction is explicitly committed if within an active transaction. Note that if a transaction is in progress, then "saves" or "updates" can happen against the data store without having executed a second level commit. They just don't become perminent until the second level commit happens.

> Another issue I would like to raise again (yeah - I
> can be boring in repeating issues with a slightly
> changed pov :-) - remember that I felt a bit uneasy
> about moving the adapting of a DataModel to a
> TableModel into the Binding? As I now see it the
> problem is not so much the very fact of adapting
> (formally the TableBinding can use a converter as
> provided through the MetaData and some nested
> MetaData that carry a MetaDataProvider to only show a
> subset of fields as columns) but the inconsistency in
> how the DataModel is used. As I see it the Binding is
> obliged to bind to exactly one _field/value_ of a
> DataModel, not to the complete DataModel (the field
> can return a value of type DataModel, though:-) -
> from the api-doc "Class which binds a user-interface
> component to a specific element in a data model." As
> always there are several ways out, f.i. change the
> contract or change the implementation, but I think it
> has to be a conscious decision before doing to much
> work with slightly bent contracts (they'll bite us
> sooner or later :-)

Ya, that part bothered me a bit too, but sometimes I just ignore my conscience ;). The programmer wouldn't necessarily know that one Binding only binds to a single field while another one is bound to the entire DataModel, without reading the code or the documentation. To be honest, I've so far ignored the Binding aspect (as well as the Form aspect) to a certain degree. This is definately something that I feel can be handled better differently.

> Somewhere (did not try to find the reason) there's a
> problem with notification after changing the record:
> There should be no need to make the AbstractBinding
> listen to recordIndex propertyChanges - it's the
> DataModel's responsibility to propagate
> navigational-induced value changes, IMO.

How should the DataModel propogate that change? I thought in an earlier thread you and Aim had agreed to use a bound property on the DataModel.

> Event propagation is inherently
> difficult to debug, and we'll get into hell anyway as
> soon as structure modification is added - so the
> earlier it get's dug out of the dirt the better.
> (Amen , can't resist some preaching from time to
> o time :-)

Amen! The problem with debugging listeners is that the stack trace is next to useless. If there's a better way, lets do it.

Hope it made sense, I got up WAY to early this morning!

Richard

kleopatra
Offline
Joined: 2003-06-11
Points: 0

Hi Richard,

>
> Hmmm.... send me the stack trace you're getting. I
> take it that you are building from source since I
> haven't posted the hibernate-enabled demo yet (have
> I?). I may have missed a jar file or two.
>

Will do.

[..snip..]

Ahh, now I see where I got confused - "first" and "second level" commit, that's a good explanation, thanks :-)

>
> Ya, that part bothered me a bit too, but sometimes I
> just ignore my conscience ;). The programmer wouldn't
> necessarily know that one Binding only binds to a
> single field while another one is bound to the entire
> DataModel, without reading the code or the
> documentation.

dont want to interfere with your conscience handling :-) But, no: it's not a matter of the programmcer knowing, it's a matter of consistent interpretation of which parameters are passed around for the sake of the framework.

And it's very straightforward to achieve, once you get used to it. I tried to show the "howto" in my examples - looks like nobody likes to read code , so I changed your master-detail demo slightly. Not sure about the cvsettique, so I'll add a new sub-package .kleopatra (which you can delete anytime you feel like it) to your masterdetail, c&ped all demo-classes and added the necessary data/binding changes into it. The changed/additional classes are prefixed with "W".

The purpose of the excercise: let the WTableBinding expect a _field_ with type TableModel to use for binding with a JTable.

The benefits:
a) the behaviour of the TableBinding is consistent with the Binding contract
b) the adapting of a DataModel into the form of a TableModel is pulled out of the responsibility of the Binding (where it should not be - as we already agreed).

How to do it manually (can be easily enough automated, I'm sure) is shown in WJavaBeanPane.bindTable(..): create the adapter DataModel --> TableModel and set it as the value of the field "items" in a WDefaultDataModel. Then bind the latter with a WTableBinding to the table.

The WDefaultDataModel is a c&p from the original - plus the rbair specifics. In the process of doing so I got remembered why I don't like big interfaces - navigation does not make sense in a one-dimensional implementation (SCNR ;)

WTableBinding is a c&p from my binding package, simply commented some specifics.

Similarly, the list could be bound - in the end I was to lazy to actually do it, mostly because the selection hooking is handled in your ListBinding as well (will go out of there anyway?)

> > There should be no need to make the
> AbstractBinding
> > listen to recordIndex propertyChanges - it's the
> > DataModel's responsibility to propagate
> > navigational-induced value changes, IMO.
>
> How should the DataModel propogate that change? I
> thought in an earlier thread you and Aim had agreed
> to use a bound property on the DataModel.
>

Sorry, expressed myself sloppyly: yes, we did agree that "recordIndex" should be a bound property. But if the navigation (== change recordIndex) lead to a change in field values, then the DataModel additionally has to fire valueChangeEvents for all fields. As far as values are concerned listeners can rely on being notified about every change, whatever the reason that triggered such a change.

And that's just the beginning of what I like to think of as "notification hell": with the number of different event types the possible paths by which a need to fire any one of them tend to explode ... and then there's always the one or other combination overlooked.

>
> Amen! The problem with debugging listeners is that
> the stack trace is next to useless. If there's a
> better way, lets do it.
>

Just curious: which debugger are you using? For my part, I feel quite comfortable with Eclipse and conditional breakpoints.

> Hope it made sense, I got up WAY to early this
> morning!

Yes it does, thanks. Hope I did not wake you up

Greetings
Jeanette

rbair
Offline
Joined: 2003-07-08
Points: 0

Good Morning,

> dont want to interfere with your conscience
> handling :-) But, no: it's not a matter of the
> programmcer knowing, it's a matter of consistent
> interpretation of which parameters are passed around
> for the sake of the framework.
>
> And it's very straightforward to achieve, once you
> get used to it. I tried to show the "howto" in my
> examples - looks like nobody likes to read code ,
> so I changed your master-detail demo slightly. Not
> sure about the cvsettique, so I'll add a new
> sub-package .kleopatra (which you can delete anytime
> you feel like it) to your masterdetail, c&ped all
> demo-classes and added the necessary data/binding
> changes into it. The changed/additional classes are
> prefixed with "W".
[snip]

Cool. I'll take a look once I get home. Sorry I couldn't look at your code earlier, its been a crazy week.

> Similarly, the list could be bound - in the end I was
> to lazy to actually do it, mostly because the
> selection hooking is handled in your ListBinding as
> well (will go out of there anyway?)

Hmmm. Just another thought, what about having two bindings, one for binding fields and one for DataModels? The thing I like about binding to a DataModel as a whole is that

1) The UI/developer doesn't have to set up listeners

2) The developer doesn't have to do any extra setup on the DataModel to bind a component to a DataModel.

Just thinking out loud...

> > > There should be no need to make the
> > AbstractBinding
> > > listen to recordIndex propertyChanges - it's the
> > > DataModel's responsibility to propagate
> > > navigational-induced value changes, IMO.
> >
> > How should the DataModel propogate that change? I
> > thought in an earlier thread you and Aim had
> agreed
> > to use a bound property on the DataModel.
> >
>
> Sorry, expressed myself sloppyly: yes, we did agree
> that "recordIndex" should be a bound property. But if
> the navigation (== change recordIndex) lead to a
> change in field values, then the DataModel
> additionally has to fire valueChangeEvents for all
> fields. As far as values are concerned listeners can
> rely on being notified about every change, whatever
> the reason that triggered such a change.
>
> And that's just the beginning of what I like to think
> of as "notification hell": with the number of
> different event types the possible paths by which a
> need to fire any one of them tend to explode ... and
> then there's always the one or other combination
> overlooked.

Oh, ya, that makes sense :).

> >
> > Amen! The problem with debugging listeners is that
> > the stack trace is next to useless. If there's a
> > better way, lets do it.
> >
>
> Just curious: which debugger are you using? For my
> part, I feel quite comfortable with Eclipse and
> conditional breakpoints.

I use Eclipse, but have only scratched the surface of conditional breakpoints.

Later,
Rich

Kleopatra

jdnc-interest@javadesktop.org wrote:
> Good Morning,
>
>>Similarly, the list could be bound - in the end I was
>>to lazy to actually do it, mostly because the
>>selection hooking is handled in your ListBinding as
>>well (will go out of there anyway?)
>
>
> Hmmm. Just another thought, what about having two bindings,
> one for binding fields and one for DataModels?

There are many ways to solve a problem :-) Personnally I prefer to go
with the one with the least ambiguity. The problem with too many degrees
of freedom is that the overall behaviour of the system is very hard to
keep stable or predict.

> The thing I like about binding to a DataModel as a whole is that
>
> 1) The UI/developer doesn't have to set up listeners
>

The backend developer's primeval fear of ui-listeners No offense
meant, just joking in the middle of the night.

> 2) The developer doesn't have to do any extra setup on the
> DataModel to bind a component to a DataModel.
>

Hmmm... as I see it in the long run the developer would rarely come into
direct contact with a DataModel and Binding: instead s/he will set up a
metaData hierarchy and let a form handle the dirty details, even if you
want to do the component creation and/or layout yourself. What we are
doing now are just the prelimaries - don't take my code too literally in
that respect, as I said, I can automate it easily enough (at least
that's what I expect, being optimistic :-) - just wanted to show some
principle. Chain, chain, chain ...

>>And that's just the beginning of what I like to think
>>of as "notification hell": with the number of
>>different event types the possible paths by which a
>>need to fire any one of them tend to explode ... and
>>then there's always the one or other combination
>>overlooked.
>
>
> Oh, ya, that makes sense :).
>

As I always think that code is easier to understand than a description
of code: today I committed a TestCase against your JavaBeanDataModel
(it's in my test package and called something starting with RB - too
tired to fire up Eclipse now, as probably it would lead to a bit of
tweaking here and there :-). It's sole concern is about the impact on
change notification and binding, showing some issues to be aware of - as
I see them, open to discussion, of course :-).

BTW, thanks for reading and analizing my error output - I'm really
curious what will come out of it in the end.

Good night
Jeanette

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rbair
Offline
Joined: 2003-07-08
Points: 0

Hey,

> > The thing I like about binding to a DataModel as a
> whole is that
> >
> > 1) The UI/developer doesn't have to set up
> listeners
> >
>
> The backend developer's primeval fear of ui-listeners
> No offense
> meant, just joking in the middle of the night.

None taken :). You address the root of this concern below...

>
> > 2) The developer doesn't have to do any extra setup
> on the
> > DataModel to bind a component to a DataModel.
> >
>
> Hmmm... as I see it in the long run the developer
> would rarely come into
> direct contact with a DataModel and Binding: instead
> s/he will set up a
> metaData hierarchy and let a form handle the dirty
> details, even if you
> want to do the component creation and/or layout
> yourself. What we are
> doing now are just the prelimaries - don't take my
> code too literally in
> that respect, as I said, I can automate it easily
> enough (at least
> that's what I expect, being optimistic :-) - just
> wanted to show some
> principle. Chain, chain, chain ...

Ya, that would make the difference. Putting the listener & model in the binding would keep the developer from having to do it. But, if the developer never actually *sees* the binding, then it doesn't really matter. In fact, leaving it out of the binding gives you more flexibility in the end.

For what its worth I finally read the JGoodies binding slides. I disagree with Karsten over putting a buffer in the Binding (since we introduced the Transaction interface which wasn't taken into account in Karsten's design), but kind of liked the idea of binding to a model instead of directly to a component.

> As I always think that code is easier to understand
> than a description
> of code: today I committed a TestCase against your
> JavaBeanDataModel
> (it's in my test package and called something
> starting with RB - too
> tired to fire up Eclipse now, as probably it would
> lead to a bit of
> tweaking here and there :-). It's sole concern is
> about the impact on
> change notification and binding, showing some issues
> to be aware of - as
> I see them, open to discussion, of course :-).

Cool. I'll take a look when I can.

> BTW, thanks for reading and analizing my error output
> - I'm really
> curious what will come out of it in the end.

No prob, still waiting to hear back from Jonathan.

Rich

PS> I'm working on refactoring my DataSource design to make it easier to create new DataSources. As a result, I'm writing a ProgressDialog to handle multiple DataSource notification events and things. I got all excited about it yesterday. I've got to do it now while I'm "feelin' the vibes". So, I might be a little slow on getting you feedback on the binding/notification stuff.

PPS> In the latest version of the demo (with the refactoring of the DataSource stuff) your version of the demo froze after commiting the RowSetDataModel changes. I'm not sure what the problem is yet, I probably butchered some threading code somewhere...

Amy Fowler

>
> Ya, that would make the difference. Putting the listener & model in the binding would keep the developer from having to do it. But, if the developer never actually *sees* the binding, then it doesn't really matter. In fact, leaving it out of the binding gives you more flexibility in the end.
>
> For what its worth I finally read the JGoodies binding slides. I disagree with Karsten over putting a buffer in the Binding (since we introduced the Transaction interface which wasn't taken into account in Karsten's design), but kind of liked the idea of binding to a model instead of directly to a component.

Unfortunately not all components have models, which is why the
current jdnc binding apis take components, but attempt to provide
overloaded constructors which take models whenever possible.

Aim

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Kleopatra

>
> Unfortunately not all components have models,

They don't?

> which is why the
> current jdnc binding apis take components, but attempt to provide
> overloaded constructors which take models whenever possible.
>

ahh, some secret api, I suspected so much after weeks of silence

Jeanette

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net