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
但是再观察一下数据库,发现数据库中并未有新的数据插入。
可以发现,在单元测试中加入了@Transactional并未导致事物的提交。所以在SpringBoot2.0的单元测试中使用@Transactional对可以对dao进行重复利用而不会担心测试数据污染了数据库。