Generally, I've oscillated between two different ways of doing transactions in Spring, the first is with declaratively using AOP and the second is programmatically throw the use of TransactionTemplate or to be really hardcore PlatformTransactionManager. I say hardcore because it's a bit more work to ensure that everything is commited or rolledback correctly.
I recently arrived at another way due to the requirements of testing the objects via interaction based testing. The AOP method makes it hard to Mock out calls as it wraps everything in a proxy and the programmatic approach seems impossible to test purely interactively (basically requiring a real TransactionTemplate). Especially as some things are classes and interfaces.
This idea combines the simple ideas of interfaces, dependency injection and either TransactionCallbackWithoutResult or TransactionCallback.
Create an interface, say "DoStuff" that has the required method to be in a transaction say, "doStuff(String stuff)". There are two implementations of this interface "DoStuffImpl" and "DoStuffInTransaction". The "DoStuffInTransaction" gets injected the "DoStuffImpl" and the "TransactionTemplate". It also implements "TransactionCallback". When it's "doStuff" method is called it sets the parameters to fields and then calls "transactionTemplate.execute(this)" and the "doInTransaction" methd calls the "DoStuffImpl.doStuff()" method.
By wiring up the "DoStuffImpl" inside "DoStuffInTransaction" your other classes can only access the transactional version. It makes interaction testing straightforward and doesn't require explicitly calling things like commit or rollback.