赞
踩
ChainedTransactionManager是Spring框架中的一个事务管理器实现,它可以将多个事务管理器组合在一起形成一个链式的事务管理器。
在实际应用中,可能需要在不同的数据源上进行事务管理。这时,可以使用多个数据源对应多个事务管理器,然后通过ChainedTransactionManager将它们组合成一个事务管理器,从而实现全局事务的管理。
ChainedTransactionManager的使用方式与普通的事务管理器类似,可以将其配置为Spring的事务管理器,然后通过@Transactional注解或编程式事务管理等方式来开启事务。
需要注意的是,ChainedTransactionManager只有在所有子事务管理器都支持嵌套事务时才能使用,否则会抛出异常。此外,ChainedTransactionManager的事务提交顺序与子事务管理器的顺序相反,即先提交最后一个子事务,最后提交第一个子事务。
从Spring Framework 5.3版本开始,Spring提供了一个新的事务管理器实现类CompositeTransactionManager,它可以像ChainedTransactionManager一样组合多个事务管理器,但具有更好的可扩展性和灵活性。因此,如果需要组合多个事务管理器时,可以考虑使用CompositeTransactionManager代替ChainedTransactionManager。
在 Spring 中操作多个数据源并保证事务的一致性,可以使用 Spring 的事务管理和多数据源配置。配置事务管理器:可以使用 Spring 的事务管理器来管理多个数据源的事务。例如,可以配置一个 ChainedTransactionManager 对象,将多个事务管理器(如 DataSourceTransactionManager)串联起来
spring.datasource.vw.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.vw.initialSize=10 spring.datasource.vw.maxActive=20 spring.datasource.vw.maxWait=60000 spring.datasource.vw.minIdle=1 spring.datasource.vw.password= spring.datasource.vw.jdbc-url=jdbc:mysql://...:3306/y_water?useUnicode=true&characterEncoding=UTF-8 spring.datasource.vw.username= mybatis.vw.mapper-locations=classpath*\:cc/eslink/villagewater/mapper/vw/*.xml mybatis.vw.type-aliases-package=cc.eslink.villagewater.domain.entity spring.datasource.bm.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.bm.initialSize=10 spring.datasource.bm.maxActive=20 spring.datasource.bm.maxWait=60000 spring.datasource.bm.minIdle=1 spring.datasource.bm.password= spring.datasource.bm.jdbc-url=jdbc:mysql://...:3306/econdsupply?useUnicode=true&characterEncoding=UTF-8 spring.datasource.bm.username= mybatis.bm.mapper-locations=classpath*\:cc/eslink/villagewater/mapper/bm/*.xml mybatis.bm.type-aliases-package=cc.eslink.villagewater.domain.entity
BmMybatisConfig
package cc.eslink.villagewater.config; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import tk.mybatis.spring.annotation.MapperScan; import javax.sql.DataSource; /** * 数据库配置 * BmMybatisConfig * * @author wxj * @date 2023/1/13 10:31 **/ @Configuration @MapperScan(basePackages = "cc.eslink.villagewater.dao.bm", sqlSessionFactoryRef = "bmSqlSessionFactory") public class BmMybatisConfig extends BaseExtendMybatisConfig { @Value("mybatis.bm.type-aliases-package") private String typeAliasesPackageKey; @Value("mybatis.bm.mapper-locations") private String mapperLocationsKey; @Bean("bmDataSource") @ConfigurationProperties(prefix = "spring.datasource.bm") @RefreshScope @Override public DataSource dataSource() { return DataSourceBuilder.create().build(); } @Bean("bmSqlSessionTemplate") @Override public JdbcTemplate jdbcTemplate( @Qualifier("bmDataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean("bmSqlSessionFactory") @Override public SqlSessionFactory sqlSessionFactory(@Qualifier("bmDataSource") DataSource dataSource) throws Exception { return super.sqlSessionFactory(dataSource); } @Override public String getMapperLocationsKey() { return mapperLocationsKey; } @Override public String getTypeAliasesPackageKey() { return typeAliasesPackageKey; } }
VwMybatisConfig
package cc.eslink.villagewater.config; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import tk.mybatis.spring.annotation.MapperScan; import javax.sql.DataSource; /** * @author wxj * @date 2023/1/13 10:31 **/ @Configuration @MapperScan(basePackages = "cc.eslink.villagewater.dao.vw", sqlSessionFactoryRef = "vwSqlSessionFactory") public class VwMybatisConfig extends BaseExtendMybatisConfig { @Value("mybatis.vw.type-aliases-package") private String typeAliasesPackageKey; @Value("mybatis.vw.mapper-locations") private String mapperLocationsKey; @Bean("vwDataSource") @ConfigurationProperties(prefix = "spring.datasource.vw") @RefreshScope @Override public DataSource dataSource() { return DataSourceBuilder.create().build(); } @Bean("vwSqlSessionTemplate") @Override public JdbcTemplate jdbcTemplate( @Qualifier("vwDataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean("vwSqlSessionFactory") @Override public SqlSessionFactory sqlSessionFactory(@Qualifier("vwDataSource") DataSource dataSource) throws Exception { return super.sqlSessionFactory(dataSource); } @Override public String getMapperLocationsKey() { return mapperLocationsKey; } @Override public String getTypeAliasesPackageKey() { return typeAliasesPackageKey; } }
BaseExtendMybatisConfig
package cc.eslink.villagewater.config; import cc.eslink.util.BeanUtil; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.io.VFS; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.core.env.Environment; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.util.ClassUtils; import javax.sql.DataSource; import java.util.ArrayList; import java.util.HashSet; import java.util.List; /** * 扩展数据源mybatis配置基类 * @author wxj * @since 2023/01/13 */ public abstract class BaseExtendMybatisConfig { private final static Logger logger = LoggerFactory.getLogger(BaseExtendMybatisConfig.class); static final String DEFAULT_RESOURCE_PATTERN = "/**/*.class"; @javax.annotation.Resource private Environment env; @RefreshScope protected abstract DataSource dataSource(); protected abstract JdbcTemplate jdbcTemplate(DataSource dataSource); /** * 让type-aliases-package支持通配符 * */ protected static String setTypeAliasesPackage(String typeAliasesPackage) { try { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); typeAliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(typeAliasesPackage) + DEFAULT_RESOURCE_PATTERN; List<String> result = new ArrayList<String>(); Resource[] resources = resolver.getResources(typeAliasesPackage); if (resources != null && resources.length > 0) { MetadataReader metadataReader = null; for (Resource resource : resources) { if (!resource.getURL().toString().contains("-facade-") && resource.isReadable()) { metadataReader = metadataReaderFactory.getMetadataReader(resource); result.add( Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); } } } if (result.size() > 0) { HashSet<String> h = new HashSet<String>(result); result.clear(); result.addAll(h); typeAliasesPackage = String.join(",", result.toArray(new String[0])); } if (logger.isDebugEnabled()) { logger.debug("扫描type-aliases-package结果: {}", typeAliasesPackage); } } catch (Exception e) { logger.error("扫描type-aliases-package异常:", e); } return typeAliasesPackage; } protected abstract String getTypeAliasesPackageKey(); protected abstract String getMapperLocationsKey(); protected SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { // 改用SpringBootVFS,解决war包扫描bug VFS.addImplClass(SpringBootVFS.class); String typeAliasesPackage = env.getProperty(getTypeAliasesPackageKey()); String mapperLocations = env.getProperty(getMapperLocationsKey()); // 处理默认的typeAliasesPackage String dealPackage = setTypeAliasesPackage(typeAliasesPackage); final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(dealPackage); sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations)); DefaultSqlSessionFactory ssf = (DefaultSqlSessionFactory) sessionFactory.getObject(); assert ssf != null; Configuration config = ssf.getConfiguration(); // 设置下划线转驼峰配置和默认的枚举处理 config.setMapUnderscoreToCamelCase(true); config.setDefaultEnumTypeHandler(org.apache.ibatis.type.EnumOrdinalTypeHandler.class); return ssf; } }
配置事务管理器:可以使用 Spring 的事务管理器来管理多个数据源的事务。例如,可以配置一个 ChainedTransactionManager 对象,将多个事务管理器(如 DataSourceTransactionManager)串联起来
其中,@EnableTransactionManagement 注解用于启用 Spring 的事务管理功能。
package cc.eslink.villagewater.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Configuration; import org.springframework.data.transaction.ChainedTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.context.annotation.Bean; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.annotation.Resource; import javax.sql.DataSource; /** * 配置事务管理器 * * @Author : wxj * @Date : 2023/3/9 10:59 */ @Configuration @EnableTransactionManagement public class TransactionConfig { @Resource @Qualifier("vwDataSource") private DataSource vwDataSource; @Resource @Qualifier("bmDataSource") private DataSource bmDataSource; @Bean public PlatformTransactionManager transactionManager() { DataSourceTransactionManager vwManager = new DataSourceTransactionManager(vwDataSource); DataSourceTransactionManager bmManager = new DataSourceTransactionManager(bmDataSource); return new ChainedTransactionManager(vwManager, bmManager); } }

@Transactional(rollbackFor = Exception.class)
@Override
public void dataReport(String ownership) {
List<VwDataAuditRecordDto> dataList = dataAuditRecordDao.queryNoReportData(ownership);
//插入审核数据
bmMonitorDataDao.batchInsert(dataList);
//插入站点信息,重复站点更新,新增站点插入
List<VwStation> stationList = vwStationDao.queryStationPageList(ownership,null,null,null,null,null);
bmStationDao.insertAndUpdate(stationList);
//更新上报状态为已上报
dataAuditRecordDao.updateReportFlag(dataList.stream().map(VwDataAuditRecordDto::getId).collect(Collectors.toList()));
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。