第一步导入坐标
<!--sharding+seata+mp整合开始 第一步-->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC2</version>
</dependency>
<!--seata与sharding整合包 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-transaction-base-seata-at</artifactId>
<version>4.0.0-RC2</version>
</dependency>
<!-- mp 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.2.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!--sharding+seata整合结束-->
第二步增加配置类
1)MybatisPlusConfig类
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/**
* mybatis-plus配置
* @author yyj
*/
@Configuration
public class MybatisPlusConfig {
/**
* 配置分页
*/
@Bean
@Order(0)
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
/**
* 主键生成策略
* @return
*/
@Bean
public IKeyGenerator keyGenerator(){
return new H2KeyGenerator();
}
}
2)MybatisPlusAutoConfig类
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.ClassPathMapperScanner;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.util.Iterator;
import java.util.List;
/**
* sharding+seata+mp整合开始 第三步配置数据源
* @author yyj
* @date 2020年7月20日16:48:19
*/
@Configuration
@AutoConfigureAfter(MybatisPlusConfig.class)
@EnableConfigurationProperties({MybatisPlusProperties.class})
public class MybatisPlusAutoConfig {
private static final Logger logger = LoggerFactory.getLogger(MybatisPlusAutoConfig.class);
private final MybatisPlusProperties properties;
private final Interceptor[] interceptors;
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
private final List<ConfigurationCustomizer> configurationCustomizers;
private final ApplicationContext applicationContext;
public MybatisPlusAutoConfig(MybatisPlusProperties properties,
ObjectProvider<Interceptor[]> interceptorsProvider, ResourceLoader resourceLoader,
ObjectProvider<DatabaseIdProvider> databaseIdProvider,
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,
ApplicationContext applicationContext) {
this.properties = properties;
this.interceptors = (Interceptor[]) interceptorsProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = (DatabaseIdProvider) databaseIdProvider.getIfAvailable();
this.configurationCustomizers = (List) configurationCustomizersProvider.getIfAvailable();
this.applicationContext = applicationContext;
}
public void afterPropertiesSet() {
this.checkConfigFileExists();
}
private void checkConfigFileExists() {
if (this.properties.isCheckConfigLocation() && StringUtils
.hasText(this.properties.getConfigLocation())) {
Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
Assert.state(resource.exists(), "Cannot find config location: " + resource
+ " (please add config file or check your Mybatis configuration)");
}
}
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory
.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
this.applyConfiguration(factory);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
}
if (this.properties.getTypeAliasesSuperType() != null) {
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
GlobalConfig globalConfig = this.properties.getGlobalConfig();
if (this.applicationContext.getBeanNamesForType(MetaObjectHandler.class, false, false).length
> 0) {
MetaObjectHandler metaObjectHandler = (MetaObjectHandler) this.applicationContext
.getBean(MetaObjectHandler.class);
globalConfig.setMetaObjectHandler(metaObjectHandler);
}
if (this.applicationContext.getBeanNamesForType(IKeyGenerator.class, false, false).length > 0) {
IKeyGenerator keyGenerator = (IKeyGenerator) this.applicationContext
.getBean(IKeyGenerator.class);
globalConfig.getDbConfig().setKeyGenerator(keyGenerator);
}
if (this.applicationContext.getBeanNamesForType(ISqlInjector.class, false, false).length > 0) {
ISqlInjector iSqlInjector = (ISqlInjector) this.applicationContext
.getBean(ISqlInjector.class);
globalConfig.setSqlInjector(iSqlInjector);
}
factory.setGlobalConfig(globalConfig);
return factory.getObject();
}
private void applyConfiguration(MybatisSqlSessionFactoryBean factory) {
MybatisConfiguration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new MybatisConfiguration();
}
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
Iterator var3 = this.configurationCustomizers.iterator();
while (var3.hasNext()) {
ConfigurationCustomizer customizer = (ConfigurationCustomizer) var3.next();
customizer.customize(configuration);
}
}
factory.setConfiguration(configuration);
}
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType)
: new SqlSessionTemplate(sqlSessionFactory);
}
@Configuration
@Import({AutoConfiguredMapperScannerRegistrar.class})
@ConditionalOnMissingBean({MapperFactoryBean.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
public MapperScannerRegistrarNotFoundConfiguration() {
}
@Override
public void afterPropertiesSet() {
MybatisPlusAutoConfig.logger.debug("No {} found.", MapperFactoryBean.class.getName());
}
}
public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware,
ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
public AutoConfiguredMapperScannerRegistrar() {
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (!AutoConfigurationPackages.has(this.beanFactory)) {
MybatisPlusAutoConfig.logger.debug(
"Could not determine auto-configuration package, automatic mapper scanning disabled.");
} else {
MybatisPlusAutoConfig.logger.debug("Searching for mappers annotated with @Mapper");
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (MybatisPlusAutoConfig.logger.isDebugEnabled()) {
packages.forEach((pkg) -> {
MybatisPlusAutoConfig.logger.debug("Using auto-configuration base package '{}'", pkg);
});
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
scanner.setAnnotationClass(Mapper.class);
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(packages));
}
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
}
第三步排除数据源
@SpringBootApplication(exclude = {
DruidDataSourceAutoConfigure.class,
DataSourceAutoConfiguration.class,
MybatisPlusAutoConfiguration.class
})
第四步seata配置
### 这里很重要
# 异常:Could not register branch into global session xid = status = Rollbacked(还有Rollbacking、AsyncCommitting等等二阶段状态) while expecting Begin
# 描述:分支事务注册时,全局事务状态需是一阶段状态begin,非begin不允许注册。属于seata框架层面正常的处理,用户可以从自身业务层面解决。
# 出现场景(可继续补充)
#
# 1. 分支事务是异步,全局事务无法感知它的执行进度,全局事务已进入二阶段,该异步分支才来注册
# 2. 服务a rpc 服务b超时(dubbo、feign等默认1秒超时),a上抛异常给tm,tm通知tc回滚,但是b还是收到了请求(网络延迟或rpc框架重试),然后去tc注册时发现全局事务已在回滚
# 3. tc感知全局事务超时(@GlobalTransactional(timeoutMills = 默认60秒)),主动变更状态并通知各分支事务回滚,此时有新的分支事务来注册
ribbon:
# 连接超时
ConnectTimeout: 5000
# 响应超时 设置超时时间设置长一点。应为回滚需要时间
ReadTimeout: 10000
seata:
enabled: true
application-id: data-api
tx-service-group: my_test_tx_group
enable-auto-data-source-proxy: true
config:
type: nacos
nacos:
namespace:
server-addr: 192.168.101.50:8848
group: SEATA_GROUP
username: nacos
password: nacos
registry:
type: nacos
nacos:
application: seata-server
server-addr: 192.168.101.50:8848
namespace:
username: nacos
password: nacos
第五步resources增加seata.conf文件
## ---------------------------------------------------------------------------
## Licensed to the Apache Software Foundation (ASF) under one or more
## contributor license agreements. See the NOTICE file distributed with
## this work for additional information regarding copyright ownership.
## The ASF licenses this file to You under the Apache License, Version 2.0
## (the "License"); you may not use this file except in compliance with
## the License. You may obtain a copy of the License at
##
## http://www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
## ---------------------------------------------------------------------------
client {
##这个是唯一的和项目名称一致
application.id = data-api
transaction.service.group = my_test_tx_group
}
注意
seata1.3.0和sharding整合会报一个错误 Can not find columnLabel INDEX_NAME
这个问题我已经给官方修复了。只需要把ResultSet列名改为小写就可以了。
等官方发新版本就可以了。 或者自己改下源码 重新打包。