TransactionTemplate and TransactionInterceptor are not at all tied to the DAO layer. They are rather exactly targetted at a service layer that sits on top of DAOs. Effectively, it doesn't matter at which level you apply them. You can nest them as deeply as you want, i.e. make your service layer transactional and let it delegate to a DAO layer that is again transactional. Spring-managed transactional code will automatically participate in existing transactions, you don't have to worry about that.
It's similar to EJB CMT in that the "propagation behavior" determines what will actually happen. This is a configuration setting: The demarcation code will look exactly the same at all levels, be it TransactionTemplate.execute blocks or proxy definitions for TransactionInterceptor.
- PROPAGATION_REQUIRED will participate in an existing transaction or create a new one for the nested scope else.
- PROPAGATION_MANDATORY will require an existing transaction, i.e. throw an exception else.
- PROPAGATION_SUPPORTS will participate in an existing transaction or execute non-transactionally else.
Generally, transactions are propagated via ThreadLocals underneath. This means that PROPAGATION_SUPPORTS does not do anything specific: Transaction-aware code will automatically participate in existing transactions or execute non-transactionally else in any case. PROPAGATION_REQUIRED is the most common behavior, therefore it is TransactionTemplate's default. Note that this will work with any transaction strategy, be it HibernateTransactionManager, JtaTransactionManager, or any other!
Both TransactionTemplate and TransactionInterceptor support all of Spring's transaction settings, as defined by the TransactionDefinition interface. With TransactionTemplate, you simply invoke setters like setPropagationBehavior and setIsolationLevel, with corresponding constants from the TransactionDefinition interface (which TransactionTemplate implements, BTW). With TransactionInterceptor and TransactionProxyFactoryBean, you specify them as transaction attribute Strings a la "PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ".
So the following would work accordingly: When loadStuff gets executed on its own, a new transaction will be created for its scope. When loadStuff gets invoked via doStuff, it will participate in the latter's existing transaction. If you switch loadStuff's propagation behavior to PROPAGATION_MANDATORY, it will still participate in an existing transaction but throw an exception when invoked on its own. Note that in many cases, it is not necessary to put transaction demarcation code in DAOs at all, leaving this completely to the service layer.
Code:
public class MyDataAccessObject {
...
public void loadStuff() {
TransactionTemplate transactionTemplate = new TransactionTemplate(this.transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
transactionTemplate.execute(
new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
...
// do some data access
...
}
}
}
}
public class MyBusinessService {
...
public void doStuff() {
TransactionTemplate transactionTemplate = new TransactionTemplate(this.transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
transactionTemplate.execute(
new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
...
myDataAccessObject.loadStuff();
...
}
}
}
}
FYI, I've slightly revised the article at
http://www.hibernate.org/110.html today to make a few things clearer. You might want to have another look at the examples there. They still don't discuss transaction nesting, though.
Juergen