簡體   English   中英

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

[英]MyBatis Operation Gets Blocked in Spring Boot Async Method

在基於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

您看到的問題是由於整個測試方法shouldUpdateOrder在一個事務中執行這一事實引起的。 這意味着在運行的線程中執行的任何更新操作shouldUpdateOrder在事務的整個持續時間內(直到退出測試方法)鎖定記錄,並且該記錄不能由另一個並發事務(以異步方式執行)更新方法)。

要解決此問題,您需要更改交易邊界。 在您的情況下,模擬現實生活使用情況的正確方法是:

  1. 在一筆交易中創建訂單並完成交易
  2. 更新另一筆交易中的訂單
  3. 檢查是否在另一個事務中按預期執行了更新

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM