Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to solve the error problem caused by springboot mybatis calling multiple data sources

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article is about how to solve the problem of errors caused by springboot mybatis calling multiple data sources. The editor thinks it is very practical, so share it with you as a reference and follow the editor to have a look.

Springboot mybatis calls multiple data sources to report an error

'org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker': Invocation of init method failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type' javax.sql.DataSource' available: more than one 'primary' bean found among candidates: [mssqlDataSource, postgreDataSource]

Copied from back to front, the bold one is the key point.

This error occurs because multiple data sources use the same mapper interface, but all use @ Primary.

As shown in the figure:

You can see from the above two figures that the same mapper interface is used, and @ Primary is added to both.

Solution method

There are two solutions. One is to remove @ Primary from one of the data sources and call the data source dynamically, that is, the data source that needs to be switched by code.

If you want to use two data sources at the same time, use different mapper, which is equivalent to postgre using the mapper,sqlserver of the postgre part and the mapper of the sqlserver part. Everyone does not interfere with each other, even if @ primary is fine.

As shown in the picture, I changed the MapperScan of postgre

The structure of springboot-mybatis multi-data source and trample springboot project is as follows

The content of springboot configuration file is as follows

The configuration classes for dynamic data sources are as follows

(must be able to be scanned by ComponentScan):

