Can't get Spring transaction to rollback (java+mysql)
Last week I finally got around to making my app use transactions. I am using Spring declarative transactions, and I know that the transactions themselves are working, as it made something with thousands of inserts go from 5-10 minutes to 5-10 seconds inside one transaction. The database is MySQL and all the tables are InnoDB. So I wanted to get this function to rollback if it doesn't work. Basically, it is importing an xml file and building the database tables from the xml. So if something fails, I don't want a partial upload or it will be inconsistent. I have been exactly following the Spring documentation, and since I did get the transactions to run, I'm guessing the general configuration is right.
Here is the relevant part of the .xml file:
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="uploadModelToProject" rollback-for="Throwable"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
Here is the relevant part o开发者_StackOverflow中文版f the Service:
public interface ModelService {
// interface to upload an LSI XML file of the model into a project
public Model uploadModelToProject(Project proj, String xmlFile);
}
The uploadModelToProject calls a method in the SAX Parser implementation, which then implements the parsing
public Model importModelToProject(Project proj, String xmlInputFileName) throws SAXException, IOException, ParserConfigurationException, ImportException {
//get a factory
SAXParserFactory spf = SAXParserFactory.newInstance();
try {
//get a new instance of parser
SAXParser sp = spf.newSAXParser();
//parse the file and also register this object for callbacks
sp.parse(xmlInputFileName, this);
} finally {
newModel = null;
}
return newModel;
}
I have tried using rollback-for="Throwable", rollback-for="Exception", rollback-for="ImportException" (my own Exception). Then I have manually thrown an exception in the code and it hasn't the worked. I don't know if I need some propogation parameter or some other setting. It doesn't seem like I am missing anything obvious. Does anybody have any suggestions? All that happens is an exception is thrown, and everything still goes into the database. Am I missing an obvious step?
Thanks for any help.
I didn't get any nibbles on suggestions, but I finally figured this out so I figured I'd post the answer in case anybody else is trying to figure out the rollback stuff. I turned on the debug level for the commons/log4j logging so I could see what was happening in the Spring framework with everything. Copious output, but useful.
Turns out what I was missing was understanding where the Spring Framework was intercepting and catching the exceptions that are in the configuration, in order to initiate the rollback.
My first set of code, before I had the transaction stuff in, I was catching the exceptions from the XML parsing and just returning a null pointer for the caller to handle. I wasn't thinking about where the exception would be handled by Spring, so it didn't hit me that if I catch the exceptions and don't rethrow, then Spring doesn't know. My next attempt was passing on all the exceptions but not handling it anywhere, mistakenly thinking that Spring was magic. The good news was the transaction was rolled back, the bad news was that a bad error page was returned to the user.
So my "a-ha!" moment was when I realized that the method specified in the configuration (i.e. the one in the service) has to be the one who throws the exception. Yes, after realizing this, it seems obvious, but it took me going through this to figure that out. So in my example, the "uploadModelToProject" method needed to rethrow the exceptions after it got it, and the caller for that method in the service needed to handle the exceptoin instead., which would happen right after Spring intercepted it and executed the rollback.
Also, instead of having my lower level function in my XML Importer throw all those different types of exceptions, I created my own "ImportException", caught the exceptions, and threw the ImportException upwards.
Hope this helps someone!
精彩评论