[英]@Transactional on controller method not working
在我的Spring MVC應用程序中,我在控制器中有一個方法,該方法需要將一堆對象(從上載的文件構建)保存到數據庫中。 現在讓我們暫時忽略有關是否應該在控制器層或服務層中完成事務的整個問題-關鍵是在技術上在控制器中進行事務應該可行,但我發現了問題。 如果您看下面的代碼,我期望的是,如果對saveContact的三個調用中的任何一個失敗並帶有一個異常(任何異常,因為我放了rollbackFor = Exception.class),那么這三個都應該回滾。 不過,我看到的是,例如,如果第三個失敗,則前兩個數據仍然存在於數據庫中。 引發的異常是PersistenceException,因此我認為這應該觸發回滾,但不會觸發(回滾到客戶端的瀏覽器,這是我期望的,因為我沒有捕獲它)。
這是我的控制器代碼:
package ch.oligofunds.oligoworld.web;
/*imports here*/
/**
* Handles requests for the application file upload requests
*/
@Controller("ExcelUploaderImpl")
@Transactional
public class ExcelUploaderImpl implements ExcelUploader {
@Autowired
PersoninfoDAO personinfoDAO;
/**
* Upload files using Spring Controller
* @throws SecurityException
* @throws NoSuchMethodException
* @throws DataAccessException
*/
@Override
@RequestMapping(value = "/importFundNAV", method = RequestMethod.POST)
public @ResponseBody String handleFileUpload(HttpServletRequest request, @RequestParam CommonsMultipartFile[] fileUpload) throws DataAccessException, NoSuchMethodException, SecurityException {
/*here save the uploaded file and initialize the serverFile variable*/
try {
success = readExcelfile(serverFile);
} catch (IOException e) {
logger.error("Failed to read the excel file", e);
result += "Failed to read the excel file\n" + e.getStackTrace() + "\n";
} finally {
serverFile.delete();
}
if (success) {
result += "You successfully imported file " + aFile.getOriginalFilename() + "\n";
} else {
result += "Failed to import file " + aFile.getOriginalFilename() + "\n";
}
}
return result;
}
@Override
public boolean readExcelfile(File xlfile) throws IOException, DataAccessException, NoSuchMethodException, SecurityException {
FileInputStream fis = new FileInputStream(xlfile); // Finds the workbook
// instance for XLSX
// file
XSSFWorkbook myWorkBook = new XSSFWorkbook(fis); // Return first sheet
// from the XLSX
// workbook
boolean success;
success = readFundDefinition(myWorkBook);
myWorkBook.close();
return success;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean readFundDefinition(XSSFWorkbook myWorkBook) throws DataAccessException, NoSuchMethodException, SecurityException {
/*here do stuff to extract data from the excel to initialize the administrator, custodian, invContact and success variables*/
saveContact(administrator);
saveContact(custodian);
saveContact(invContact);
/*If any of the three invocations to saveContact fails, I want all three inserts to rollback*/
return success;
}
@Override
public void saveContact(Personinfo personinfo) throws DataAccessException, NoSuchMethodException, SecurityException {
/*a bunch of stuff before this line*/
personinfo.copy(personinfoDAO.store(personinfo)); // <--- this is where the transaction could fail
/*a bunch of stuff after this line*/
}
}
這是它的接口:
@Controller
public interface ExcelUploader {
public @ResponseBody String handleFileUpload(HttpServletRequest request, @RequestParam CommonsMultipartFile[] fileUpload) throws DataAccessException, NoSuchMethodException, SecurityException;
public boolean readExcelfile(File xlfile) throws IOException, DataAccessException, NoSuchMethodException, SecurityException;
public boolean readFundDefinition(XSSFWorkbook myWorkBook) throws DataAccessException, NoSuchMethodException, SecurityException;
public void saveContact(Personinfo personinfo, Personaddress personAddress, Personemail personEmail, Personphone personPhone) throws DataAccessException, NoSuchMethodException, SecurityException;
public void readNAV(XSSFWorkbook myWorkBook);
public boolean isEmpty(Object object, Method... methods);
}
我的web-context.xml包含:
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<context:component-scan base-package="ch.oligofunds.oligoworld.web" scoped-proxy="interfaces" />
我的dao-context.xml包含:
<context:component-scan base-package="ch.oligofunds.oligoworld.dao" scoped-proxy="interfaces" />
<context:component-scan base-package="ch.oligofunds.oligoworld.security" scoped-proxy="interfaces" />
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<context:property-placeholder location="classpath:CopyofoligoWorld-dao.properties" />
<!-- Using Atomikos Transaction Manager -->
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init"
destroy-method="close">
<property name="forceShutdown" value="true" />
<property name="startupTransactionService" value="true" />
<property name="transactionTimeout" value="60" />
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" />
<!-- Configure the Spring framework to use JTA transactions from Atomikos -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager" />
<property name="userTransaction" ref="atomikosUserTransaction" />
<property name="transactionSynchronizationName" value="SYNCHRONIZATION_ON_ACTUAL_TRANSACTION" />
</bean>
<bean name="mysqlDS,springSecurityDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" >
<property name="driverClassName" value="${mysql.connection.driver_class}" />
<property name="username" value="${mysql.connection.username}" />
<property name="password" value="${mysql.connection.password}" />
<property name="url" value="${mysql.connection.url}" />
<property name="maxIdle" value="${mysql.minPoolSize}" />
<property name="maxActive" value="${mysql.maxPoolSize}" />
</bean>
我認為這會觸發回滾,這是一個例外:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Column 'name' cannot be null
對我來說,目前尚不清楚是否@Transactional批注已被拾取-從我的理解來看,應該如此,因為我正在掃描ch.oligofunds.oligoworld.web軟件包,並且控制器接口已使用@Controller進行批注。 但是我的理解可能是錯誤的。 :)
有什么提示嗎? 謝謝
使用proxy-target-class =“ true”告訴Spring使用cglib來處理代理,但是您指定了scoped-proxy =“ interfaces”。
該方法上的@Transactional
沒有任何附加值,因為它是內部方法調用(並且您的類已經是事務性的)。 Spring使用代理,只有對對象的調用才能通過代理傳遞。
同樣,您的代碼也存在缺陷,您不應捕獲和吞下異常,因為這會干擾對tx的支持(它依賴事務來確定是否回滾,目前還沒有異常,因此總是嘗試提交)。
最后,您使用MySQL來確保您使用的表類型實際上支持事務(MyISAM表不支持tx)。
但是,我強烈建議將事務部分(或您現在在控制器中執行的業務邏輯)移至服務。 控制器(通常是Web層)應該只是一個薄層,它將傳入的請求轉換為可用於服務層的內容,並將結果轉換為可用於Web顯示的內容。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.