你能解释为什么@Transactional没有回滚

时间:2013-01-01 22:57:59

标签: java spring-jdbc spring-transactions

我正在尝试在数据库中创建两个表(ABC,XYZ)。 XYZ语法故意有错误的语法,并且假设失败。想法是在尝试XYZ时应该回滚ABC的表创建。我是Spring Transaction的新手,并且已经搜索了这个简单的例子,日志似乎表明TransactionManager正在回滚,但最后我看到表ABC存在于Database中。任何人都可以解释为什么@Transactional不回滚......

    package com.tango.db.dao;

    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.transaction.annotation.Transactional;

    public class DBDAO {

            private JdbcTemplate jdbcTemplate;

            public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
              this.jdbcTemplate = jdbcTemplate;
            }

            @Transactional(rollbackFor = Exception.class)
            public void createTables(){
              jdbcTemplate.execute("CREATE TABLE ABC(ID NUMBER NOT NULL,  NAME VARCHAR2(35), primary key(ID))");
              jdbcTemplate.execute("CREATE TABLE XYZ(ID NUMBER NOT NULL, NAME VARCHAR2(35), UPDATE DATE(7), primary key(ID))");
            }

            public static void main(String[] args) {
              ClassPathXmlApplicationContext ctx = new         ClassPathXmlApplicationContext("spring-context.xml");
              DBDAO dbdao = (DBDAO)ctx.getBean("dbDAO");
              dbdao.createTables();
            }    
    }

弹簧context.xml中

    <bean id="local.oracle.dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
            <property name="url" value="jdbc:oracle:thin:@10.211.55.6:1521:xe"/>
            <property name="username" value="abc"/>
            <property name="password" value="1234"/>
            <property name="defaultAutoCommit" value="false"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <constructor-arg ref="local.oracle.dataSource"/>
    </bean>

    <bean id="dbDAO" class="com.tango.db.dao.DBDAO">
            <property name="jdbcTemplate" ref="jdbcTemplate"/>

    </bean>

    <tx:annotation-driven transaction-manager="txManager"/>

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="local.oracle.dataSource"/>
    </bean>

以下是日志:

    DEBUG SQLErrorCodeSQLExceptionTranslator - Translating SQLException with SQL state '42000', error code '904', message [ORA-00904: : invalid identifier
    ]; SQL was [CREATE TABLE XYZ(ID NUMBER NOT NULL, NAME VARCHAR2(35), UPDATE DATE(7), primary key(ID))] for task [StatementCallback]
    DEBUG DataSourceTransactionManager - Initiating transaction rollback
    DEBUG DataSourceTransactionManager - Rolling back JDBC transaction on Connection [jdbc:oracle:thin:@10.211.55.6:1521:xe, UserName=DBO, Oracle JDBC driver]
    DEBUG DataSourceTransactionManager - Releasing JDBC Connection [jdbc:oracle:thin:@10.211.55.6:1521:xe, UserName=DBO, Oracle JDBC driver] after transaction
    DEBUG DataSourceUtils - Returning JDBC Connection to DataSource
    Exception in thread "main" org.springframework.jdbc.BadSqlGrammarException: StatementCallback; bad SQL grammar [CREATE TABLE XYZ(ID NUMBER NOT NULL, NAME VARCHAR2(35), UPDATE DATE(7), primary key(ID))]; nested exception is java.sql.SQLSyntaxErrorException: ORA-00904: : invalid identifier

        at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:233)
        at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:407)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:428)
        at com.tango.db.dao.DBDAO.createTables(DBDAO.java:19)
        at com.tango.db.dao.DBDAO$$FastClassByCGLIB$$112527a.invoke(<generated>)
        at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
        at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
        at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
        at com.tango.db.dao.DBDAO$$EnhancerByCGLIB$$7b0ac5f7.createTables(<generated>)
        at com.tango.db.dao.DBDAO.main(DBDAO.java:25)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
    Caused by: java.sql.SQLSyntaxErrorException: ORA-00904: : invalid identifier

        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:440)
        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
        at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:837)
        at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:445)
        at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:191)
        at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:523)
        at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:193)
        at oracle.jdbc.driver.T4CStatement.executeForRows(T4CStatement.java:999)
        at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1315)
        at oracle.jdbc.driver.OracleStatement.executeInternal(OracleStatement.java:1890)
        at oracle.jdbc.driver.OracleStatement.execute(OracleStatement.java:1855)
        at oracle.jdbc.driver.OracleStatementWrapper.execute(OracleStatementWrapper.java:304)
        at org.apache.commons.dbcp.DelegatingStatement.execute(DelegatingStatement.java:264)
        at org.apache.commons.dbcp.DelegatingStatement.execute(DelegatingStatement.java:264)
        at org.springframework.jdbc.core.JdbcTemplate$1ExecuteStatementCallback.doInStatement(JdbcTemplate.java:421)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:396)
        ... 16 more

3 个答案:

答案 0 :(得分:2)

在您的情况下,事务边界并不完全取决于Spring。如果您深入研究Oracle文档,它会将事务边界定义如下:

  

事务以第一个可执行SQL语句开头。一个   事务在提交或回滚时结束   显式地使用COMMIT或ROLLBACK语句或隐式地使用   发布DDL声明。

http://docs.oracle.com/cd/B28359_01/server.111/b28318/transact.htm#i6564

CREATE TABLE是一种DDL(数据定义语言)语句。因此,当您发布第一个CREATE TABLE时,Oracle将其视为隐式事务结束。因此,当您的第二个CREATE TABLE发出并失败时,还原第一个为时已晚。

答案 1 :(得分:1)

这是因为 create table DDL (数据定义语言)语句,而仅DML (数据修改语言)语句被回滚。每个DDL语句都有一个隐式提交,因此无法回滚。

要完成,DML语句包括:插入更新删除

答案 2 :(得分:0)

看起来您正在使用JDK代理,但没有为Spring声明任何用于创建代理的接口。 DBDAO需要是一个包含createTables方法的接口,因此Spring可以创建包含事务代码的运行时实现。