充电桩项目实战:搞定多数据源!

2024-01-29 10:28:14 浏览数 (4)

你好,我是田哥

最近,我在对充电桩项目进行微服务升级中,既然是项目升级,难免会遇到各种各样的问题。比如:分布式事务问题、多数据源问题、分布式锁问题等。

项目技术栈

  • Spring
  • Spring Boot
  • Spring Cloud Alibaba(Nacos、Gateway、openfeigin、Sentinel)
  • MongoDB Redis
  • EMQX
  • XXL-JOB
  • RabbitMQ
  • MySQL
  • Atomikos(分布式事务)
  • OSS

项目结构

项目结构

题外话:如果想年后找到更好的工作,推荐看这篇文章

Java后端面试复习规划表,5万字

因为对数据库进行拆分成了多个库,所以,肯定会涉及到比如分布式事务、多数据源等问题,关于分布式事务这采用Atomikos解决的,可以参考之前的文章:

Spring Boot MyBatis Atomikos MySQL(附源码)

分布式事务这里就不聊了,咱们来聊聊多数据源的问题。

多数据源

多数据源的场景通常有:

  • 1:主和从数据库数据源
  • 2:A项目中的数据库和B项目中的数据库
  • 3:A公司数据库和B公司数据库
主和从数据库数据源

主要是用于数据库架构变成了主从结构,通常会使用到注解:@Primary

在Spring框架中,@Primary注解用于指定一个Bean作为主要的候选者,当有多个相同类型的Bean可供选择时,标记为@Primary的Bean将优先被考虑。这在处理多个相同类型Bean的情况时非常有用,特别是在自动装配(Autowiring)时。

例如,假设您有一个接口MyService和两个实现类FirstServiceSecondService,并且都使用@Service注解进行了声明。如果没有使用@Primary注解,在进行自动装配时,Spring会抛出异常,因为无法确定应该选择哪个实现类。但是,如果其中一个实现类使用了@Primary注解,那么Spring就会选择这个Bean进行装配。

以下是一个示例:

代码语言:javascript复制
@Service
public class FirstService implements MyService {
    // ...
}

@Service
@Primary
public class SecondService implements MyService {
    // ...
}

在上面的代码中,由于SecondService使用了@Primary注解,因此Spring会自动装配SecondService作为MyService的实现类。

A项目中的数据库和B项目中的数据库

我的充电桩项目中就是这类场景,主要是对充电桩项目拆分了。

下面来看看实现过程。

实现

实现通常分为下面几步:

  • pom配置依赖
  • 配置数据源信息
  • 读取配置信息
  • 使用
pom配置依赖
代码语言:javascript复制
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.16</version>
</dependency>

跟普通Spring Boot项目集成MyBatis没什么区别。

properties配置
代码语言:javascript复制
spring.datasource.chargeuser.driverClassName = com.mysql.jdbc.Driver
spring.datasource.chargeuser.jdbc-url = jdbc:mysql://localhost:3306/charge-user?characterEncoding=utf8&useSSL=false&serverTimezone=GMT+8&allowPublicKeyRetrieval=true
spring.datasource.chargeuser.username = root
spring.datasource.chargeuser.password = 123456

spring.datasource.chargemarket.driverClassName = com.mysql.jdbc.Driver
spring.datasource.chargemarket.jdbc-url = jdbc:mysql://localhost:3306/charge-market?characterEncoding=utf8&useSSL=false&serverTimezone=GMT+8&allowPublicKeyRetrieval=true
spring.datasource.chargemarket.username = root
spring.datasource.chargemarket.password = 123456

我们这里配置了两个数据源,如果有需要我们可以照着这样配置即可。

数据源信息的装配
代码语言:javascript复制
@Configuration
@MapperScan(basePackages = "com.tian.mapper.market", sqlSessionTemplateRef  = "marketSqlSessionTemplate")
public class DataSourceMarketConfig {

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

    @Bean(name = "marketSqlSessionFactory")
    public SqlSessionFactory marketSqlSessionFactory(@Qualifier("marketDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(
                "classpath:mapper/market/*.xml"));
        return bean.getObject();
    }

    @Bean(name = "marketTransactionManager")

    public DataSourceTransactionManager marketTransactionManager(@Qualifier("marketDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "marketSqlSessionTemplate")
    public SqlSessionTemplate marketSqlSessionTemplate(@Qualifier("marketSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

注意:

  • 1:spring.datasource.chargemarket就是我们在properties文件配置数据源信息。
  • 2:com.tian.mapper.market 就是我们需要扫描的mapper
  • 3:classpath:mapper/market/*.xml 扫描的XxxxMapper.xml文件

然后,关于数据源spring.datasource.chargemarket 就这样搞定了,其他mapper的使用和普通mybatis没什么区别。

再来看看spring.datasource.chargeuser数据源的处理,和上面完全一毛一样。

代码语言:javascript复制
@Configuration
@MapperScan(basePackages = "com.tian.mapper.user", sqlSessionTemplateRef = "userSqlSessionTemplate")
public class DataSourceUserConfig {

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

    @Bean(name = "userSqlSessionFactory")
    public SqlSessionFactory userSqlSessionFactory(@Qualifier("userDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(
                "classpath:mapper/user/*.xml"));
        return bean.getObject();
    }

    @Bean(name = "userTransactionManager")
    public DataSourceTransactionManager userTransactionManager(@Qualifier("userDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "userSqlSessionTemplate")
    public SqlSessionTemplate userSqlSessionTemplate(@Qualifier("userSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

强调一下:项目中使用UserMapper方式并没有什么区别,还是以前Spring Boot MyBatis方式,所以这里就没有必要在展示相关无用的代码了。

多数据源配置就这么搞定了,学会(废)了吗?

总结

对上面多数据源的配置多一个简单的总结:

  • 1:数据库连接信息不一样(多数据源)
  • 2:扫描的mapper不一样和mapper.xml
  • 3:然后把对应的dataSourceSqlSessionFactoryTransactionManager以及SqlSessionTemplate关联起来。

最后在使用我们的XxxMapper时候就会自动给我们关联起来了相关的SqlSessionFactoryTransactionManager以及SqlSessionTemplat

1 人点赞