Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java @控制器上的事务方法不工作_Java_Spring_Hibernate_Transactions_Controller - Fatal编程技术网

Java @控制器上的事务方法不工作

Java @控制器上的事务方法不工作,java,spring,hibernate,transactions,controller,Java,Spring,Hibernate,Transactions,Controller,在我的SpringMVC应用程序中,我在控制器中有一个方法,需要将一组对象(从上传的文件构建)保存到数据库中。让我们暂时撇开关于事务是应该在控制器层还是服务层中完成的整个问题——问题是,在控制器中完成事务在技术上应该是可行的,但我发现了一些问题。 如果您查看下面的代码,我所期望的是,如果对saveContact的三个调用中的任何一个都因异常而失败(任何异常,因为我将rollbackor=Exception.class),那么这三个调用都应该回滚。不过,我看到的是,如果第三个失败,前两个的数据仍然

在我的SpringMVC应用程序中,我在控制器中有一个方法,需要将一组对象(从上传的文件构建)保存到数据库中。让我们暂时撇开关于事务是应该在控制器层还是服务层中完成的整个问题——问题是,在控制器中完成事务在技术上应该是可行的,但我发现了一些问题。 如果您查看下面的代码,我所期望的是,如果对saveContact的三个调用中的任何一个都因异常而失败(任何异常,因为我将rollbackor=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*/
    }
}
My 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" />
    <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>
我不清楚@Transactional注释是否被提取——据我所知,应该是这样的,因为我正在扫描ch.oligofunds.oligoworld.web包,并且控制器接口用@controller注释。但我的理解可能是错误的

有什么提示吗? 感谢使用proxy target class=“true”告诉spring使用cglib来处理代理,但是您已经指定了作用域proxy=“interfaces”


@Transactional
在该方法上没有任何附加值,因为它是一个内部方法调用(并且您的类已经是事务性的)。Spring使用代理,只通过代理调用对象

此外,您的代码也有缺陷,您不应该捕获并吞下异常,因为这会干扰tx支持(它依赖于事务来确定是否回滚,目前没有异常,因此始终尝试提交)

最后,您使用的是MySQL,请确保您使用的表类型实际上支持事务(MyISAM表不支持tx)


但是,我强烈建议将事务部分(或您现在在控制器中执行的业务逻辑)移动到服务。控制器(或一般的web层)应该只是一个薄层,将传入的请求转换为可用于服务层的内容,并将结果转换为可用于web显示的内容。

添加代理目标class=“true”,因为没有它,我会出现以下错误:请求处理失败;嵌套异常为java.lang.IllegalStateException:映射的控制器方法类“ch.oligofunds.oligoworld.web.ExcelUploaderImpl”不是实际控制器bean实例“com.sun.proxy.$Proxy69”的实例。如果控制器需要代理(例如由于@Transactional),请使用基于类的代理。我试图理解这一点:我不能同时使用两者?好的,所以我首先尝试删除web-context.xml(和dao-context.xml)中的proxy target class=“true”,只留下作用域proxy=”interfaces”。如前所述,我得到了上面评论中提到的错误。然后,我将proxy target class=“true”放回原处,并通过删除“implements ExcelUploader”和所有@Override注释来修改控制器(因此现在根本不使用接口)。这里运行的是事务,但事务仍然不工作(前两个插入在第三个插入失败时仍显示在DB中)。
@Transactional
该方法没有任何附加值,因为它是一个内部方法调用。Spring使用代理,只通过代理调用对象。此外,您的代码也有缺陷,您不应该捕获并吞下异常,因为这会干扰tx支持(它依赖于事务来确定是否回滚,目前没有异常,因此始终尝试提交)。最后,您使用的是MySQL,请确保您使用的表类型实际上支持事务(MyISAM表不支持tx)。我认为服务水平更好。看这个,这是毫无疑问的。然而,正如我在问题中所说,关键是它在技术上是可行的,我在这里试图理解的是为什么它不起作用。事实上,将其放在服务层本身并不能解决问题。@M.Deinum谢谢,这是一个很好的指针。我尝试将事务性迁移到控制器端点,并且确实有效。但是这很难看:)所以我最终重新组织了事情,所以事务不再在控制器中。你能把你的评论转换成回答吗?这样我就可以接受了?
    <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