Why can't methods in Mybatis Mapper be overloaded?

catalogue

  • preface
  • Environment configuration
  • Error demonstration
  • Why not reload?
  • How to find the corresponding SQL in XML?
  • summary

preface

  • When I first started Mybatis, I may have made a mistake, that is, I overloaded the methods when writing Mapper interface, but always reported errors when running. At that time, I was really depressed, but I couldn't find out the reason. I had to change the method name silently. Ha ha, what a humble operation.
  • Today, I will write an article to solve your doubts from the perspective of source code. Why can't the methods in Mybatis be overloaded?

Environment configuration

  • Everything in this article is based on mybatis 3 5 and springboot-2.3.3 RELEASE.

Error demonstration

  • Take chestnuts for example: suppose there are two requirements, one is to filter users according to their id and the other is to filter users according to their gender. At this time, the overloaded methods in Mapper are as follows:
public interface UserMapper {
    List<UserInfo> selectList(@Param("userIds") List<String> userIds);

    List<UserInfo> selectList(Integer gender);
    }
  • There is no error in this, but start the project and report the following error:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse mapping resource: 'file [H:\work_project\demo\target\classes\mapper\UserInfoMapper.xml]'; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'file [H:\work_project\demo\target\classes\mapper\UserInfoMapper.xml]'. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for cn.cb.demo.dao.UserMapper.selectList. please check file [H:\work_project\demo\target\classes\mapper\UserInfoMapper.xml] and file [H:\work_project\demo\target\classes\mapper\UserInfoMapper.xml]
 at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655)
 at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:635)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
 at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
 at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1307)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1509)
 ... 81 more
  • What does that mean? Stupid~

  • General meaning: CN cb. demo. dao. UserMapper. The id of selectlist already exists, which leads to the failure of creating sqlSessionFactory.

Why not reload?

  • Through the above exception prompt, you can know that the creation of sqlSessionFactory failed. This must be familiar. As the name suggests, it is the factory that creates SqlSession.
  • Springboot and Mybatis will have an automatic configuration class of initiator, MybatisAutoConfiguration, in which a piece of code is to create sqlSessionFactory, as shown in the following figure:
  • Since the creation failed, there must be an exception here. The "general idea" here is:

How do the < Select > and < Insert > tags in the XML file correspond to the < Select > and < Insert > tags in the XML file one by one? Let's explain them one by one.

  • As shown in the above figure, during the creation of SqlSessionFactory, the previous part of the code is to set some configurations and does not involve parsing the content of XML, so the answer must be return factory in the last line getObject();, So I put a breakpoint here and look at it a little bit. So I went all the way to org mybatis. spring. In the sqlsessionfactorybean#buildsqlsessionfactory method, one of the codes is as follows:
  • Here's xmlmapperbuilder parse(); Is to parse the XML file and Mapper interface, and continue to look down.
  • Skip unimportant code at org apache. ibatis. builder. xml. There is a line of important code in the xmlmapperbuilder #configurationelement method, as shown in the following figure:
  • Here we start to build MappedStatement according to the tags of select, insert, update and delete in the XML file. Keep following in.
  • Skip the unimportant code and see org apache. ibatis. builder. Mapperbuilderassistant#addmappedstatement the return value of this method is MappedStatement. Needless to say, it must be this method. Take a closer look, you can clearly see the code of building id, as shown in the following figure:
  • As can be seen from the above figure, the code for creating ID is id = applyCurrentNamespace(id, false);, The specific implementation is shown in the figure below:

The code in the figure above is clear. id in MappedStatement = mapper's full class name + '+ Method name. If overloaded, there must be a MappedStatement with the same id.

  • In fact, this does not mean that the method cannot be overloaded. If it is repeated, it will be repeated. There is no conflict. Here we need to look at a structure, as follows:
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
      .conflictMessageProducer((savedValue, targetValue) ->
          ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
  • The constructed mappedStatements will be stored in mappedStatements, as shown in the following code:
public void addMappedStatement(MappedStatement ms) {
    //key is id 
    mappedStatements.put(ms.getId(), ms);
  }
  • The put(k,v) method of StrictMap is as follows:

It should be understood here. Now the exception thrown corresponds to the above exception information. This StrictMap does not allow duplicate keys, and the stored key is the id. Therefore, methods in Mapper cannot be overloaded.

How to find the corresponding SQL in XML?

  • When using Mybatis, simply call the method in Mapper to execute SQL, as shown in the following code:
List<UserInfo> userInfos = userMapper.selectList(Arrays.asList("192","198"));

How to find the corresponding SQL by simply calling one line? In fact, it is to find the corresponding MappedStatement from map < string, MappedStatement > mappedstatements according to the id.

  • At org apache. ibatis. session. defaults. The defaultsqlsession#selectlist method has this line of code, as shown in the following figure:
  • MappedStatement ms = configuration.getMappedStatement(statement); This line of code is to obtain the corresponding mappedstatement from mappedStatements according to the id. the source code is as follows:
public MappedStatement getMappedStatement(String id) {
    return this.getMappedStatement(id, true);
  }

summary

  • After writing this article, it must be clear why the methods in Mapper cannot be overloaded. In the final analysis, it is because this id=Mapper's full class name + '+ Method name.
  • If you think the author has written well and gained something, pay attention to it, share a wave, and pay attention to the first-hand article push of wechat public number ape technology column!!!

Tags: Java Mybatis

Posted by sales@gmba.dk on Tue, 17 May 2022 15:30:09 +0300