作者简介:大家好,我是撸代码的羊驼,前阿里巴巴架构师,现某互联网公司CTO
联系v:sulny_ann(17362204968),加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
在《Spring Boot中JdbcTemplate源码分析》中讲到了自动配置相关的源代码实现。基于Spring Boot自动配置默认配置的组件,我们可以来自定义JdbcTemplate的实例化。而多数据源的配置就是在此基础上实例化多个数据源和JdbcTemplate。
下面,我们来看具体的源代码实现。
依赖类库
关于依赖类库与集成JdbcTemplate时的一样,Spring Boot版本2.2.2.RELEASE。
相关pom依赖如下:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<!--数据库连接相关--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency></dependencies>
spring-boot-starter-jdbc是集成jdbc的依赖,mysql-connector-java是基于mysql的依赖类库。lombok是简化代码的工具类,如果不需要可去掉,并去掉类中相关的注解。
spring-boot-starter-test为单元测试依赖的类库,这里单元测试使用的是junit5,注意使用方法与junit4差别比较大。
配置application
application.properties配置如下:
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/spring?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=truespring.datasource.primary.username=rootspring.datasource.primary.password=root_123spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/spring1?serverTimezone=UTC&useUnicode=true\ &characterEncoding=utf-8&useSSL=truespring.datasource.secondary.username=rootspring.datasource.secondary.password=root_123spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
既然是多数据源,肯定要配置多个数据源的配置。与单数据源配置基本形式差不多。
需要注意的是第一个配置项的key为:spring.datasource.primary.jdbc-url。与单数据源时使用的spring.datasource.url有所区别。不然,启动时会抛出异常。
多数据源对应的Java配置
下面就需要我们自己来实例化DataSource和JdbcTemplate。相关的实例化也可参看源码解析文章中Spring Boot的实例化方式。
这里,我们的实现如下:
@Configurationpublic class DataSourceConfig {
@Primary @Bean(name = "primaryDataSource") @ConfigurationProperties(prefix="spring.datasource.primary") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); }
@Bean(name = "secondaryDataSource") @ConfigurationProperties(prefix="spring.datasource.secondary") public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); }
@Bean(name="primaryJdbcTemplate") public JdbcTemplate primaryJdbcTemplate (@Qualifier("primaryDataSource") DataSource dataSource ) { return new JdbcTemplate(dataSource); }
@Bean(name="secondaryJdbcTemplate") public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); }
}
@Configuration声明该类为配置类。
@Primary指定当出现多个相同类型的实例化对象时,以该注解标注的为默认的。
@Bean实例化Bean,并将其注入到容器当中。这里分别实例化了primaryDataSource和secondaryDataSource两个DataSource,以Bean的名称来区分。
@ConfigurationProperties将前缀为spring.datasource.primary和前缀为spring.datasource.secondary的配置属性设置到对应的DataSource中。
随后实例化了两个JdbcTemplate,直接通过new关键字创建,并且把对应的DataSource作为构造参数传入。
经过该配置文件的配置,便有了两个JdbcTemplate。
实体类
实体类如下:
@Datapublic class Order {
private int id;
private String orderNo;
private int amount;}
@Data为Lombok的注解,自动生成一些默认的方法,比如属性的getter/setter方法。
数据库
关于数据库的DDL如下:
CREATE TABLE `tb_order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `amount` int(11) NOT NULL DEFAULT '1', `order_no` varchar(64) NOT NULL DEFAULT '', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
同样的表在两个数据库中进行创建。
接口定义
定义OrderService接口:
public interface OrderService {
/** * 创建订单 * @param order 订单信息 * @return 记录数 */ int save(Order order);
/** * 保存到指定库 * @param order 订单信息 * @param jdbcTemplate jdbc * @return */ int save(Order order, JdbcTemplate jdbcTemplate);}
接口实现:
@Service("orderService")public class OrderServiceImpl implements OrderService {
@Resource @Qualifier("primaryJdbcTemplate") private JdbcTemplate jdbcTemplate;
@Override public int save(Order order) { return jdbcTemplate.update("insert into tb_order(order_no, amount) values(?, ?)", order.getOrderNo(), order.getAmount()); }
@Override public int save(Order order, JdbcTemplate secJdbcTemplate) { if (secJdbcTemplate != null) { return secJdbcTemplate.update("insert into tb_order(order_no, amount) values(?, ?)", order.getOrderNo(), order.getAmount()); } else { return jdbcTemplate.update("insert into tb_order(order_no, amount) values(?, ?)", order.getOrderNo(), order.getAmount()); } }}
在实现方法中,默认注入了主库的JdbcTemplate,同时在原来的save方法中新增了一个JdbcTemplate参数,可以根据是否传递该新的JdbcTemplate来决定使用哪个JdbcTemplate。
当然在此方法内也可以使用一个JdbcTemplate,然后根据参数动态的修改该JdbcTemplate指向的具体实现类。可以根据具体情况进行灵活运用。
单元测试
单元测试类如下:
@Slf4j@SpringBootTestclass OrderServiceTest {
@Resource private OrderService orderService;
@Resource @Qualifier("primaryJdbcTemplate") private JdbcTemplate primaryJdbcTemplate;
@Resource @Qualifier("secondaryJdbcTemplate") private JdbcTemplate secondaryJdbcTemplate;
@Test void save() { Order order = new Order(); order.setOrderNo("N003"); order.setAmount(10000); orderService.save(order, primaryJdbcTemplate); orderService.save(order, secondaryJdbcTemplate); }}
执行以上单元测试,两个库中的tb_order表分别插入了一条数据。关于其他增删改查操作,可参考保存方法进行扩展。