1

我正在尝试在数据库中创建两个表(ABC,XYZ)。XYZ 语法故意具有错误的语法,并且假定会失败。这个想法是在尝试 XYZ 时应该回滚 ABC 的表创建。我是 Spring Transaction 的新手,并且四处搜索以提出这个简单的示例,日志似乎显示 TransactionManager 正在回滚,但最后我看到表 ABC 存在于数据库中。谁能解释为什么@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();
            }    
    }

弹簧上下文.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
4

3 回答 3

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 发出并失败时,恢复第一个为时已晚。

于 2013-01-02T00:44:43.540 回答
1

这是因为创建表是一个DDL(数据定义语言)语句,并且只有 DML(数据修改语言)语句被回滚。每个 DDL 语句都有一个隐式提交,因此它们不能回滚。

完整地说,DML 语句是:插入更新删除

于 2013-01-01T23:28:31.317 回答
0

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

于 2013-01-01T23:27:19.880 回答