Most J2EE architecture is built like this (or should be!):
- Actions (Struts)
- Services
- DAOs (Data access objects)
public List getAllActiveLegacyFeatures() throws Exception {
List features = fpsDao.grabAllActiveLegacyFeatures();
return features;
}
Moving on. So you really only have 2 choices. Wrap the transactions at the DAO layer or the Service layer. Lets begin by showing you how to "Wrap" a transaction. Here is some code that does it:
<bean id="ImportKeyDataServiceImplementation" class="cat.dds.fpsdma.services.ImportKeyDataService">
<constructor-arg index="0" ref="fpsDAO" />
</bean>
<bean id="ImportKeyDataService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="theTransactionManager" />
</property>
<property name="target">
<ref local="ImportKeyDataServiceImplementation" />
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
The first "bean" here is the actual object. Notice that on line 2 it has a DAO. Now this object should never be used, so we actually call it an "...Implementation". Notice that the second bean is named "ImportKeyDataService" It is not an implementation, but the object that we should use. (This is just how we did it on FPS. We just used this naming convention so we new that we should never use the "Implementation" objects). Notice that the Implementation object is referenced on line 9. This ties all this together. So, when you actually use the object "ImportKeyDataService" you will get a Wrapped transaction.
What does this give you? Lots. When any DAO call is made in the ImportKeyDataService object above, Hibernate will not commit anything. Batch updates and such automatically roll back correctly if there is an error thrown out of the ImportKeyDataService object. This would not happen if you wrapped the DAO. Every time you go in and back out of an object (the object looses scope) you commit. So, if you have a DAO doing something, it would commit every time you made a call.
Now there are two times when you want the DAO to be wrapped.
- If you are doing DB2 calls to CDID or some alien database, and you are just doing "selects", it is best to commit after every transaction. For some reason Mainframers like that.
- JUnits usually require commits to work properly. We make a junit for EVERY database call. They just don't work if you don't commit after each one. However, in this case, Spring makes this easy.
Two additional things that can also be useful in your examples:
ReplyDelete1) You can use an abstract bean definition to specify a "template" that you can use for your services that you want to wrap with a transaction. Particularly, if your services use consistent naming patterns (save*, delete*, get*) you can you can reuse that template across all your services that use the same naming pattern. So, instead of defining the same transaction manager attributes over and over again on every one of your services you can just define an abstract template and then define that template as a parent for each bean definition that you want to wrap with a transaction manager. Makes for more concise bean definitions if you have a lot of services that need to use a transaction manager...
2) You could define your "Implementation" class as an inner bean of the 'target' property for the TransactionFactoryProxyBean instead of a standalone bean. If a bean is defined as an inner bean you can't directly reference it because it doesn't have a bean ID. This can help avoid confusion over which bean you're supposed to interact with because now the only bean that's actually available is the one that you've wrapped with the transaction manager. This is a useful way to make sure that you're only exposing the services that you want to as Spring beans instead of having to remember by the naming pattern...
I don't know of anyway to get the comment post form to accept nicely-formatted code :(, so I couldn't easily post an example of each of these in this comment. Perhaps I might post them as a new blog entry...
Awesome advice. I would love to see some code. Spring has so many nuances. Please make a new post and give us some good examples.
ReplyDelete