From: Gavin_King/Cirrus%<CI...@ci...> - 2002-05-17 07:19:48
|
> I know that commit() and cancel() were recently deprecated, but > I'm thinking it might be a good idea to add some methods for a > slightly more general transaction handling mechanism to the Session > class. hhmmmmmmm .... damn this is all coming back to haunt ..... Lets state the problem carefully: What API allows the user to END a Session and (1) is easy to use and understand for new users, (2) has methods with clear semantics, (3) supports the various different possible styles of connection + transaction management eg. application server datasource + JTA, DriverManager + Connection.commit(), long transactions, (4) comforms to the philosophy that Hibernate is a thin wrapper around JDBC that exposes all underlying JDBC functionality to the application. (A wrapper with "windows"?) The old approach with Session.commit() + Session.cancel() tried to be a great solution to (1) but because it miserably failed at (2) ended up being very confusing for _all_ users. The new API is a much better solution in terms of (2), (3) and (4) - particularly (2) - but still requires a fairly lengthy explanation about how to actually end a Session - something that should, on the face of it, be so damn simple!! > What I have in mind is that Session would support at least three > additional methods, something like beginTransaction, > commitTransaction, and rollbackTransaction. These would delegate > to an object which implements a "SimpleTransactionManager" interface > with those methods. When we had this discussion earlier, Brad Clow commented that one thing that was so confusing was that we were overloading the word "commit" to mean something other than committing a transaction. I agreed at the time, and still agree. If we do something like you are suggesting, we should not use the words "commit", "begin" "rollback" or "transaction" anywhere on the Session interface. I would suggest something along the lines of: public interface TransactionStrategy { public Connection begin() throws ....; // fetch a connection public void commit(Connection connection) throws ...; // commit, close connection public void rollback(Connection connection) throws ....; // rollback, close connection } public interface Session { ..... public void end() throws ....; //commit, close public void cancel() throws ....; //rollback, close public void pause() throws ....; //commit, disconnect public void resume() throws .....; //reconnect } Then client code could look like: Session s = factory.openSession(sessionLifecycle); try { //do some work .... s.flush(); } catch (Exception e) s.cancel(); throw e; } s.end(); Session.pause() and Session.resume() would be used to disconnect and reconnect. > Hibernate could supply a couple of really > simple implementations of this interface, e.g. one which just maps > to JDBC calls, and The implementations I can think of would be: DriverManagerTransactionStrategy JTADatasourceTransactionStrategy NonJTADatasourceTransactionStrategy > perhaps one which maps to "BEGIN TRAN", "COMMIT > TRAN", "ROLLBACK TRAN" statements as appropriate for the SQL > dialect. JDBC already does this mapping, by providing Connection.commit() and Connection.rollback() ... > application code could switch the underlying transaction > handling mechanism without changing the client code This is a 'new' requirement that is not implemented by the current API. It seems a worthwhile addition.... > IMO, the transaction-handling idiom would become a bit neater > and better abstracted: > > Session sess = factory.openSession(); > try { > sess.beginTransaction(); > //do some work > ... > sess.commitTransaction(); > } > catch (Exception e) { > sess.rollbackTransaction(); > throw e; > } > finally { > sess.close(); > } I need more opinions upon which API is better. flush(), end(), cancel() OR commitTransaction(), rollbackTransaction(), close() I'm also not keen on changing the cirrus.hibernate.Session interface again at this stage. It might be preferable to develop this API (ie. the new Session and SessionFactory interfaces) in a seperate package. Say, cirrus.hibernate.4dummies or something. ;-> The new functionality could be implemented either on the existing cirrus.hibernate.impl classes or as wrapper classes that delegate to cirrus.hibernate.SessionFactory and cirrus.hibernate.Session. After all this thinking, I'm still not 100% convinced we should go down this path..... (or not yet, anyway). If there is a general clamor of agreement that this is a much better solution, I guess we should take it. However, this may just be increasing the complexity of our API to fix a problem (hard for new users to understand) that would be just as easily fixed by better documentation. We have so far managed to cram a large amount of functionality into a very, very narrow API. This is a Good Thing. Adding new APIs now might not be.... undecided.... Gavin |
From: Brad C. <bra...@wo...> - 2002-05-19 23:29:45
|
> The new API is a much better solution in terms of (2), (3) and (4) - > particularly (2) - but still requires a fairly lengthy explanation about > how to actually end a Session - something that should, on the face of it, > be so damn simple!! i think this could be dealt with by changing/re-organising the docs a little (no matter what is decided out of this discussion) so that there is a section on usage scenarios with an example for say: ejb container, standalone app where hibernate gets the connection for u, standalone app where u supply a connection. each example should include the session factory properties and the try/catch/finally block for manipulating persistent objects. > I think part of the problem is that the relationship between a Session and a > transaction isn't as clear as it might be, and this is at least partly > because the separation between the two is currently implemented by saying > "here's the connection, you're on your own with transactions". I imagine > this is likely to encourage the tendency of users to think of a session as > being like a connection, which you can use repeatedly for multiple > transactions. i think this point is somewhat valid, even though the section titled "Sessions, Transactions and Threads" in the docs is pretty specific on the fact that transactions are delegated to the jdbc connection. unless the session behaviour is changed to support re-use for multiple transactions, adding a begin transaction type method in the api anywhere might increase the tendancy of user's to think of a session as being like a connection. if it wasn't for the ability to open a session by passing in a connection, i think commit & rollback methods on the session would be ok - as long as they don't close the session, as that should go in a finally block. commit could flush and call commit on the connection - as long as the docs made it clear that commit did this. perhaps having a separate transaction management class that threw an exception if it were used inappropriately for the environment it is in would get around my concern when using an external connection. if there r going to be commit & rollback type methods i think they should be called commit & rollback, or have those words in them as these are generally accepted and known terms. having said all that, i don't have a problem with the current api - i haven't written a transaction management wrapper (maybe i am missing something :-)) - however, most of our non-read type operations are currently done in ejbs using container managed transactions. i initially thought that calling commit/rollback directly on the connection wasn't very elegant, but it does help to re-inforce that transactions are delegated to the jdbc connection. i found this also helped when understanding integrating hibernate in an application server. brad |
From: Gavin_King/Cirrus%<CI...@ci...> - 2002-05-20 10:51:05
|
Thanks everyone for the input. Havn't been responding in detail because I wanted to get other views and because I dont have a very strong view and also cos I've been busy coding some new functionality and reworking some older code to perform better. The next release will be a pretty major update :) It would be nice if some of you guys would try out the CVS version at this stage. Okay, Anton has convinced me of the need for _something_. I notice that some other OO persistence frameworks use a Manager interface and a Transaction interface, where the Transaction object is short-lived and implements only begin/commit/rollback and the Manager is a long-lived object. For Hibernate, I hadn't seen the need for a seperate Transaction object since the Session object is short-lived. I'm now happy to reverse this decision. Rather than mix begin/commit/rollback methods onto the Session, where they dont really fit very comfortably, lets have a seperate object that controls transaction boundaries for a Session (and abstracts the underlying JTA or JDBC transactions). A Session can span multiple Transactions, to support the "long transactions with versioning" approach. ie. What I'm suggesting is, rather than having the user create the Session by passing a TransactionStrategy object and then having the Session delegate begin/commit/rollback, just let the user manipulate the transaction strategy object directly. This leaves us the freedom to change all this at a later date, without leaving a million deprecated methods lying around on Session. The remaining question (if other people agree to this approach) is how the user obtains Transactions and how they associate the Session with the Transaction. I can think of some alternatives: EITHER (1): Session s = factory.openSession(); Transaction tx = new JDBCTransaction(s); tx.begin(); try { s.update(object); //new method I just wrote ;) s.flush(); tx.commit(); } catch (Exception e) { tx.rollback(); throw e; } finally { s.close(); } OR (2): Transaction tx = new JTATransaction(); Session s = factory.openSession(); tx.begin(s); try { s.update(object); s.flush(); tx.commit(s); } catch (Exception e) { tx.rollback(s); throw e; } finally { s.close(); } OR (3): Properties props = new Properties(); props.setProperty("hibernate.transaction_strategy", "JDBC"); Session s = factory.openSession(props); Transaction tx = s.beginTransaction(); try { s.update(object); s.flush(); tx.commit(); } catch (Exception e) { tx.rollback(); throw e; } finally { s.close(); } I like both (1) and (2) for not requiring modifications to the current API (which is an enormous plus to me). All three leave the existing API meaningful ( flush() + close() + connection() are all still useful ) so no more deprecations (this makes me very comfortable). (2) has the advantage that a single Transaction object could be used for multiple Sessions (if, for example, there were multiple databases). I'm not sure if thats particularly useful ... the other approaches still allow multiple Sessions per transaction, but with multiple Transaction objects... To fully benefit from the abstraction that Anton is after, (1) and (2) would probably require a TransactionFactory interface (to let us swap JDBCTransaction for JTATransaction somewhat transparently). Properties props = new Properties(); props.setProperty("hibernate.transaction_strategy", "JTA"); TransactionFactory factory = Hibernate.createTransactionFactory(props); Then, for (1): Transaction tx = TransactionFactory.createTransaction(session); Or, for (2): Transaction tx = TransactionFactory.createTransaction(); So, in defence of approach (3) above, you end up with a simpler API. Other questions in my mind: (1) I had kind of hoped to roll the stuff thats done by ConnectionProvider into the TransactionStrategy (ie opening, closing a JDBC connection) but it now seems to make more sense to orthogonalize these things. Does everyone else concur with that? (2a) For swappable Transaction strategies to work, JTATransaction would need to look up the surrent transaction in JNDI. Is this always okay? The usual way to get the current transaction in an EJB is from the EJBContext. (2b) What about in a CMT bean? I guess you would just need to revert to Transaction tx = new ContainerTransaction(session, ejbContext); (3) Is the call to tx.begin() really necessary in approach (1)?? Surely the work could be done in the constructor.... Okay, cut me down if all this still aint enough. thanks Gavin |
From: Gavin_King/Cirrus%<CI...@ci...> - 2002-05-21 02:04:57
|
Woops. I really meant to write this in the last mail on this topic: Properties props = new Properties(); props.setProperty("hibernate.transaction_strategy", "JTA"); TransactionFactory txFactory = Hibernate.createTransactionFactory(props); Then, for (1): Transaction tx = txFactory.createTransaction(session); Or, for (2): Transaction tx = txFactory.createTransaction(); |
From: Anton v. S. <an...@ap...> - 2002-05-21 05:27:40
|
Gavin, Your latest proposals, with the factory correction, make for a very clean design. I'd be happy enough with a mechanism like this, and I'm willing to work on developing it. I have a "but", though, as you might have guessed. The good news is that I think that what I'd like to see can be implemented as a trivial wrapper on top of your latest design, so the question just becomes whether or not this sort of wrapper ought to become an official part of Hibernate. I think that separating the Transaction and the Session makes perfect sense at a certain level, but I'm personally interested in something even more high-level (you can call it "brain-dead", if you like). I have two specific issues: 1. I'm not completely comfortable with requiring that every transaction include the sequence: s.flush(); tx.commit(); I know this must seem very picky, but it seems to me that this is another case where if the idiom is always the same, a single method ought to be able to handle it, also preventing potential mistakes (like forgetting to flush - I'll avoid the metaphors that come to mind :) With your clean separation of Transaction & Session, though, I don't think that hybrid flush/commit method belongs in either of those classes, so this is where the wrapper described below would come in. 2. More generally, I think there's a case for combining the Session and Transaction functionality on a single interface, just to keep the final API as simple as possible. I think we may disagree on this, but I'd like to know whether you see the problem with a combined interface as having mainly to do with backwards compatibility, or whether you see other disadvantages to it. The wrapper I'd like to propose involves a slight change in direction, driven by the desire to keep the Session interface as unchanged as possible. To achieve this, a TransactionalSession interface would extend the Session interface, or possibly inherit from a common parent. [The name TransactionalSession is provisional, for clarity in the discussion.] The TransactionalSession interface would have a recommended usage slightly different from that of the Session interface. The Session interface would be retained as is, and would still support developers who want to manipulate their connections directly, or use transaction objects directly. The Session interface would also continue to support sessions without an overall transaction. The TransactionalSession interface would be used something like this: TransactionalSession tx = factory.beginTransaction(session); try { myObj = tx.load(MyClass.class, key); //... tx.commit(); } catch (Exception e) { tx.rollback(); throw e; } finally { tx.close(); } The biggest differences here are: 1. Since it's only intended for use in cases where an overall transaction is required, the transaction can be begun upon creation, so there's no need for a separate "begin" method. 2. Since it incorporates Session functionality, there's no need for the developer to manage two objects, a session and a transaction. 3. It's not necessary to call both flush() and commit() on a TransactionalSession. A class like "RelationalDatabaseTransactionalSession" would have a commit() method implemented like this: this.flush(); // inherited from RelationalDatabaseSession this.transaction.commit(); // delegate commit to underlying strategy-specific transaction object Although this means that flush() would be deprecated in the TransactionalSession interface, it would remain a valid method in the Session interface. One possible downside is that this can be seen as introducing a kind of dual API, which would have to be carefully dealt with in documentation. It's quite conceivable you'd want to use both Sessions and TransactionalSessions in the same program, although probably not at the same time. I think there are various ways to address this, but I'll leave that until we're more in sync on the basics. The general way I'm looking at it, though, is that Sessions and Transactions are building blocks used to create a higher level layer (the "hibernate.4dummies" layer? Count me as dummy #1 :) In a sense, TransactionalSession isn't that different from the deprecated Session API, with its commit and cancel methods. However, I suspect that one of the objections to integrating a more generalized transaction handling mechanism into the Session interface, has been the complexity it would add to the implementation(s) of Session. TransactionalSession ought to be a fairly simple combination of the session and transaction objects, which would be able to make more specific assumptions about some things, and avoid complicating the Session implementation with extra conditional logic. I haven't addressed the question of how the factory for TransactionalSessions would work, but that's pretty much orthogonal to the API used for actual transactions. Your suggestion sounds fine to me: > Properties props = new Properties(); > props.setProperty("hibernate.transaction_strategy", "JTA"); > TransactionFactory txFactory = Hibernate.createTransactionFactory(props); The question would arise of whether a beginTransaction() method (or createTransaction() or whatever) should be implemented on the SessionFactory interface, for maximum integration of TransactionalSessions; or whether a TransactionalSessionFactory is needed. I think this is a separate issue from the TransactionFactory involved with the choice of transaction strategy. Here are my answers to the questions you raised: > (1) I had kind of hoped to roll the stuff thats done by ConnectionProvider > into the TransactionStrategy (ie opening, closing a JDBC > connection) but it now seems to make more sense to orthogonalize > these things. Does everyone else concur with that? It sounds right to me. The hand-waving summary of my imagined design involves discrete, single-purpose components under the hood, being combined at a high level in interfaces like Session and TransactionalSession. > (2a) For swappable Transaction strategies to work, JTATransaction would > need to look up the surrent transaction in JNDI. Is this always okay? The > usual way to get the current transaction in an EJB is from the EJBContext. > (2b) What about in a CMT bean? I guess you would just need to revert to > > Transaction tx = new ContainerTransaction(session, ejbContext); My thought was that if Hibernate provides a few basic transaction strategy implementations initially, they should be fairly small, simple and take the most obvious approach, as you suggest. Users with different requirements can tweak them, improve them, write their own, or just complain. The docs could ask for feedback about this. > (3) Is the call to tx.begin() really necessary in approach (1)?? > Surely the work could be done in the constructor.... As you can tell from my TransactionalSession wrapper, I agree with this. Sorry to be dragging this out. We've definitely made great progress - I'm just being very demanding. In my defense, I think the more effort that goes into coming up with good designs at this level, the more widely useful and long-lived the result is likely to be. But if I'm taking things too far or sucking up too much bandwidth at this 1.0-is-imminent stage, feel free to apply brakes. Anton |
From: Gavin_King/Cirrus%<CI...@ci...> - 2002-05-21 06:52:46
|
> I think that separating the Transaction and the Session makes perfect > sense at a certain level, but I'm personally interested in something > even more high-level (you can call it "brain-dead", if you like). I > have two specific issues: > > 1. I'm not completely comfortable with requiring that every > transaction include the sequence: > > s.flush(); > tx.commit(); If it helps, I'm perfectly happy if Transaction.commit() calls Session.flush(). I don't see anything wrong with that at all. If it saves a line of code, its all good. ( find() methods also call flush() transparently ) > With your clean separation of Transaction & Session, though, I don't > think that hybrid flush/commit method belongs in either of those > classes, so this is where the wrapper described below would come in. No, it doesn't and thats why the old commit()/cancel() methods were problematic. There is not necessarily a 1-1 relationship between transactions and sessions, so modelling them as a single object just doesn't fit (to me anyway). > 2. More generally, I think there's a case for combining the > Session and Transaction functionality on a single interface, just to > keep the final API as simple as possible. I think we may disagree > on this, but I'd like to know whether you see the problem with a > combined interface as having mainly to do with backwards > compatibility, or whether you see other disadvantages to it. Yeah, we disagree. I see your POV and I know why it would be nice and, in fact, I originally designed things that way ( with Session.commit() and Session.cancel() ). However, while its a more convenient API for most applications, I don't feel its a correct model of the underlying concepts. I think it will be a hassle later on. > 1. Since it's only intended for use in cases where an overall > transaction is required, the transaction can be begun upon creation, > so there's no need for a separate "begin" method. Quite happy with that. But as I pointed out begin() isnt really needed with seperate transaction objects either. You can do that stuff in the constructor. Of course, instantiating the Transaction is an extra operation thats not needed in your design. > 2. Since it incorporates Session functionality, there's no need for > the developer to manage two objects, a session and a transaction. As I see it, this is the main advantage to your model. maintaining pointers to both objects _does_ make application code messier. > 3. It's not necessary to call both flush() and commit() on a > TransactionalSession. As above, I'm happy if Transactions flush the Session. > One possible downside is that this can be seen as introducing a > kind of dual API, which would have to be carefully dealt with in > documentation. I am soooo not keen on this. The main reasons we wanted to do all this in the first place were: 1. Abstract application code from the actual transaction mechanism ( and away from JDBC.setAutocommit() ). 2. Be easier to learn. It seems to me that (1) is fully satisfied by the approach of having seperate transaction objects. (2) is somewhat satisfied though I will give you that your proposed API is simpler yet. What I am quite certain of is this: documenting 2 different APIs and giving new users a choice will unsatisfy (2). Its like "well, we cant make up our mind on the right way to model it so we will add a whole bunch of extra unnecessary complexity to allow _both_ models". :) > The general way I'm looking at it, though, is that Sessions and > Transactions are building blocks used to create a higher level > layer (the "hibernate.4dummies" layer? Count me as dummy #1 :) Heh, in my comment about dummies I certainly meant no disrespect to sensible people who prefer simpler APIs over complicated ones :-) > In a sense, TransactionalSession isn't that different from the > deprecated Session API, with its commit and cancel methods. Exactly. And people found the old API confusing. > > Properties props = new Properties(); > > props.setProperty("hibernate.transaction_strategy", "JTA"); > > TransactionFactory txFactory = Hibernate.createTransactionFactory(props); > The question would arise of whether a beginTransaction() method (or > createTransaction() or whatever) I get the impression we will all agree on beginTransaction() :) > > (1) I had kind of hoped to roll the stuff thats done by ConnectionProvider > > into the TransactionStrategy (ie opening, closing a JDBC > > connection) but it now seems to make more sense to orthogonalize > > these things. Does everyone else concur with that? > It sounds right to me. The hand-waving summary of my imagined > design involves discrete, single-purpose components under the hood, > being combined at a high level in interfaces like Session and > TransactionalSession. quite. > > (2a) For swappable Transaction strategies to work, JTATransaction would > > need to look up the surrent transaction in JNDI. Is this always okay? The > > usual way to get the current transaction in an EJB is from the EJBContext. > > (2b) What about in a CMT bean? I guess you would just need to revert to > > > > Transaction tx = new ContainerTransaction(session, ejbContext); > My thought was that if Hibernate provides a few basic transaction > strategy implementations initially, they should be fairly small, > simple and take the most obvious approach, as you suggest. Users with > different requirements can tweak them, improve them, write their own, > or just complain. The docs could ask for feedback about this. quite. > Sorry to be dragging this out. We've definitely made great progress > - I'm just being very demanding. In my defense, I think the more > effort that goes into coming up with good designs at this level, the > more widely useful and long-lived the result is likely to be. But if > I'm taking things too far or sucking up too much bandwidth at this > 1.0-is-imminent stage, feel free to apply brakes. Your suggestions are _very_ welcome Anton. And I strongly believe that ideas should always be put forward as strongly-as-possible to get the most out of them. Clearly we have already agreed on a bunch of stuff that makes us all happier than before. I'm not yet 100% sure if TransactionalSession should be left as an exercise for the user or should eventually be integrated into the core API. I am 100% sure I don't want to make the decision now, cos I might regret it. Unless other people start shouting at me, I'm ready to either implement, or let Anton implement: package cirrus.hibernate; interface TransactionFactory { public static TransactionFactory buildTransactionFactory(Properties props); public static Transaction beginTransaction(Session session); public Transaction beginTransaction(Session session); } interface Transaction { public void commit() throws HibernateException; //flushes session public void rollback() throws HibernateException; } package cirrus.hibernate.transaction; public class JTATransaction implements Transaction public class JDBCTransaction implements Transaction public class CMTTransaction implements Transaction Then our idiom becomes: Session s = sf.openSession(); Transaction t = tf.beginTransaction(s); try { Foo foo = s.load(fooid); foo.setName("FOO"); t.commit(); } catch (Exception e) { t.rollback(); } finally { s.close(); } Thats pretty nice, surely? |
From: Anton v. S. <an...@ap...> - 2002-05-22 05:35:53
|
> I'm not yet 100% sure if > TransactionalSession should be left as an exercise for the user or > should eventually be integrated into the core API. I am 100% sure I > don't want to make the decision now, cos I might regret it. Makes sense. >... > > Session s = sf.openSession(); > Transaction t = tf.beginTransaction(s); > try { > Foo foo = s.load(fooid); > foo.setName("FOO"); > t.commit(); > } > catch (Exception e) { > t.rollback(); > } > finally { > s.close(); > } > > Thats pretty nice, surely? Yes, it's excellent, thank you. (Don't think I didn't notice that beginTransaction() wasn't added to Session, though! Sneaky but smart... :) > Unless other people start shouting at me, I'm ready to either > implement, or let Anton implement I'd be happy to work on this, to make up for having introduced this last-minute reshuffle. Let me take a look at it on Wednesday and/or Thursday, and I'll report back once I've done that. I may respond to other parts of your message tomorrow - I was out all day today. Anton |
From: Anton v. S. <an...@ap...> - 2002-05-24 06:05:48
|
Gavin, You suggested: > interface TransactionFactory { > public static TransactionFactory buildTransactionFactory(Properties props); > public static Transaction beginTransaction(Session session); > public Transaction beginTransaction(Session session); > } I assume buildTransactionFactory() needs to be defined somewhere other than on the interface it's a factory method for. Did you have somewhere in mind? It could be added to Datastore, but I don't know if that's an acceptable change to an existing interface. Or, TransactionFactory could be made into an publicly exposed class, but that seems a little messy to me. Anton |
From: Anton v. S. <an...@ap...> - 2002-05-25 21:46:48
|
I ran into a problem where a NullPointerException was being thrown from deep inside Hibernate (latest version), with no real clue as to what the problem was: java.lang.NullPointerException at java.lang.reflect.Method.invoke(Native Method) at cirrus.hibernate.impl.ClassPersister.setPropertyValues(ClassPersister.java:4 49) at cirrus.hibernate.impl.ClassPersister.hydrate(ClassPersister.java:680) at cirrus.hibernate.impl.RelationalDatabaseSession.loadFromResultSet(Relational DatabaseSession.java:1133) ... It took me a while to figure out where to look. It turned out that there was a null value in my database, in a column with a setter method that expects a Java double - so really, the problem was either with my data or with the setter method, depending on how you look at it. To make the error message more helpful in future, I've added an exception case to ClassPersister.setPropertyValues: catch (NullPointerException npe) { throw new HibernateException( "NullPointerException occurred inside setter for class " + className + ", property " + propertyNames[j] ); } I've checked the change into CVS - if there's a better way to handle this, please let me know. Anton |
From: Brad C. <bra...@wo...> - 2002-05-21 07:17:53
|
forgive me if this a brain dead question - i am just skimming all this because i currently don't have the time right now to do it justice - what is the general structure of code that would now be used in a CMT environment? i was just concerned that a commit is going to replace the flush, which doesn't make sense (in this environment). brad > -----Original Message----- > From: Gavin_King/Cirrus%CI...@ci... > [mailto:Gavin_King/Cirrus%CI...@ci...] > Sent: Tuesday, 21 May 2002 4:39 PM > To: hib...@li... > Subject: RE: [Hibernate-devel] Simple transaction management >=20 >=20 >=20 >=20 > > I think that separating the Transaction and the Session=20 > makes perfect > > sense at a certain level, but I'm personally interested in something > > even more high-level (you can call it "brain-dead", if you like). I > > have two specific issues: > > > > 1. I'm not completely comfortable with requiring that every > > transaction include the sequence: > > > > s.flush(); > > tx.commit(); >=20 > If it helps, I'm perfectly happy if Transaction.commit() calls > Session.flush(). I don't see anything wrong with that at all. If > it saves a line of code, its all good. ( find() methods also call > flush() transparently ) >=20 > > With your clean separation of Transaction & Session, though, I don't > > think that hybrid flush/commit method belongs in either of those > > classes, so this is where the wrapper described below would come in. >=20 > No, it doesn't and thats why the old commit()/cancel() methods were > problematic. There is not necessarily a 1-1 relationship between > transactions and sessions, so modelling them as a single object > just doesn't fit (to me anyway). >=20 > > 2. More generally, I think there's a case for combining the > > Session and Transaction functionality on a single interface, just to > > keep the final API as simple as possible. I think we may disagree > > on this, but I'd like to know whether you see the problem with a > > combined interface as having mainly to do with backwards > > compatibility, or whether you see other disadvantages to it. >=20 > Yeah, we disagree. I see your POV and I know why it would be nice > and, in fact, I originally designed things that way ( with > Session.commit() and Session.cancel() ). However, while its a more > convenient API for most applications, I don't feel its a correct model > of the underlying concepts. I think it will be a hassle later on. >=20 > > 1. Since it's only intended for use in cases where an overall > > transaction is required, the transaction can be begun upon creation, > > so there's no need for a separate "begin" method. >=20 > Quite happy with that. But as I pointed out begin() isnt really > needed with seperate transaction objects either. You can do that > stuff in the constructor. Of course, instantiating the Transaction > is an extra operation thats not needed in your design. >=20 > > 2. Since it incorporates Session functionality, there's no need for > > the developer to manage two objects, a session and a transaction. >=20 > As I see it, this is the main advantage to your model. maintaining > pointers to both objects _does_ make application code messier. >=20 > > 3. It's not necessary to call both flush() and commit() on a > > TransactionalSession. >=20 > As above, I'm happy if Transactions flush the Session. >=20 >=20 > > One possible downside is that this can be seen as introducing a > > kind of dual API, which would have to be carefully dealt with in > > documentation. >=20 > I am soooo not keen on this. The main reasons we wanted to do all > this in the first place were: >=20 > 1. Abstract application code from the actual transaction mechanism > ( and away from JDBC.setAutocommit() ). > 2. Be easier to learn. >=20 > It seems to me that (1) is fully satisfied by the approach of having > seperate transaction objects. (2) is somewhat satisfied though I > will give you that your proposed API is simpler yet. >=20 > What I am quite certain of is this: documenting 2 different APIs and > giving new users a choice will unsatisfy (2). Its like "well, we cant > make up our mind on the right way to model it so we will add a whole > bunch of extra unnecessary complexity to allow _both_ models". >=20 > :) >=20 > > The general way I'm looking at it, though, is that Sessions and > > Transactions are building blocks used to create a higher level > > layer (the "hibernate.4dummies" layer? Count me as dummy #1 :) >=20 > Heh, in my comment about dummies I certainly meant no disrespect > to sensible people who prefer simpler APIs over complicated ones > :-) >=20 > > In a sense, TransactionalSession isn't that different from the > > deprecated Session API, with its commit and cancel methods. >=20 > Exactly. And people found the old API confusing. >=20 > > > Properties props =3D new Properties(); > > > props.setProperty("hibernate.transaction_strategy", "JTA"); > > > TransactionFactory txFactory =3D > Hibernate.createTransactionFactory(props); >=20 > > The question would arise of whether a beginTransaction() method (or > > createTransaction() or whatever) >=20 > I get the impression we will all agree on beginTransaction() :) >=20 >=20 > > > (1) I had kind of hoped to roll the stuff thats done by > ConnectionProvider > > > into the TransactionStrategy (ie opening, closing a JDBC > > > connection) but it now seems to make more sense to orthogonalize > > > these things. Does everyone else concur with that? >=20 > > It sounds right to me. The hand-waving summary of my imagined > > design involves discrete, single-purpose components under the hood, > > being combined at a high level in interfaces like Session and > > TransactionalSession. >=20 > quite. >=20 > > > (2a) For swappable Transaction strategies to work,=20 > JTATransaction would > > > need to look up the surrent transaction in JNDI. Is this=20 > always okay? > The > > > usual way to get the current transaction in an EJB is from the > EJBContext. > > > (2b) What about in a CMT bean? I guess you would just=20 > need to revert to > > > > > > Transaction tx =3D new ContainerTransaction(session, ejbContext); >=20 > > My thought was that if Hibernate provides a few basic transaction > > strategy implementations initially, they should be fairly small, > > simple and take the most obvious approach, as you suggest. =20 > Users with > > different requirements can tweak them, improve them, write=20 > their own, > > or just complain. The docs could ask for feedback about this. >=20 > quite. >=20 > > Sorry to be dragging this out. We've definitely made great progress > > - I'm just being very demanding. In my defense, I think the more > > effort that goes into coming up with good designs at this level, the > > more widely useful and long-lived the result is likely to=20 > be. But if > > I'm taking things too far or sucking up too much bandwidth at this > > 1.0-is-imminent stage, feel free to apply brakes. >=20 > Your suggestions are _very_ welcome Anton. And I strongly believe that > ideas should always be put forward as strongly-as-possible to get the > most out of them. Clearly we have already agreed on a bunch of stuff > that makes us all happier than before. I'm not yet 100% sure if > TransactionalSession should be left as an exercise for the user or > should eventually be integrated into the core API. I am 100% sure I > don't want to make the decision now, cos I might regret it. >=20 > Unless other people start shouting at me, I'm ready to either > implement, or let Anton implement: >=20 >=20 > package cirrus.hibernate; >=20 > interface TransactionFactory { > public static TransactionFactory buildTransactionFactory(Properties > props); > public static Transaction beginTransaction(Session session); > public Transaction beginTransaction(Session session); > } >=20 > interface Transaction { > public void commit() throws HibernateException; //flushes session > public void rollback() throws HibernateException; > } >=20 > package cirrus.hibernate.transaction; >=20 > public class JTATransaction implements Transaction > public class JDBCTransaction implements Transaction > public class CMTTransaction implements Transaction >=20 > Then our idiom becomes: >=20 > Session s =3D sf.openSession(); > Transaction t =3D tf.beginTransaction(s); > try { > Foo foo =3D s.load(fooid); > foo.setName("FOO"); > t.commit(); > } > catch (Exception e) { > t.rollback(); > } > finally { > s.close(); > } >=20 > Thats pretty nice, surely? >=20 >=20 > _______________________________________________________________ >=20 > Don't miss the 2002 Sprint PCS Application Developer's Conference > August 25-28 in Las Vegas -- http://devcon.sprintpcs.com/adp/index.cfm >=20 > _______________________________________________ > Hibernate-devel mailing list > Hib...@li... > https://lists.sourceforge.net/lists/listinfo/hibernate-devel >=20 |
From: Gavin_King/Cirrus%<CI...@ci...> - 2002-05-21 07:31:19
|
> forgive me if this a brain dead question - i am just skimming all this > because i currently don't have the time right now to do it justice - > what is the general structure of code that would now be used in a CMT > environment? i was just concerned that a commit is going to replace the > flush, which doesn't make sense (in this environment). For a CMTTransaction or JTATransaction Transaction.commit() would call Session.flush(). For a JDBCTransaction Transaction.commit() would call Session.flush() then Session.connection().commit(). The call to flush() is just an extra convenience. Essentially Transaction.commit() has empty implementations for container managed transactions and a non-empty implementation for JDBC transactions.... Brad, I don't see any other sensible approach to this. If we want to abstract the underlying transaction mechanism, as agrued by Anton, there has to be a method which indicates conclusion of the unit of work (because JDBCTransaction needs to call commit() from there). There also needs to be a method which forces rollback ( whether it calls rollback() or setRollbackOnly() ). If you really object to the first method being called commit(), what else should we call it? I guess if you really despise this, you can continue using the current approach. I am not proposing ANY change at all to existing code. Otherwise suggest a better alternative.... Gavin |
From: Brad C. <bra...@wo...> - 2002-05-21 23:16:19
|
don't get me wrong, i don't despise this change at all. :-) i just remember when i was first using hibernate, i got it going in a stanalone application. i then tried using it in a CMT environment, which was a little frustrating because back then u still had to call commit, which certainly isn't intuitive. however, all is ok, as the flush method is staying on session. we simply need a code example in the docs showing the general code structure in different environments (which i don't mind helping out on). brad > -----Original Message----- > From: Gavin_King/Cirrus%CI...@ci... > [mailto:Gavin_King/Cirrus%CI...@ci...] > Sent: Tuesday, 21 May 2002 5:17 PM > To: hib...@li... > Subject: RE: [Hibernate-devel] Simple transaction management >=20 >=20 >=20 > > forgive me if this a brain dead question - i am just=20 > skimming all this > > because i currently don't have the time right now to do it justice - > > what is the general structure of code that would now be=20 > used in a CMT > > environment? i was just concerned that a commit is going=20 > to replace the > > flush, which doesn't make sense (in this environment). >=20 >=20 > For a CMTTransaction or JTATransaction Transaction.commit() would call > Session.flush(). > For a JDBCTransaction Transaction.commit() would call=20 > Session.flush() then > Session.connection().commit(). >=20 > The call to flush() is just an extra convenience. Essentially > Transaction.commit() has empty implementations for container managed > transactions and a non-empty implementation for JDBC transactions.... >=20 > Brad, I don't see any other sensible approach to this. If we want to > abstract the underlying transaction mechanism, as agrued by=20 > Anton, there > has to be a method which indicates conclusion of the unit of=20 > work (because > JDBCTransaction needs to call commit() from there). There=20 > also needs to be > a method which forces rollback ( whether it calls rollback() or > setRollbackOnly() ). If you really object to the first method=20 > being called > commit(), what else should we call it? >=20 > I guess if you really despise this, you can continue using the current > approach. I am not proposing ANY change at all to existing code. >=20 > Otherwise suggest a better alternative.... >=20 > Gavin >=20 >=20 > _______________________________________________________________ >=20 > Don't miss the 2002 Sprint PCS Application Developer's Conference > August 25-28 in Las Vegas -- http://devcon.sprintpcs.com/adp/index.cfm >=20 > _______________________________________________ > Hibernate-devel mailing list > Hib...@li... > https://lists.sourceforge.net/lists/listinfo/hibernate-devel >=20 |
From: Anton v. S. <an...@ap...> - 2002-05-17 21:52:07
|
> hhmmmmmmm .... damn this is all coming back to haunt ..... Sorry to rehash - I hadn't seen the previous discussion. I'll check out the archives to catch up, although I haven't yet done so in the response below. > Lets state the problem carefully: > > What API allows the user to END a Session and > > (1) is easy to use and understand for new users, > (2) has methods with clear semantics, > (3) supports the various different possible styles of connection + > transaction management eg. application server datasource + JTA, > DriverManager + Connection.commit(), long transactions, > (4) comforms to the philosophy that Hibernate is a thin wrapper > around JDBC that exposes all underlying JDBC functionality to > the application. (A wrapper with "windows"?) I agree with all of that. I'm proposing two additional requirements, one of which I stated explicitly and one of which I didn't (but should have). The one I stated explicitly was: > application code could switch the underlying transaction > handling mechanism without changing the client code Aside from the convenience of this, I think that the current design with session.connection().xxx encourages users to depend on direct access to the Connection, and doesn't provide an alternative. In a sense, this leads users down the wrong path - Hibernate abstracts all sorts of things but then just exposes the connection and encourages its use. I'm all for the idea of "a wrapper with windows", but I'm also suggesting that it shouldn't be necessary to use those windows. It should be an option for those who want it. My second requirement comes from never having been a fan of the JDBC transaction API, and its reliance on toggling autoCommit to begin and end transactions. This is unintuitive, doesn't "say what it means", and can have bad consequences in the case of mistakes. For example, forgetting to setAutoCommit(true) at the end of a transaction results in all subsequent requests on that connection being lumped into a single transaction, with potentially horrible effects on concurrency. I'm suggesting that a very thin wrapper here would be beneficial in general, and I think it's appropriate for Hibernate to offer this as an optional alternative, to encourage sensible transaction usage that reads well in the application code, guards against certain kinds of errors, and corrects the egregious mistakes that Sun made. ;) If the above two additional requirements are met, I'm not as concerned about the exact form this API takes. OTOH, if everyone thinks I've been smoking the $2 crack, I'll stick to using my own wrapper to get the functionality I want. What prompted me to start this thread was the questions that have arisen about what Sessions are, and the attempt to clarify them in documentation. I think part of the problem is that the relationship between a Session and a transaction isn't as clear as it might be, and this is at least partly because the separation between the two is currently implemented by saying "here's the connection, you're on your own with transactions". I imagine this is likely to encourage the tendency of users to think of a session as being like a connection, which you can use repeatedly for multiple transactions. I think that making it unnecessary to directly access the connection would be of general benefit to Hibernate users, and as a side benefit, may be less likely to result in confusion, although obviously that's just a guess. It would still be necessary to make it clear, for example, that multiple transactions shouldn't be executed on a single session. I suppose commitTransaction() and rollbackTransaction() could also simply close the connection - more on this below. > When we had this discussion earlier, Brad Clow commented that one > thing that was so confusing was that we were overloading the word > "commit" to mean something other than committing a transaction. > I agreed at the time, and still agree. If we do something like > you are suggesting, we should not use the words "commit", "begin" > "rollback" or "transaction" anywhere on the Session interface. That was why I suggested method names like "commitTransaction". My intent was to clearly delineate between operations on the Hibernate Session, and the conceptual/database transaction. I was also trying to stick more closely to the current API's approach, and basically suggesting that session.connection().commit() could simply be (optionally) replaced by session.commitTransaction(), which would buy some additional abstraction and thus flexibility. My suggestion actually doesn't change the current API all that much, and of course the existing API could still be used as is, i.e. it's a backwards-compatible addition. > public interface Session { > ..... > public void end() throws ....; //commit, close > public void cancel() throws ....; //rollback, close > public void pause() throws ....; //commit, disconnect > public void resume() throws .....; //reconnect > } This would satisfy my concerns above in terms of its functionality. I have two further observations: 1. The begin/commit/rollback idiom is pretty well known and understood. Essentially making up new words for these doesn't necessarily help new users. 2. I do see some benefit to separating the transaction functionality from the session. For example (and perhaps I just need more info here): what about the case where a session will be "read-only", so that an explicit overall transaction isn't needed, i.e. equivalent to JDBC setAutoCommit(true)? If an overall transaction is implicitly created by Hibernate, it could reduce concurrency unnecessarily. This was one reason why I thought it might be a good idea to separate out the transaction calls from the session open/close. A readonly session could simply omit the transaction calls, and perhaps include an initial call to suspendFlushes(), for example. I think this might be simpler than adding features to the above API to support this case. > I'm also not keen on changing the cirrus.hibernate.Session interface again > at this stage. It might be preferable to develop this API (ie. the new > Session and SessionFactory interfaces) in a seperate package. Say, > cirrus.hibernate.4dummies or something. ;-> The new functionality could be > implemented either on the existing cirrus.hibernate.impl classes or as > wrapper classes that delegate to cirrus.hibernate.SessionFactory and > cirrus.hibernate.Session. I'm sensitive to this concern. Again, that was a reason I proposed adding xxxTransaction() to Session, since it wouldn't break existing code, and it could be an optional mechanism. If the other approach is decided on, making a Session the same as a transaction, perhaps an alternative to Session would be needed. I'm being very outspoken for a brand new Hibernate user working on his first application with it. All Gavin's fault for encouraging me. :) If I'm missing something or am wrong about this, please apply cluestick to my noggin with appropriate force. But even if my conclusions aren't palatable, I think at least the concern about wrapping connections has validity, and I'd be interested in figuring out (and working on) a sufficiently general way to address that. Anton |
From: Phillip B. <phi...@cl...> - 2002-05-19 02:48:10
|
I'd just like to say I agree with what Anton has been saying about simplifying transaction management. The relationship between Sessions and transactions isn't as clear as it could be. Although I haven't done much with Hibernate the first thing I did do was put a transaction wrapper around Session so I could get back to thinking about transactions rather than sessions. This sounds a lot like what Anton did. If more than one of us is inventing this wheel, then either we're both doing things wrong or maybe a wheel needs to be built. My NZD$0.02. /PhillipB |