Spring的事物注解是@Transactional,加上@Transactional可以将对多个数据库更新操作作为一个数据来提交。例如:
@RestController @RequestMapping("/register") public class RegisterController { @Autowired private ProducerService producerService; @RequestMapping("/producer") @Transactional(rollbackFor = Exception.class) public Long registerProducer(@RequestBody ProducerDTO producerDTO) { return producerService.registerProducer(producerDTO); } }
当我们传递一个producerDTO进来后,调用Service服务,Service服务又会调用dao把数据存到数据库。
@Service public class ProducerServiceImpl implements ProducerService { @Autowired private ProducerDao producerDao; @Override public Long registerProducer(ProducerDTO producerDTO) { Producer producer = new Producer(); BeanUtils.copyProperties(producerDTO, producer); producerDao.insertSelective(producer); return producer.getId(); } }
<insert id="insertSelective" parameterType="com.waimaibang.console.model.Producer" useGeneratedKeys="true" keyProperty="id"> <!-- WARNING - @mbg.generated This element is automatically generated by MyBatis Generator, do not modify. --> insert into producer <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="name != null"> name, </if> <if test="createTime != null"> create_time, </if> <if test="updateTime != null"> update_time, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=BIGINT}, </if> <if test="name != null"> #{name,jdbcType=VARCHAR}, </if> <if test="createTime != null"> #{createTime,jdbcType=TIMESTAMP}, </if> <if test="updateTime != null"> #{updateTime,jdbcType=TIMESTAMP}, </if> </trim> </insert>
这是很多开发人员都习以为常的操作。
但是在SpringBoot 2.0中,如果在单元测试中使用@Transactional会怎么样呢?
@RunWith(SpringRunner.class) @SpringBootTest public class ConsoleRegisterTest { @Autowired private ProducerDao producerDao; @Autowired private ConsumerDao consumerDao; @Autowired private DestinationDao destinationDao; /** * 模拟控制台producer/consumer/destination注册服务 * @Transactional 不提交修改 */ @Test @Transactional public void loginTest() { Long producerId = registerProducer(); Long consumerId = registerConsumser(); Long destinationId = registerDestination(); //绑定producerId、destinationId int producerDestinationInsertResult = destinationDao.bindProducerDestination(producerId, destinationId); //绑定consumerId、destinationId int consumerDestinationInsertResult = destinationDao.bindConsumerDestination(consumerId, destinationId); System.out.printf("register a new producer, id = %s%n", producerId); System.out.printf("register a new consumer, id = %s%n", consumerId); System.out.printf("register a new destination, id = %s%n", destinationId); System.out.printf("register a new producerDestination, result = %s%n", producerDestinationInsertResult > 0); System.out.printf("register a new consumerDestination, result = %s%n", consumerDestinationInsertResult > 0); } /** * 模拟注册producer * @return */ public Long registerProducer() { Producer producer = new Producer(); producer.setName("test_producer"); producerDao.insertSelective(producer); return producer.getId(); } /** * 模拟注册consumer * @return */ public Long registerConsumser() { Consumer consumer = new Consumer(); consumer.setName("test_producer"); consumerDao.insertSelective(consumer); return consumer.getId(); } /** * 模拟注册destination * @return */ public Long registerDestination() { Destination destination = new Destination(); destination.setName("test_destination"); destinationDao.insertSelective(destination); return destination.getId(); } }
看下控制台的输出结果:
register a new producer, id = 3 register a new consumer, id = 3 register a new destination, id = 3 register a new producerDestination, result = true register a new consumerDestination, result = true
但是再观察一下数据库,发现数据库中并未有新的数据插入。
扫描二维码关注公众号,回复:
1453496 查看本文章
可以发现,在单元测试中加入了@Transactional并未导致事物的提交。所以在SpringBoot2.0的单元测试中使用@Transactional对可以对dao进行重复利用而不会担心测试数据污染了数据库。