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 |