我正在尝试在数据库中创建两个表(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
答案 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可以创建包含事务代码的运行时实现。