简体   繁体   English

@Transactional在控制器方法上不起作用

[英]@Transactional on controller method not working

In my Spring MVC application, I have a method in a controller that needs to save a bunch of objects (built from an uploaded file) to a database. 在我的Spring MVC应用程序中,我在控制器中有一个方法,该方法需要将一堆对象(从上载的文件构建)保存到数据库中。 Let us leave aside for the moment the whole question about whether transactions should be done in the controller or service layer -- the point is that it should technically be feasible to do it in the controller, but I am finding problems. 现在让我们暂时忽略有关是否应该在控制器层或服务层中完成事务的整个问题-关键是在技术上在控制器中进行事务应该可行,但我发现了问题。 If you look at the code below, what I am expecting is that if any of the three calls to saveContact fails with an Exception (any exception, since I put rollbackFor = Exception.class ), then all three should be rolled back. 如果您看下面的代码,我期望的是,如果对saveContact的三个调用中的任何一个失败并带有一个异常(任何异常,因为我放了rollbackFor = Exception.class),那么这三个都应该回滚。 Still, what I see is that if for example the third one fails, the data from the first two is still present in the database. 不过,我看到的是,例如,如果第三个失败,则前两个数据仍然存在于数据库中。 The exception thrown is a PersistenceException, so I believe this should trigger the rollback, but it doesn't (it bubbles up to the client's browser, which is what I expected since I'm not catching it). 引发的异常是PersistenceException,因此我认为这应该触发回滚,但不会触发(回滚到客户端的浏览器,这是我期望的,因为我没有捕获它)。

Here's my controller code: 这是我的控制器代码:

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*/
    }
}

Here's the interface for it: 这是它的接口:

@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);
}

My web-context.xml contains: 我的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" />

And my dao-context.xml contains: 我的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>

Here's the exception that I thought would trigger the rollback: 我认为这会触发回滚,这是一个例外:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Column 'name' cannot be null          

It's not clear to me whether the @Transactional annotation is being picked up at all - from my understanding it should, since I'm scanning the ch.oligofunds.oligoworld.web package and the controller interface is annotated with @Controller. 对我来说,目前尚不清楚是否@Transactional批注已被拾取-从我的理解来看,应该如此,因为我正在扫描ch.oligofunds.oligoworld.web软件包,并且控制器接口已使用@Controller进行批注。 But my understanding may be wrong. 但是我的理解可能是错误的。 :) :)

Any hints? 有什么提示吗? Thanks 谢谢

With proxy-target-class="true" you're telling spring to use cglib to handle the proxying but you've specified scoped-proxy="interfaces". 使用proxy-target-class =“ true”告诉Spring使用cglib来处理代理,但是您指定了scoped-proxy =“ interfaces”。

See https://stackoverflow.com/a/15568457/117839 参见https://stackoverflow.com/a/15568457/117839

@Transactional on that method doesn't have any added value as it is an internal method call (and your class is already transactional). 该方法上的@Transactional没有任何附加值,因为它是内部方法调用(并且您的类已经是事务性的)。 Spring uses proxies and only calls into the object pass thorough the proxy. Spring使用代理,只有对对象的调用才能通过代理传递。

Also your code is flawed you shouldn't catch and swallow exceptions as that interferes with the tx support (it relies on transactions to determine to rollback or not, currently there is never an exception hence always tries to commit). 同样,您的代码也存在缺陷,您不应捕获和吞下异常,因为这会干扰对tx的支持(它依赖事务来确定是否回滚,目前还没有异常,因此总是尝试提交)。

Finally you are using MySQL make sure that you are using table types that actually supports transactions (MyISAM tables don't have tx support). 最后,您使用MySQL来确保您使用的表类型实际上支持事务(MyISAM表不支持tx)。

I would however strongly suggest to move the transactional part (or the business logic which you are now doing in your controller) to a service. 但是,我强烈建议将事务部分(或您现在在控制器中执行的业务逻辑)移至服务。 The controller (or web layer in general) should only be a thin layer converting the incoming request into something useable for the service layer and the result into something useable for the web to display. 控制器(通常是Web层)应该只是一个薄层,它将传入的请求转换为可用于服务层的内容,并将结果转换为可用于Web显示的内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 `@Transactional` 不适用于 Spring 2 controller - `@Transactional` not working for Spring 2 controller @Transactional 不能从预定的方法工作 - @Transactional not working from scheduled method Spring @Transactional persist 方法不起作用 - Spring @Transactional persist method not working 测试和控制器中标记为事务的方法的不同行为 - different behavior of method marked transactional in test and in controller 使 Spring 3 MVC 控制器方法成为事务性的 - Making Spring 3 MVC controller method Transactional EntityManager 持久化和合并在@Transactional 方法中不起作用 - EntityManager persist and merge not working in @Transactional method 将 @Transactional 方法结果从 Service 传递到 Controller 层 Spring Boot - Pass @Transactional method result from Service to Controller Layer Spring Boot 控制器方法上的@Transactional 不会回滚事务,但服务方法上会 - @Transactional on Controller Method does not roll back transactions, but on Service Methods does 从Spring Boot测试调用的@Caching方法[注有@Transactional]无法正常工作 - @Caching method called from spring boot test [annotated with @Transactional] not working 延迟加载在Hibernate的非事务类/方法中工作 - Lazy loading working in non-transactional class/method in Hibernate
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM