MyBatis操作在Spring Boot异步方法中被阻止

时间:2016-05-17 09:48:55

标签: asynchronous spring-boot mybatis

在我的基于Spring Boot 1.3.3的项目中,我将MyBatis与mybatis-spring-boot-starter 1.1.1集成为持久层,所有CRUD操作似乎都单独工作,但集成测试失败了,我找到了DB异步任务中的操作被阻止。 测试代码如下所示:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SapiApplication.class)
@Transactional
public class OrderIntegrationTest {
    @Test
    public void shouldUpdateOrder() throws InterruptedException{
        Order order1 = getOrder1();
        orderService.createOrder(order1);

        Order order1updated = getOrder1Updated();
        orderService.updateOrderAsync(order1updated);

        Thread.sleep(1000l);

        log.info("find the order!");
        Order order1Db = orderService.findOrderById(order1.getOrderId());
        log.info("found the order!");
        assertEquals("closed", order1Db.getStatus());
    }
}

预期的执行顺序是createOrder() - > updateOrderAsync() - > findOrderById(),但实际执行顺序是createOrder() - > updateOrderAsync()已启动并被阻止 - > findOrderById() - > updateOrderAsync()继续并结束。 经度:

16:23:04.261 [executor1-1] INFO  c.s.api.web.service.OrderServiceImpl - updating order: 2884384
16:23:05.255 [main] INFO  c.s.a.w.service.OrderIntegrationTest - find the order!
16:23:05.280 [main] INFO  c.s.a.w.service.OrderIntegrationTest - found the order!
16:23:05.299 [executor1-1] INFO  c.s.api.web.service.OrderServiceImpl - updated order: 2884384

其他相关代码:

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderDao orderDao;

    @Async("executor1")
    @Override
    public void updateOrderAsync(Order order){
        log.info("updating order: {}", order.getOrderId());
        orderDao.updateOrder(order);
        log.info("updated order: {}", order.getOrderId());
    }
}

DAO:

public interface OrderDao {
    public int updateOrder(Order order);
    public int createOrder(Order order);
    public Order findOrderById(String orderId);
}

Gradle依赖项:

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-jdbc' 
    compile 'org.springframework.boot:spring-boot-starter-security' 
    compile 'org.springframework.boot:spring-boot-starter-web' 
    compile 'org.springframework.boot:spring-boot-starter-actuator'
    compile 'org.mybatis.spring.boot:mybatis-spring-boot-starter:1.1.1' 
    compile 'ch.qos.logback:logback-classic:1.1.2'
    compile 'org.springframework.boot:spring-boot-configuration-processor'
    runtime 'mysql:mysql-connector-java' 
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' 
    testCompile 'org.springframework.boot:spring-boot-starter-test'  
    testCompile "org.springframework.security:spring-security-test" 
}

Spring配置:

@SpringBootApplication
@EnableAsync
@EnableCaching
@EnableScheduling
@MapperScan("com.sapi.web.dao")
public class SapiApplication {

@Bean(name = "executor1")
protected Executor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(100);
    return executor;
}

@Bean
@Primary
@ConfigurationProperties(prefix = "datasource.primary")
public DataSource numberMasterDataSource() {
    return DataSourceBuilder.create().build();
}

@Bean(name = "secondary")
@ConfigurationProperties(prefix = "datasource.secondary")
public DataSource provisioningDataSource() {
    return DataSourceBuilder.create().build();
}

@Bean(name = "jdbcTpl")
public JdbcTemplate jdbcTemplate(@Qualifier("secondary") DataSource dsItems)   {
    return new JdbcTemplate(dsItems);
}

public static void main(String[] args) {
    SpringApplication.run(SapiApplication.class, args);
}
}

属性:

mybatis.mapper-locations=classpath*:com/sapi/web/dao/*Mapper.xml
mybatis.type-aliases-package=com.sapi.web.vo

datasource.primary.driver-class-name=com.mysql.jdbc.Driver
datasource.primary.url=jdbc:mysql://10.0.6.202:3306/sapi
datasource.primary.username=xxx
datasource.primary.password=xxx
datasource.primary.maximum-pool-size=80
datasource.primary.max-idle=10
datasource.primary.max-active=150
datasource.primary.max-wait=10000
datasource.primary.min-idle=5
datasource.primary.initial-size=5
datasource.primary.validation-query=SELECT 1
datasource.primary.test-on-borrow=false
datasource.primary.test-while-idle=true
datasource.primary.time-between-eviction-runs-millis=18800
datasource.primary.jdbc-interceptors=ConnectionState;SlowQueryReport(threshold=100)

datasource.secondary.url = jdbc:mysql://10.0.6.202:3306/xdb
datasource.secondary.username = xxx
datasource.secondary.password = xxx
datasource.secondary.driver-class-name = com.mysql.jdbc.Driver

logging.level.org.springframework.web=DEBUG

1 个答案:

答案 0 :(得分:0)

您看到的问题是由于整个测试方法shouldUpdateOrder在一个事务中执行的事实。这意味着在运行shouldUpdateOrder的线程中执行的任何更新操作都会在事务的整个持续时间内锁定记录(直到从测试方法退出),并且该记录无法通过另一个并发事务更新(在异步方法中执行)。

要解决此问题,您需要更改事务边界。在您的情况下,模拟现实生活使用的正确方法是

  1. 在一个交易中创建订单并完成交易
  2. 在另一个交易中更新订单
  3. 检查更新是否按照预期在另一个事务中执行