Transaction not rolled back
Bug Report
Using local transactions without rollback I used a single database sharding tables use local transaction mode I have configured two tables
- product
- order
sharding.yaml
i setting
defaultType: LOCAL
dataSources:
product:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.jdbc.Driver
jdbcUrl: jdbc:mysql://192.168.18.128:3310/db_order
username: root
password: 123456
rules:
- !SHARDING
tables:
t_product: # 分表,逻辑表名
# 节点表添加下初始的表,后续会在新增租户的时候新增表且刷新节点
actualDataNodes: product.t_product,product.t_product_1,product.t_product_2
tableStrategy: # 配置分表策略
standard: # 用于单分片键的标准分片场景
shardingColumn: tenant_id
shardingAlgorithmName: real-data-inline
t_order: # 分表,逻辑表名
# 节点表添加下初始的表,后续会在新增租户的时候新增表且刷新节点
actualDataNodes: product.t_order,product.t_order_1,product.t_order_2
tableStrategy: # 配置分表策略
standard: # 用于单分片键的标准分片场景
shardingColumn: tenant_id
shardingAlgorithmName: real-data-inline
# 分片算法配置
shardingAlgorithms:
real-data-inline: # 分片算法名称
type: CLASS_BASED #自定义策略
props:
strategy: standard
# 包名+类名
algorithmClassName: org.example.utils.DataShardingAlgorithm
bindingTables:
- t_product,t_order
- !TRANSACTION
defaultType: LOCAL
props:
sql-show: true
Method implementation
@Transactional
public Object addProductAndOrder(){
Product product = new Product();
product.setProductNo("TEST-11111");
product.setTenantId(2);
productMapper.insert(product);
int i = 1/0;
Order order = new Order();
order.setOrderNo("aaaaa");
order.setTenantId(2);
orderMapper.insert(order);
return "success";
}
i setting int i = 1/0; imitate exception
2023-09-10 16:26:37.600 INFO 9244 --- [nio-8080-exec-1] ShardingSphere-SQL : Logic SQL: INSERT INTO t_product ( id,
product_no,
tenant_id ) VALUES ( ?,
?,
? )
2023-09-10 16:26:37.600 INFO 9244 --- [nio-8080-exec-1] ShardingSphere-SQL : Actual SQL: product ::: INSERT INTO t_product_2 ( id,
product_no,
tenant_id ) VALUES (?, ?, ?) ::: [1700787847348391938, TEST-11111, 2]
2023-09-10 16:26:37.644 ERROR 9244 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause
java.lang.ArithmeticException: / by zero
at org.example.controller.UserController.addProductAndOrder(UserController.java:65) ~[classes/:na]
at org.example.controller.UserController$$FastClassBySpringCGLIB$$6f13626b.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.29.jar:5.3.29]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.29.jar:5.3.29]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.29.jar:5.3.29]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.29.jar:5.3.29]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.29.jar:5.3.29]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.29.jar:5.3.29]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.29.jar:5.3.29]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.29.jar:5.3.29]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.29.jar:5.3.29]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.29.jar:5.3.29]
at org.example.controller.UserController$$EnhancerBySpringCGLIB$$23380ca2.addProductAndOrder(<generated>) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.29.jar:5.3.29]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.29.jar:5.3.29]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.29.jar:5.3.29]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.29.jar:5.3.29]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.29.jar:5.3.29]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.29.jar:5.3.29]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072) ~[spring-webmvc-5.3.29.jar:5.3.29]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) ~[spring-webmvc-5.3.29.jar:5.3.29]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.29.jar:5.3.29]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.29.jar:5.3.29]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:529) ~[tomcat-embed-core-9.0.78.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.29.jar:5.3.29]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:623) ~[tomcat-embed-core-9.0.78.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) ~[tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-9.0.78.jar:9.0.78]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.78.jar:9.0.78]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.29.jar:5.3.29]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.78.jar:9.0.78]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.29.jar:5.3.29]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.78.jar:9.0.78]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.29.jar:5.3.29]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.78.jar:9.0.78]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.78.jar:9.0.78]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
The execution results are shown above,Transaction not rolled back, How can I configure to solve the above problem?
very Thank you.
Which version of ShardingSphere did you use?
shardingsphere-jdbc 5.3.2 + JDK 17 + spring-boot 2.7.14
Which project did you use? ShardingSphere-JDBC or ShardingSphere-Proxy?
ShardingSphere-JDBC
@StarHuzy Hi, 5.3.2 has e2e coverage for transaction rollback, so there will be no rollback problem. I also tested manually and the problem did not occur. Specifically, you may need to provide a complete demo that can reproduce the problem to further locate the problem.
@FlyingZC
Sorry for the slow response , This is my demo ! https://github.com/StarHuzy/sharging-jdbc
In this case, I used two transaction modes
- LOCAL
- XA/Atomikos
in short
LOCAL This mode transaction is invalid ,
The transaction in this XA/Atomikos mode has been rolled back
I didn't find the Spring @Transactional annotation in your demo. You can manually test the ShardingSphere transaction functionality using the JDBC API to see if it works properly. Alternatively, you can refer to this demo: https://github.com/FlyingZC/shardingsphere-transaction-spring
• Understanding
- In a single-datasource sharding setup, you insert first, then throw an exception to expect LOCAL transactions (defaultType=LOCAL) to roll back, but the record is not rolled back; the same flow rolls back under XA/Atomikos.
- The sample repo sharging-jdbc contains no @Transactional, and sharding.yaml sets defaultType: XA while the code uses DataSourceTransactionManager.
Root Cause
- No Spring transaction boundary: without @Transactional (or manual setAutoCommit(false)/rollback), MySQL auto-commits each statement, so it looks like “LOCAL transaction is invalid.”
- Transaction type and manager mismatch: sharding.yaml is XA, but you use a local transaction manager. To test LOCAL, set defaultType: LOCAL and ensure @Transactional is active.
- ShardingSphere 5.3.2 already has e2e coverage for local transaction rollback; no similar rollback bug is known.
Analysis
- In LOCAL mode, ShardingSphere delegates to the underlying datasource; you must annotate service methods with @Transactional (or manage JDBC transactions manually), otherwise each write auto-commits.
- If you want XA, specify @ShardingTransactionType(TransactionType.XA) or TransactionTypeHolder.set(TransactionType.XA) before the call, and use a JTA JtaTransactionManager (e.g., Atomikos). Otherwise it falls back to local transactions.
- Official transaction doc (Spring + local/XA config): https://shardingsphere.apache.org/document/current/en/user-manual/shardingsphere-jdbc/usage/transaction/
Conclusion
- This is a usage/configuration issue rather than a ShardingSphere bug. Recommendations:
- For local transactions, set defaultType: LOCAL in sharding.yaml and ensure the tables use InnoDB.
- Add @Transactional to service methods that include multiple writes, e.g.:
@Service
public class OrderService {
@Transactional
public void addOrderThenFail() {
orderMapper.insert(...);
int x = 1 / 0; // simulate exception
orderItemMapper.insert(...);
}
}
3. Ensure Spring’s transaction manager is based on the ShardingSphere DataSource.
- If it still doesn’t roll back, please share: a minimal repro including @Transactional, your current sharding.yaml, SHOW CREATE TABLE outputs, and SQL logs around the exception so we can dig deeper.
There hasn't been any activity on this issue recently, and in order to prioritize active issues, it will be marked as stale.