2016-05-17 1 views
0

В моем проекте, основанном на Spring Boot 1.3.3, я интегрировал MyBatis с mybatis-spring-boot-starter 1.1.1 в качестве уровня устойчивости, все операции CRUD работают нормально, но тесты интеграции не удались, и я обнаружил, что операция БД блокируется в асинхронной задаче. тест код выглядит следующим образом:Операция MyBatis с блокировкой в ​​Spring Boot Async Method

@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() продолжение и завершение. Log:

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()); 
    } 
} 

дао:

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 

ответ

0

Проблема вы видите, вызвана тем, что весь метод испытания shouldUpdateOrder выполняется в одной транзакции. Это означает, что любая операция обновления, которая выполняется в потоке, который выполняется shouldUpdateOrder, блокирует запись на весь период транзакции (то есть до выхода из тестового метода), и эта запись не может быть обновлена ​​другой параллельной транзакцией (которая выполняется в async метод).

Для решения проблемы вам необходимо изменить границы транзакций. В вашем случае правильного способа эмулировать реальное использование жизни является

  1. создать заказ в одной транзакции и завершить сделку
  2. порядка обновления в другой транзакции
  3. проверьте, что обновление выполняется, как ожидается, в другой сделке
+0

большое спасибо! вы спасли меня много времени, после вашего совета я изменил границы транзакций и проблема была решена. – qianlei