Integration of Mybatis into spring principles

Mybatis integrated into spring

SqlSessionTemplate integration

Spring provides the SqlSessionTemplate class, which is used to inject instances of this class into spring for sql operations. This class implements the sqlsession interface and directly calls the sqlsession method to execute sql. This class holds the SqlSessionFactory class and creates a new sqlsession every time the method is executed.

  • The SqlSessionTemplate class structure is shown in the following figure. SqlSessionInterceptor is an internal dynamic proxy class, and the sqlSessionProxy proxy object executes the invoke method of the internal class SqlSessionInterceptor.
  • sqlSessionProxy is a dynamic proxy class that implements the SqlSession interface. The selectOne and other methods of SqlSessionTemplate actually execute the invoke method of the proxy class
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
  • The proxy class SqlSessionInterceptor generates a new sqlsession object every time it executes invoke. This is where the thread safety of each sqlsession is guaranteed.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//Get a new sqlSession each time
   SqlSession sqlSession = 
   SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
	...
}
  • When using, SqlSessionTemplate needs to be injected every time. For convenience, you can inherit SqlSessionDaoSupport class. This class holds an instance of SqlSessionTemplate.

MapperScanConfiguration auto injection integration

It is still inconvenient to use the SqlSessionTemplate method above. You can use @ Autowired directly after injection by defining Mapper interface. In this way, how to get the proxy object and execute the corresponding sql?

  • MapperScanConfiguration class implements the postProcessBeanDefinitionRegistry method of BeanDefinitionRegistryPostProcessor. In the method, Mapper class is scanned and encapsulated into BeanDefinition. After obtaining all Mapper beandefinitions, execute the setBeanClass method of BeanDefinition, and modify beanClass to MapperFactoryBean.class, Therefore, the MapperFactoryBean class is finally registered in the BeanDefinition container. Each MapperFactoryBean also holds a corresponding Mapper class. This step can change the Mapper interface that cannot be instantiated into a MapperFactoryBean that can be instantiated, and associate the corresponding Mapper with the proxy object created later.
//Execution path postprocessbeandefinitionregistry#classpathmappercanner#doscan#processbeandefinitions

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
          + "' mapperInterface");

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
//Modify the class type of BeanDefinition of the corresponding Mapper interface class to MapperFactoryBean type.
      definition.setBeanClass(this.mapperFactoryBeanClass);

      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) {
          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) {
          LOGGER.warn(
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }

      if (!explicitFactoryUsed) {
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      definition.setLazyInit(lazyInitialization);
    }
  }
  • MapperFactoryBean implements the FactoryBean interface, so in the spring container instantiation phase, the instantiated bean is the object returned from the getObject method. MapperFactoryBean inherits SqlSessionDaoSupport. You can get the SqlSession. See the previous Mybatis execution process The Mapper proxy object can be obtained, so the Mapper proxy object generated by MapperProxy is actually registered in the IoC container.
 @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  • When in use, spring will inject the corresponding Mapper proxy object and execute the invoke method of MapperProxy to operate sq.

Tags: Java Mybatis Spring

Posted on Fri, 19 Nov 2021 21:08:52 -0500 by veronicabend