Spring事务传播设置有如下类型:
- REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。==> 会跟随service层方法回滚事务
- SUPPORTS :如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。==> 会跟随service层方法回滚事务
- MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。==> 会跟随service层方法回滚事务
- REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。 ==> 即使service层的方法也加了事务,也无法回滚dao层的事务(内层已经提交了,外层回滚个屁啊)。
- NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起。 ==> 即使service层的方法也加了事务,也无法回滚dao层的事务(内层已经提交了,外层回滚个屁啊)。
- NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。 ==> service层无事务的方法可以运行,也就是说,他只能被一个父事务调用。否则,他就要抛出异常。
- NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED
上述只是单事务方法的情况,如果存在事务嵌套呢??比如serviceA.methodA()内部调用了serviceB.methodB()方法,methodA()和methodB()都是有事务的。这种情况下可以根据如下表格对照:
简单示例:
package com.ubuntuvim.mybatis.service;import java.util.Random;import java.util.UUID;import javax.annotation.Resource;import com.ubuntuvim.mybatis.dao.UserMapper;import com.ubuntuvim.mybatis.entity.User;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;/** * @Author: ubuntuvim * @Date: 2020/9/8 下午10:18 */@Servicepublic class UserServiceImpl implements UserService { @Resource UserMapper userMapper; @Override public void saveOneNoTransition() { userMapper.save(new User(UUID.randomUUID().toString(), "王五service无事务"+new Random().nextInt(10), "M")); } @Transactional(rollbackFor = Exception.class) @Override public void saveOneHasTransition() { userMapper.save(new User(UUID.randomUUID().toString(), "ubuntuvim,service有事务"+new Random().nextInt(10), "M")); } @Override public void saveUsersNoTransition() { String id = UUID.randomUUID().toString(); userMapper.save(new User(UUID.randomUUID().toString(), "张三service无事务", "M")); userMapper.save(new User(id, "张三2service无事务", "F")); userMapper.save(new User(UUID.randomUUID().toString(), "张三3service无事务", "F")); userMapper.save(new User(UUID.randomUUID().toString(), "张三4service无事务", "M")); // 这个save的id和第二个相同会导致数据库id主键冲突报错,因为当前方法没有加事务不会回滚前面的save持久化 userMapper.save(new User(id, "张三5service无事务", "M")); userMapper.save(new User(UUID.randomUUID().toString(), "张三6service无事务", "F")); userMapper.save(new User(UUID.randomUUID().toString(), "张三7service无事务", "M")); } /** * 由于此方法上添加了事务,即使在调用的userMapper.save()方法上也添加事务也会回滚。 * 但是如果dao的save方法的事务传播属性声明为propagation = Propagation.REQUIRES_NEW则无法回滚,在dao层已经提交了事务。service层回滚不了了。 * */ @Transactional(rollbackFor = Exception.class) @Override public void saveUsersHasTransition() { String id = UUID.randomUUID().toString(); userMapper.save(new User(UUID.randomUUID().toString(), "李四service有事务", "F")); userMapper.save(new User(id, "李四2service有事务", "F")); userMapper.save(new User(UUID.randomUUID().toString(), "李四3service有事务", "F")); userMapper.save(new User(UUID.randomUUID().toString(), "李四4service有事务", "M")); /* @Transactional(rollbackFor = Exception.class) 这个save的id和第二个相同会导致数据库id主键冲突报错,但是当前方法加了事务会从异常这个回滚前面的save 最终结果是这个方法内的save都不会持久化 */ userMapper.save(new User(id, "张三5service有事务", "M")); userMapper.save(new User(UUID.randomUUID().toString(), "李四6service有事务", "M")); userMapper.save(new User(UUID.randomUUID().toString(), "李四7service有事务", "F")); }}
service调用dao的方法。
package com.ubuntuvim.mybatis.dao;import com.ubuntuvim.mybatis.entity.User;import org.apache.ibatis.annotations.Insert;import org.apache.ibatis.annotations.Select;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;/** * @Author: ubuntuvim * @Date: 2020/8/2 20:54 */public interface UserMapper { @Select("select * from user t where t.name = #{name}") User getUser(User user); /** * 保存user,id是唯一的,否则insert会报错 * REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。==> 会跟随service层方法回滚事务 * SUPPORTS :如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。==> 会跟随service层方法回滚事务 * MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。==> 会跟随service层方法回滚事务 * REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。 ==> 即使service层的方法也加了事务,也无法回滚dao层的事务(内层已经提交了,外层回滚个屁啊)。 * NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起。 ==> 即使service层的方法也加了事务,也无法回滚dao层的事务(内层已经提交了,外层回滚个屁啊)。 * NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。 ==> service层无事务的方法可以运行,也就是说,他只能被一个父事务调用。否则,他就要抛出异常。 * NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED * @param user */ @Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED) @Insert("insert into user(id, name, sex) value (#{id}, #{name}, #{sex})") void save(User user);}
测试类:
package com.ubuntuvim.mybatis.service;import javax.annotation.Resource;import com.ubuntuvim.mybatis.MybatisTest;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/** * UserServiceImpl Tester. * * @author * @since
9月 8, 2020
* @version 1.0 */@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTest(classes = MybatisTest.class)public class UserServiceImplTest { @Resource UserService userServiceImpl; @Test public void testSaveUsersNoTransition() throws Exception { userServiceImpl.saveUsersNoTransition(); } @Test public void testSaveUsersHasTransition() throws Exception { userServiceImpl.saveUsersHasTransition(); } @Test public void testSaveOneNoTransation() { userServiceImpl.saveOneNoTransition(); } @Test public void testSaveOneHasTransation() { userServiceImpl.saveOneHasTransition(); }}