Package com.letzgo.config;import com.alibaba.druid.pool.DruidDataSource;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.SqlSessionTemplate;import org.mybatis.spring.annotation.MapperScan;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.core.io.support.PathMatchingResourcePatternResolver Import org.springframework.jdbc.datasource.DataSourceTransactionManager;import javax.sql.DataSource / * * @ author allen * @ date 2019-01-10 15:08 * / public class DynamicDatasourceConfig {@ Configuration @ MapperScan (basePackages = "com.letzgo.dao.master") public static class Master {@ Primary @ Bean ("masterDataSource") @ Qualifier ("masterDataSource") @ ConfigurationProperties (prefix = "spring.datasource.master") public DataSource dataSource () {return new DruidDataSource () } @ Primary @ Bean ("masterSqlSessionFactory") @ Qualifier ("masterSqlSessionFactory") public SqlSessionFactory sqlSessionFactory (@ Qualifier ("masterDataSource") DataSource dataSource) throws Exception {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean (); factoryBean.setDataSource (dataSource); factoryBean.setMapperLocations (new PathMatchingResourcePatternResolver (). GetResources ("classpath:mapper/master/*.xml")); return factoryBean.getObject () } @ Primary @ Bean ("masterTransactionManager") @ Qualifier ("masterTransactionManager") public DataSourceTransactionManager transactionManager (@ Qualifier ("masterDataSource") DataSource dataSource) {return new DataSourceTransactionManager (dataSource);} @ Primary @ Bean ("masterSqlSessionTemplate") @ Qualifier ("masterSqlSessionTemplate") public SqlSessionTemplate sqlSessionTemplate (@ Qualifier ("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate (sqlSessionFactory) } @ Configuration @ MapperScan (basePackages = "com.letzgo.dao.slave") public static class Slave {@ Bean ("slaveDataSource") @ Qualifier ("slaveDataSource") @ ConfigurationProperties (prefix = "spring.datasource.slave") public DataSource dataSource () {return new DruidDataSource () } @ Bean ("slaveSqlSessionFactory") @ Qualifier ("slaveSqlSessionFactory") public SqlSessionFactory sqlSessionFactory (@ Qualifier ("slaveDataSource") DataSource dataSource) throws Exception {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean (); factoryBean.setDataSource (dataSource); factoryBean.setMapperLocations (new PathMatchingResourcePatternResolver (). GetResources ("classpath:mapper/slave/*.xml")); return factoryBean.getObject () @ Bean ("slaveTransactionManager") @ Qualifier ("slaveTransactionManager") public DataSourceTransactionManager transactionManager (@ Qualifier ("slaveDataSource") DataSource dataSource) {return new DataSourceTransactionManager (dataSource);} @ Bean ("slaveSqlSessionTemplate") @ Qualifier ("slaveSqlSessionTemplate") public SqlSessionTemplate sqlSessionTemplate (@ Qualifier ("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate (sqlSessionFactory);}

After completing the basic configuration, write a database access operation in master and slave respectively, and then open two simple interfaces to trigger the data access operation of master and slave respectively.

So far, no basic structure of the project has been built, start the project and test it.

We will find that the database access of master can be accessed normally, but the database operation of slave is not allowed. The error message is as follows:

Org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): *

At first, we tried to solve such an error through Baidu. Most of them said that the namespace of the xml file did not correspond to the full name of the dao interface, or that the interface method did not correspond to the method in xml, and so on.

I checked my code many times and rebooted many times, but it is not that these methods are wrong, but the problems in this case are not caused by these problems. In the end, I had no choice but to look at the source code, and finally found the problem.

Debug source code debugging to the end, found that whether to perform mater or slave database operations, using the same SqlSession, the same! There must be something wrong with this.

Continue to check the source code to see the injection process of SqlSession.

We know that as long as mybatis writes the interface without writing the implementation class (it should be later than 3.0), it actually uses a proxy, and each dao interface actually corresponds to a MapperFactoryBean in the spring container (those who do not understand FactoryBean can take a look at some of the core interfaces of spring, which you must know if you want to understand the spring source code).

When the bean is obtained from the container, the getObject method of MapperFactoryBean produces a proxy class of the MapperProxy object based on the SqlSession instance.

The crux of the problem is MapperFactoryBean, which inherits the SqlSessionDaoSupport class, has one property, SqlSession, and this is the SqlSession instance on which the proxy class is created. Then we can see when the SqlSession instance was injected, and we can find out why the same object was injected.

Look for the place where spring is injected, the method of spring injection. At present, individuals know that there are annotation processors, such as @ Autowired's annotation processor AutowiredAnnotationBeanPostProcessor, and other similar BeanPostProcessor interface implementation classes, and another is the injection mode of definer attributes in BeanDefinition, which is determined in the definition stage of bean. If the former does not know, you can take a look at it. I will not repeat it here. The source code of the latter process is as follows (only the core part is intercepted. If you are interested, you can take a look at the process for yourself. the call chain is relatively deep, and the code will be posted more, dazzling):

The BeanDefinition of the debug to dao interface class (as mentioned above is actually MapperFactoryBean), and found that his autowiremode is 2. Refer to the source code.

It can be found to be automatically assembled according to the type.

Here comes the most important part.

When debug, it is found that the dao interface of master is executed into the this.autowireByType (beanName, mbd, bw, newPvs) method, and the instance injected into the SqlSession attribute in MapperFactoryBean is the masterSqlSessionTemplate object.

When the dao interface of slave executes this method, the masterSqlSessionTemplate object is also injected. Injection is found in the spring container by type. (at this time, slaveSqlSessionTemplate is also in the container. Why did you find masterSqlSessionTemplate by type injection but did not report an error? it should be the function of @ Primary)

At this point, the cause of the problem has been basically found, so how to solve it? Why is BeanDefinition defined as autowiremode=2? you can only find @ MapperScan to look at the source code for processing this annotation, and finally find the following methods of ClassPathMapperScanner:

Private void processBeanDefinitions (Set beanDefinitions) {Iterator var3 = beanDefinitions.iterator (); while (var3.hasNext ()) {BeanDefinitionHolder holder = (BeanDefinitionHolder) var3.next (); GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition () If (this.logger.isDebugEnabled ()) {this.logger.debug ("Creating MapperFactoryBean with name'" + holder.getBeanName () + "'and'" + definition.getBeanClassName () + "mapperInterface");} definition.getConstructorArgumentValues () .addGenericArgumentValue (definition.getBeanClassName ()); definition.setBeanClass (this.mapperFactoryBean.getClass ()) Definition.getPropertyValues () .add ("addToConfig", this.addToConfig); boolean explicitFactoryUsed = false; if (StringUtils.hasText (this.sqlSessionFactoryBeanName)) {definition.getPropertyValues () .add ("sqlSessionFactory", new RuntimeBeanReference (this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true } else if (this.sqlSessionFactory! = null) {definition.getPropertyValues (). Add ("sqlSessionFactory", this.sqlSessionFactory); explicitFactoryUsed = true;} if (StringUtils.hasText (this.sqlSessionTemplateBeanName)) {if (explicitFactoryUsed) {this.logger.warn ("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. SqlSessionFactory is ignored. ");} definition.getPropertyValues (). Add (" sqlSessionTemplate ", new RuntimeBeanReference (this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true;} else if (this.sqlSessionTemplate! = null) {if (explicitFactoryUsed) {this.logger.warn (" Cannot use both: sqlSessionTemplate and sqlSessionFactory together. SqlSessionFactory is ignored. ");} definition.getPropertyValues () .add (" sqlSessionTemplate ", this.sqlSessionTemplate); explicitFactoryUsed = true } if (! explicitFactoryUsed) {if (this.logger.isDebugEnabled ()) {this.logger.debug ("Enabling autowire by type for MapperFactoryBean with name'" + holder.getBeanName () + "'.");} definition.setAutowireMode (2);}

Line 44 is the key, but there is one condition that holds because the @ MapperScan annotation does not specify sqlSessionTemplateRef or sqlSessionFactoryRef, and because no specific sqlSessionTemplate or sqlSessionFactory,mybatis is specified, it is automatically assembled by type by default.

So far, the solution to the problem has come up with:

The two @ MapperScan uses in the code are changed to:

@ MapperScan (basePackages = "com.letzgo.dao.master", sqlSessionFactoryRef = "masterSqlSessionFactory", sqlSessionTemplateRef = "masterSqlSessionTemplate") @ MapperScan (basePackages = "com.letzgo.dao.slave", sqlSessionFactoryRef = "slaveSqlSessionFactory", sqlSessionTemplateRef = "slaveSqlSessionTemplate")

Restart for testing, and the problem is resolved.

Thank you for reading! This is the end of this article on "how to solve the problem of errors caused by springboot mybatis calling multiple data sources". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, you can share it for more people to see!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report