dynamic-datasource-spring-boot-starte v3.4.0
怎么集成dynamic
多数据源在项目文档写的很清楚 文档地址
DatasourceConfig
import com.baomidou.dynamic.datasource.provider.AbstractJdbcDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.geniuses.common.constant.SmartFormConst;
import com.geniuses.datasource.DataSourceConstant;
import com.geniuses.datasource.web.entity.DataSourceEntity;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
/**
* Description: 项目启动加载数据源<br>
* Date: 2021-6-8 17:18<br>
*
* @author moon
* @since 1.0.0
*/
@Configuration
public class DatasourceConfig {
/**
* 数据源自定义加载
*
* @param properties 数据源配置(某些idea中properties字段可能会爆红,忽略即可,不影响使用 )
* @return DynamicDataSourceProvider
*/
@Bean
public DynamicDataSourceProvider dynamicDataSourceProvider(DynamicDataSourceProperties properties) {
Map<String, DataSourceProperty> datasourceMap = properties.getDatasource();
DataSourceProperty masterDataSourceProperty = datasourceMap.get("master");
// 加载主数据源(yml中配置的数据源)
return new AbstractJdbcDataSourceProvider(masterDataSourceProperty.getDriverClassName(), masterDataSourceProperty.getUrl(), masterDataSourceProperty.getUsername(), masterDataSourceProperty.getPassword()) {
@Override
protected Map<String, DataSourceProperty> executeStmt(Statement statement) throws SQLException {
// 该地方只能使用statement操作数据库(数据源未加载,不能注入service操作数据库)
// 这里的数据源列表来源可以是任何地方
ResultSet resultSet = statement.executeQuery("select pk_id,username,password,full_url from auth_data_source ");
while (resultSet.next()){
String name = resultSet.getString("pk_id");
String username = resultSet.getString("username");
String password = DataSourceEntity.dataSourcePassDecrypt(resultSet.getString("password"));
String url = resultSet.getString("full_url");
String driver = DataSourceConstant.JDBC_DRIVER;
DataSourceProperty property = new DataSourceProperty();
property.setUsername(username);
property.setPassword(password);
property.setUrl(url);
property.setDriverClassName(driver);
datasourceMap.put(name, property);
}
return datasourceMap;
}
};
}
}
该方法有个弊端: 如果有一个数据源连接失败,项目启动失败
DsProcessor
实现数据源懒加载(用到的时候去数据库查询,添加进数据源)该方法可避免第一种方案的数据源连接失败导致的项目启动失败问题
MyDsProcessor
继承DsSpelExpressionProcessor
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.processor.DsSpelExpressionProcessor;
import com.geniuses.datasource.DataSourceConstant;
import com.geniuses.datasource.web.service.IDataSourceOnlineService;
import com.geniuses.datasource.web.service.IDataSourceService;
import lombok.Data;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Set;
/**
* Description: 自定义DsProcessor <br>
* Date: 2021-6-11 10:08<br>
*
* @author moon
* @since 1.0.0
*/
@Data
public class MyDsProcessor extends DsSpelExpressionProcessor {
/**
* 查询数据源连接池信息的接口,根据文档中controller改造
*/
@Autowired
private IDataSourceOnlineService dataSourceOnlineService;
/**
* 项目中存储数据源信息的service
*/
@Autowired
private IDataSourceService dataSourceService;
/**
* @DS 注解中spel表达式的前缀,该值可自定义,想要使用该processor注解中的前缀就要和该值相同
*/
private static final String HEADER_PREFIX = "#my.";
@Override
public boolean matches(String key) {
return key.startsWith(HEADER_PREFIX);
}
@Override
public String doDetermineDatasource(MethodInvocation invocation, String key) {
String name = super.doDetermineDatasource(invocation, key.replace("my.",""));
if (name != null && !StrUtil.equals(name, DataSourceConstant.PRIMARY_DATA_SOURCE_NAME, true)) {
Set<String> nameSet = dataSourceOnlineService.onlineDataSource();
if (!nameSet.contains(name)) {
synchronized (HEADER_PREFIX) {
// 加锁防止重复添加同一个数据源
nameSet = dataSourceOnlineService.onlineDataSource();
if (!nameSet.contains(name)) {
// 添加该数据源
dataSourceOnlineService.add(dataSourceService.getById(name),true);
}
}
}
}
return name;
}
}
DataSourceOnlineServiceImpl
主要方法@Service
public class DataSourceOnlineServiceImpl implements IDataSourceOnlineService {
@Autowired
private DataSource dataSource;
@Autowired
@SuppressWarnings("all")
private DefaultDataSourceCreator dataSourceCreator;
@Override
public Set<String> add(DataSourceEntity entity, boolean passEncrypted) {
DataSourceProperty dataSourceProperty = this.getDataSourceProperty(entity, passEncrypted);
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
ds.addDataSource(entity.getId(), dataSource);
return ds.getCurrentDataSources().keySet();
}
private DataSourceProperty getDataSourceProperty(DataSourceEntity entity, boolean passEncrypted) {
DataSourceProperty dataSourceProperty = new DataSourceProperty();
dataSourceProperty.setDriverClassName(DatasourceTypeEnum.getDriverClassName(entity.getDatabaseType()));
dataSourceProperty.setUsername(entity.getUsername());
if (passEncrypted) {
// 解密
dataSourceProperty.setPassword(DataSourceEntity.dataSourcePassDecrypt(entity.getUsrpwd()));
} else {
dataSourceProperty.setPassword(entity.getUsrpwd());
}
dataSourceProperty.setUrl(entity.getFullUrl());
dataSourceProperty.setPoolName(entity.getId());
return dataSourceProperty;
}
}
@DS("#my.dataSource")
@Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRES_NEW)
public void dsTest(){}