Java programmers who have been working for many years seem to use very few multiple data sources, especially in the current situation of microservices being so hot, it is normal for different businesses to access a database, and Java access to data sources is not as simple as PHP and other scripting languages, but in special business situations, we have to use multiple data sources. Today we will talk about it Talk about this topic
1, Application cases
Our database A is the main database, and other databases are configured in the main database. The number of slave databases B, C and D is not fixed. We will dynamically write the configuration to the main database according to the business needs and dynamically create A new database. That is to say, in the project, we only need to configure the data source of the main database, and other slave databases need to read the configuration from the main database and dynamically create the data source, which is dynamic Inject into the Spring container, dynamically switch the data source when using to realize the corresponding functional logic
2, Environment configuration
Springboot:2.0.4
Mybatis-plus:3.0.7.1
JDK:1.8
3, Program practice
1. Modification of project startup class
Add @ Import({DynamicDataSourceRegister.class}) annotation in the startup class to replace the default data source configuration
2. Code structure
The code structure is as follows:
@Component public class ApplicationContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ApplicationContextUtil.applicationContext = applicationContext; } /** * Gets the ApplicationContext stored in the static variable */ public static ApplicationContext getApplicationContext() { checkApplicationContext(); return applicationContext; } /** * Get the Bean from the static variable ApplicationContext and automatically transform it to the type of the assigned object */ public static <T> T getBean(String name) { checkApplicationContext(); if (applicationContext.containsBean(name)) { return (T) applicationContext.getBean(name); } return null; } /** * Get the Bean from the static variable ApplicationContext and automatically transform it to the type of the assigned object */ public static <T> T getBean(Class<T> clazz) { checkApplicationContext(); return (T) applicationContext.getBeansOfType(clazz); } private static void checkApplicationContext() { if (applicationContext == null) throw new IllegalStateException("applicaitonContext Uninjected,Please be there. applicationContext.xml Definition in SpringContextUtil"); } public synchronized static void registerSingletonBean(String beanName,Class clzz,Map<String,Object> original) { checkApplicationContext(); DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) ApplicationContextUtil.getApplicationContext().getAutowireCapableBeanFactory(); if(beanFactory.containsBean(beanName)){ removeBean(beanName); } GenericBeanDefinition definition = new GenericBeanDefinition(); //Class class definition.setBeanClass(clzz); //Attribute assignment definition.setPropertyValues(new MutablePropertyValues(original)); //Register to spring context beanFactory.registerBeanDefinition(beanName, definition); } public synchronized static void registerSingletonBean(String beanName, Object obj, Map<String,Object> original) { checkApplicationContext(); DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) ApplicationContextUtil.getApplicationContext().getAutowireCapableBeanFactory(); if(beanFactory.containsBean(beanName)){ removeBean(beanName); } GenericBeanDefinition definition = new GenericBeanDefinition(); //Class class definition.setBeanClass(obj.getClass()); //Attribute assignment definition.setPropertyValues(new MutablePropertyValues(original)); //Register to spring context beanFactory.registerBeanDefinition(beanName, definition); } public synchronized static void registerSingletonBean(String beanName,Object obj) { registerSingletonBean(beanName,obj,BeanUtils.transBean2Map(obj)); } /** * Delete bean s managed in spring * @param beanName */ public static void removeBean(String beanName){ ApplicationContext ctx = ApplicationContextUtil.getApplicationContext(); DefaultListableBeanFactory acf = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory(); if(acf.containsBean(beanName)) { acf.removeBeanDefinition(beanName); } } }
public class BeanUtils { public static Map<String, Object> transBean2Map(Object obj) { if(obj == null){ return null; } Map<String, Object> map = new HashMap<>(); try { BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass()); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor property : propertyDescriptors) { String key = property.getName(); // Filter class attribute if (!key.equals("class")) { // Get the getter method corresponding to property Method getter = property.getReadMethod(); Object value = getter.invoke(obj); map.put(key, value); } } } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) { e.printStackTrace(); } return map; } }
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } public void updateTargetDataSource(Map<String,DataSource> customDataSources){ Map<Object,Object> customDS=new HashMap<Object, Object>(); customDS.putAll(customDataSources); setTargetDataSources(customDS); afterPropertiesSet(); } }
public class DynamicDataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static List<String> dataSourceIds = new ArrayList<>(); public static String getDataSourceType() { return contextHolder.get(); } public static void setDataSourceType(String dataSourceType) { if(!containsDataSource(dataSourceType)){ DynamicDataSourceRegister.addSlaveDataSource(dataSourceType); } contextHolder.set(dataSourceType); } public static void clearDataSourceType() { contextHolder.remove(); } /** * Determine whether the specified datasroute currently exists */ public static boolean containsDataSource(String dataSourceId) { return dataSourceIds.contains(dataSourceId); } }
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class); //Default data source public static DataSource defaultDataSource; //User defined data source public static Map<String, DataSource> slaveDataSources = new HashMap<>(); public static BeanDefinitionRegistry beanDefinitionRegistry=null; public static String driverName; public static String userName; public static String password; public static String type; public static String url; @Override public void setEnvironment(Environment environment) { initDefaultDataSource(environment); } private void initDefaultDataSource(Environment env) { // Read master data source driverName=env.getProperty("spring.datasource.driver-class-name"); userName=env.getProperty("spring.datasource.username"); password=env.getProperty("spring.datasource.password"); type=env.getProperty("spring.datasource.type"); url=env.getProperty("spring.datasource.url"); Constant.defaultDbName="a"; Map<String, Object> dsMap = new HashMap<>(); dsMap.put("driver",driverName); dsMap.put("url",url); dsMap.put("username",userName); dsMap.put("password",password); dsMap.put("type",type); defaultDataSource = buildDataSource(dsMap); } @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { DynamicDataSourceContextHolder.dataSourceIds.add("dataSource"); Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); //Add default data source targetDataSources.put("dataSource", this.defaultDataSource); this.beanDefinitionRegistry=beanDefinitionRegistry; beanDefinitionRegistry(defaultDataSource,targetDataSources); logger.info("Dynamic DataSource Registry"); } public static void addSlaveDataSource(String dataSourceType){ BeanDefinition beanDefinition=beanDefinitionRegistry.getBeanDefinition("dataSource"); PropertyValue propertyValue=beanDefinition.getPropertyValues().getPropertyValue("targetDataSources"); Map<String,DataSource> oldTargetDataSource=(Map<String,DataSource>) propertyValue.getValue(); String newUrl=firstStr+dataSourceType+secondStr; Map<String, Object> dsMap = new HashMap<>(); dsMap.put("driver",driverName); dsMap.put("url",newUrl); dsMap.put("username",userName); dsMap.put("password",password); dsMap.put("type",type); DataSource ds = buildDataSource(dsMap); oldTargetDataSource.put(dataSourceType,ds); DynamicDataSource dynamicDataSource =ApplicationContextUtil.getBean("dataSource"); dynamicDataSource.updateTargetDataSource(oldTargetDataSource); DynamicDataSourceContextHolder.dataSourceIds.add(dataSourceType); } public void beanDefinitionRegistry(DataSource defaultDataSource,Map<Object,Object> targetDataSources){ //Create DynamicDataSource GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(DynamicDataSource.class); beanDefinition.setSynthetic(true); MutablePropertyValues mpv = beanDefinition.getPropertyValues(); mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource); if(targetDataSources.size()>0){ mpv.addPropertyValue("targetDataSources", targetDataSources); } //Registration - BeanDefinitionRegistry beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition); } public static DataSource buildDataSource(Map<String, Object> dataSourceMap) { try { Object type = dataSourceMap.get("type"); Class<? extends DataSource> dataSourceType; dataSourceType = (Class<? extends DataSource>) Class.forName((String) type); String driverClassName = dataSourceMap.get("driver").toString(); String url = dataSourceMap.get("url").toString(); String username = dataSourceMap.get("username").toString(); String password = dataSourceMap.get("password").toString(); // Custom DataSource configuration DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url) .username(username).password(password).type(dataSourceType); return factory.build(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } }
3. Examples
DynamicDataSourceContextHolder.setDataSourceType("B"); Integer lProductUv=dataVisitCollectionMapper.getProductUv(dDate); DynamicDataSourceContextHolder.setDataSourceType(Constant.defaultDbName);
When setting datasourcetype, judge whether this data source exists. If it exists, switch it directly. If it does not exist, create it dynamically and add it to the Spring container, so as to realize the purpose of creating data source dynamically
4, Summary and review
The main difference between this article and many other spring boot multi data source articles is that the configuration of the sub database is dynamically linked to each slave database according to the configuration in the main database when using, so as to realize a more flexible data source access experience. If you have better methods and suggestions, you can private mail. I'm Li Zhengfan. Thank you