java - 你能解释一下为什么@Transactional 不回滚吗

标签 java spring-jdbc spring-transactions

我正在尝试在数据库中创建两个表(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();
            }    
    }

Spring 上下文.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

最佳答案

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

A transaction begins with the first executable SQL statement. A transaction ends when it is committed or rolled back, either explicitly with a COMMIT or ROLLBACK statement or implicitly when a DDL statement is issued.

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

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

关于java - 你能解释一下为什么@Transactional 不回滚吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14115013/

相关文章:

java - 使用注释的 Spring JDBC 模板为 null

java - Spring Framework应用中JDBC使用RowMapper的一些疑惑

java - 尝试使用 Autowiring 的候选者运行存储库 impl 进行测试

java - Spring 响应式(Reactive)事务在取消时提交,产生部分提交

java - Android 中的不安全类?

java - 使用 Siena 进行非规范化数据建模

java - 如何在 Android Studio 中更改汉堡菜单按钮和选项卡文本的颜色?

java - 尝试检索特定路径下的文件夹/文件的 CMIS 查询不返回任何文档

activemq - 无法使用 Atomikos 配置涉及 ActiveMQ 和嵌入式 H2 的分布式事务

java - 从关闭文件方法抛出 IOException 时如何管理事务(包括文件 IO)