MyBatis integrated into Spring

Use of SqlSession MyBatis

MyBatis provides execute SQL statements, commit or rollback transactions and acquire mapper instances method. SqlSession created by the class factory SqlSessionFactory, SqlSessionFactory is created SqlSessionFactoryBuilder class constructor.

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

Use SqlSession mybatis-spring of

When using the integrated mybatis-spring Spring, SqlSessionFactory use the implementation class SqlSessionFactoryBean FactoryBean of Spring calls SqlSessionFactoryBuilder indirectly to create. SqlSession replaced by its thread-safe implementation class SqlSessionTemplate, it can automatically commit, rollback, closed session based transaction mechanism of Spring. To use SqlSessionTemplate Spring container, it must be poured into the container.

// 注入 SqlSessionTemplate
@Bean
public SqlSessionTemplate sqlSession() throws Exception {
    return new SqlSessionTemplate(sqlSessionFactory());
}

public SqlSessionFactory sqlSessionFactory() throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    // 指定数据源连接信息
    factoryBean.setDataSource(dataSource());
    // 指定 mapper 文件路径
    InputStream inputStream = Resources.getResourceAsStream("mapper/UserSpringMapper.xml");
    factoryBean.setMapperLocations(new InputStreamResource(inputStream));
    return factoryBean.getObject();
}

// 使用 Spring 事务机制
@Bean
PlatformTransactionManager getTransactionManager() {
    return new DataSourceTransactionManager(dataSource());
}

Use mybatis-spring-boot-starter automatic injector

If Springboot, by introducing mybatis-spring-boot-starterthe MyBatis components automatically injected into Spring container, the starter will be introduced mybatis-spring-boot-autoconfigure(see how to develop their own Springboot starter), the package which has a significant configuration class MybatisAutoConfiguration, by looking at its source code can be seen, it there are two static inner class MapperScannerRegistrarNotFoundConfiguration, AutoConfiguredMapperScannerRegistrarwherein, MybatisAutoConfigurationand MapperScannerRegistrarNotFoundConfigurationare added to @Configuration Spring annotation, so they will be loaded into the container when Spring started, and AutoConfiguredMapperScannerRegistrarby MapperScannerRegistrarNotFoundConfigurationthe annotation @Import indirectly injected into the vessel.

AutoConfiguredMapperScannerRegistrarAchieved ImportBeanDefinitionRegistrar, so the method registerBeanDefinitions () is executed when the container starts, the following two main effects:

  1. BeanFactory packet acquired from the scan path
  2. Initialization and configuration MapperScannerConfigurer (designated annotation type @Mapper, specify packet paths, etc.) to register BeanFactory

MapperScannerConfigurer realized BeanDefinitionRegistryPostProcessor, so its methods postProcessBeanDefinitionRegistry () will be executed when the container starts, initialize the subclass ClassPathBeanDefinitionScanner by this method ClassPathMapperScanner , call scan (String ... basePackages), scan package path @Mapper notes of all interfaces, registration to BeanFactory, followed by post-treatment:

  1. The type of modification is BeanDefinition MapperFactoryBean
  2. Specifies MapperFactoryBean constructor argument is the full class name of the interface class @Mapper
  3. Provided sqlSessionFactory, sqlSessionTemplate, according to the type autowiring
  4. Created by the reflection MapperFactoryBean instance, there are parameters which constructor to call, the incoming interface @Mapper, Class cached folders interface

As shown below: MapperFactoryBean inheritance relationship

Folders Factory Bean

Initialization and configuration parsing

DaoSupport realized InitializingBean.afterPropertiesSet (), by this method, the Mapper cached MapperRegistry the Map<Class<?>, MapperProxyFactory<?>> knownMappers, key to Mapper interfaces, value as factory Mapper proxy class MapperProxyFactory; Finally, MapperAnnotationBuilder.parse () to parse XML configuration file or annotated method, cached Configuration of Map<String, MappedStatement> mappedStatementssource code process is as follows:

DaoSupport.afterPropertiesSet()
->MapperFactoryBean.checkDaoConfig()
->Configuration.addMapper(this.mapperInterface)
->MapperRegistry.addMapper(type)
->knownMappers.put(type, new MapperProxyFactory<>(type))
// 解析 SQL 配置
->MapperAnnotationBuilder.parse()
-->configuration.addMappedStatement(statement)

Generate a proxy object

MapperFactoryBean of FactoryBean.getObject implements (), removed from the interface map MapperProxyFactory Mapper knownMappers cache, uses this to create a factory class MapperProxy proxy class, the MapperProxy<T> implements InvocationHandlerapparent use of the JDK dynamic proxy, the source process is as follows:

MapperFactoryBean.getObject()
->SqlSessionTemplate.getMapper(mapperInterface)
->Configuration.getMapper(mapperInterface, this)
->MapperRegistry.getMapper(mapperInterface, sqlSession)
->MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
->mapperProxyFactory.newInstance(sqlSession)

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

Here, the proxy object is generated, it can be simply applied by the Springboot @Autowired easily access interface proxy object Mapper (MapperProxy) from the vessel of the annotation.

Implementation process

Assuming that there is @Mapper UserDao class annotation.

@Mapper
public interface UserDao {
  @Select("select * from t_user where id = #{id}")
  Optional<UserEntity> findOne(String id);
}

Get Bean by @Autowired. As is clear from the above, actually it gets to a proxy object MapperProxy.

@Autowired
UserDao userDao;

A proxy object MapperProxy the invoke () method call UserDao method is actually executed.

// 调用 findOne
userDao.findOne(id);

// 实际执行的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

invoke () method substantially performs source process is as follows:

MapperMethod.execute(sqlSession, args)
sqlSessionProxy.selectOne(statement, parameter)

Note that SqlSessionin SqlSessionTemplate constructor parameters are initialized, and it is also a proxy class , is SqlSessionInterceptor agent

this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
    new Class[] { SqlSession.class }, new SqlSessionInterceptor());

The method is therefore selectOne SqlSessionInterceptor.invoke () intercepting the reflective performed SqlSession.selectOne () method, the source process is as follows:

private class SqlSessionInterceptor implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 打开获取 DefaultSqlSession;
    SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
        SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
    try {
      // 反射执行 SqlSession 的方法 selectOne(String statement, Object parameter) 进行查询
      Object result = method.invoke(sqlSession, args);
      if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
        // 提交
        sqlSession.commit(true);
      }
      // 返回查询结果
      return result;
    } catch (Throwable t) {
      // 异常时释放连接
      closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
    } finally {
      if (sqlSession != null) {
        // 释放连接
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
      }
    }
  }
}

Annotations and configuration files

Springboot application can also choose to use the annotation, or configuration files ways to use MyBatis, generally simple CRUD directly using annotations (such as @ Select, @ SelectProvider) can be, can reduce a lot of the configuration file; complex SQL may still use profile way more convenient to operate some specific still have to look at the actual situation to choose, note that each DAO mode annotations and configurations may exist, but the same method described in notes and configuration can not exist simultaneously.

If by way of the configuration file, you can specify the location of the configuration file in application.yml DAO configuration file:

# 使用基于配置文件的 MyBatis 时指定 Mapper 配置的路径
mybatis:
  mapper-locations: mapper/*Dao.xml

Guess you like

Origin www.cnblogs.com/bigshark/p/11374857.html