Разговор о mybatis-spring-boot-starter

MyBatis

Введение

   Я до сих пор помню, как в прошлом году у меня было телефонное интервью.После того, как я представил интервьюеру стек технологий проекта, он вдруг спросил меня: как Springboot интегрировал mybatis? Я был в замешательстве в то время, как интегрировать? Просто ввести стартовую конфигурацию? Мне казалось, что он потерял дар речи, когда услышал мой ответ. Я недавно бездельничал, и вдруг вспомнил об этом деле, так что буду изучать эту проблему.


Портал:

Анализ исходного кода Mybatis (1) Инициализация MapperProxy

Анализ исходного кода Mybatis (2) MappedStatement

Анализ исходного кода Mybatis (3) Поддержка аннотаций

Анализ исходного кода Mybatis (4) Mybatis выполняет четыре компонента SQL

Анализ исходного кода Mybatis (5) Изображение показывает, как выполнять операторы SQL

Анализ исходного кода Mybatis (6) Механизм кэширования Mybatis

Анализ исходного кода Mybatis (7) Механизм плагинов Mybatis


2. Мибатис

2.1 Архитектурный дизайн

  Mybatis действительно отличный фреймворк. Его исходный код короткий и лаконичный, с использованием множества шаблонов проектирования, он очень подходит разработчикам в качестве отправной точки для чтения исходного кода. В то же время многослойность Mybatis также очень чистая Я не буду здесь подробно обсуждать, если выброшу тепловую карту в Интернете.

三层架构

  • Уровень интерфейса API, выставленные разработчикам, мы в основном используем Mapper.
  • уровень обработки данных, для реализации внутреннего процесса mybatis, включая поиск SQL, синтаксический анализ, выполнение и реализацию обработки сопоставления результатов и т. д.
  • слой базового модуля, предоставляя самую базовую функциональную поддержку, такую ​​как транзакции, кэширование, логирование и т. д.

2.2 Процесс выполнения

执行流程

2.2.1 Инициализация

  Mybatis находится в процессе инициализации,SqlSessionFactoryBuilder(режим строителя) вызоветXMLConfigBuilder(режим bulider) Чтение Mybatis-config.xml и всех файлов Mapper.xml,Создайте объект конфигурации основного объекта, который запускает Mybatis..然后将Configuration对象作为参数构建一个SqlSessionFactory(заводская выкройка) используется для созданияSqlSessionобъект.

2.2.2 SqlSession

Основная функция объекта   SqlSession заключается в завершении доступа к базе данных и отображении результатов.Классом реализации по умолчанию является DefaultSqlSession, который имеет два свойства, которые необходимо настроить:Configurationа такжеExecutor. Конфигурация — это основной объект операции Mybatis, упомянутой ранее. Все операции SqlSession с базой данных выполняются через Executor.

получатьSqlSessionПосле этого нам также нужно получить MapperMapperВыполните метод SQL.SqlSessionизgetMapperМетод заключается в том, чтобы связаться с приложением и ссылкой на Mybatis.Конечно, MyBatis получает экземпляр Mapper следующим образом:Динамический прокси. Класс динамического прокси — это класс MapperProxy, который мы подробно проанализируем в следующих главах.

также,SqlSession небезопасен для потоков, поэтому область действия объекта SqlSession должна быть ограничена методом. Но веснаSqlSessionTemplateпомогли нам решить эту проблему.SqlSessionTemplateОн потокобезопасен и может совместно использоваться несколькими DAO или картографами. При вызове метода SQL (в том числе с помощьюgetMapper()метод в преобразователе, возвращаемом методом),SqlSessionTemplateГарантирует, что используемый SqlSession связан с текущей транзакцией Spring. Кроме того, он управляет временем существования сеанса, включая необходимые операции закрытия, фиксации или отката. Кроме того, он также отвечает за перевод исключений MyBatis в Spring.DataAccessExceptions. Подробнее см. в официальной документации mybatis-spring.

In MyBatis you use the SqlSessionFactory to create an SqlSession. Once you have a session, you use it to execute your mapped statements, commit or rollback connections and finally, when it is no longer needed, you close the session. With MyBatis-Spring you don't need to use SqlSessionFactory directly because your beans can be injected with a thread safe SqlSession that automatically commits, rollbacks and closes the session based on Spring's transaction configuration.

2.2.3 Выполнение операторов Sql

Выполнение оператора   SQL включает в себя различные компоненты, наиболее важными из которых являютсяExecutor,StatementHandler,ParameterHandlerа такжеResultSetHandler. Объект Executor создается при создании объекта Configuration и кэшируется в объекте Configuration, отвечает за кэш первого уровня и кэш второго уровня и обеспечивает операции, связанные с управлением транзакциями. Основная функция объекта Executor заключается в вызове StatementHandler для доступа к базе данных и сохранения результатов запроса в кеше (если кеш сконфигурирован). объект java.sql.Statement и получить набор результатов ResultSet, и, наконец, завершить сопоставление набора результатов с помощью ResultSetHandler, получить объект и вернуть его.


3. Быстрый старт

3.1 Springboot интегрирует Mybatis

Подготовка к Springboot для интеграции Mybatis очень проста.Если это проект maven, вам нужно только заполнить вводную часть в pom.xml, чтобы быстро приступить к работе.

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>
<!-- 添加mysql驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.12</version>
 </dependency>

Итак, что сделал mybatis-spring-boot-starter?Давайте посмотрим, что говорит официальная документация

As you may already know, to use MyBatis with Spring you need at least an SqlSessionFactory and at least one mapper interface.MyBatis-Spring-Boot-Starter will:

  • Autodetect an existing DataSource
  • Will create and register an instance of a SqlSessionFactory passing that DataSource as an input using the SqlSessionFactoryBean
  • Will create and register an instance of a SqlSessionTemplate got out of the SqlSessionFactory
  • Auto-scan your mappers, link them to the SqlSessionTemplate and register them to Spring context so they can be injected into your beans
  • Springboot автоматически обнаружит наши настроенные数据源,
  • Создать и зарегистрироватьSqlSessionFactoryЭкземпляр действует наSqlSessionFactoryBean(Прокси-объект. То, что получено в BeanFactory, на самом деле является объектом, возвращаемым функцией getObject() FactoryBean)
  • также зарегистрирует создание изSqlSessionFactoryполучен изSqlSessionTemplateпример.
  • автоматическое сканированиеMapper,(Здесь Springboot по умолчанию сканирует мапперы с аннотацией @Mapper, при необходимости можно передать@MapperScanОпределите путь сканирования, используя)связать их сSqlSessionTemplateи зарегистрируйте их в контейнере внедрения контекста Spring.
/**
   * If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan
   * mappers based on the same component-scanning path as Spring Boot itself.
   */
  @org.springframework.context.annotation.Configuration
  @Import(AutoConfiguredMapperScannerRegistrar.class)
  @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
  public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
      logger.debug(
          "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
    }

  }

Вот примечание, когда контейнер Spring не может его найтиMapperFactoryBean.classа такжеMapperScannerConfigurer.class, будет импортироватьAutoConfiguredMapperScannerRegistrar.classСканирование картографов с аннотациями @Mapper продолжается для завершения внедрения.

 @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

      if (!AutoConfigurationPackages.has(this.beanFactory)) {
        logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
        return;
      }

      logger.debug("Searching for mappers annotated with @Mapper");

      List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
      if (logger.isDebugEnabled()) {
        packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
      }

      BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
      builder.addPropertyValue("processPropertyPlaceHolders", true);
      builder.addPropertyValue("annotationClass", Mapper.class);
      builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
      BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
      Stream.of(beanWrapper.getPropertyDescriptors())
          // Need to mybatis-spring 2.0.2+
          .filter(x -> x.getName().equals("lazyInitialization")).findAny()
          .ifPresent(x -> builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"));
      registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
    }

Однако, исходя из принципа сохранения, для завершения инъекции маппера рекомендуется использовать @MapperScan. Добавьте @MapperScan («путь сопоставления») в основной класс запуска приложения, чтобы завершить инициализацию mybatis.

@SpringBootApplication
@MapperScan("com.example.*")
public class MybatisApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisApplication.class, args);
    }

}

3.2 Написать маппер и xml

Что касается оператора sql, вы также можете выбрать форму аннотаций, например @select, которая также поддерживается mybatis. Автор лично предпочитает XML, который легко читается и прост в обслуживании. Здесь моя сущность и маппер автоматически генерируются генератором, что очень удобно. Заинтересованные друзья могут узнать об этом. организация:

package com.example.mybatis.model;

public class Role {
    private Long id;

    private String roleId;

    private String roleName;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getRoleId() {
        return roleId;
    }

    public void setRoleId(String roleId) {
        this.roleId = roleId == null ? null : roleId.trim();
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName == null ? null : roleName.trim();
    }
}

маппер: маппер

package com.example.mybatis.mapper;

import com.example.mybatis.model.Role;
import org.apache.ibatis.annotations.Mapper;

//@Mapper
public interface RoleMapper {
    int deleteByPrimaryKey(Long id);

    int insert(Role record);

    int insertSelective(Role record);

    Role selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(Role record);

    int updateByPrimaryKey(Role record);
}

xml: оператор SQL

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.mybatis.mapper.RoleMapper" >
  <resultMap id="BaseResultMap" type="com.example.mybatis.model.Role" >
    <id column="id" property="id" jdbcType="BIGINT" />
    <result column="role_id" property="roleId" jdbcType="VARCHAR" />
    <result column="role_name" property="roleName" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, role_id, role_name
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select 
    <include refid="Base_Column_List" />
    from role
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from role
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.example.mybatis.model.Role" >
    insert into role (id, role_id, role_name
      )
    values (#{id,jdbcType=BIGINT}, #{roleId,jdbcType=VARCHAR}, #{roleName,jdbcType=VARCHAR}
      )
  </insert>
  <insert id="insertSelective" parameterType="com.example.mybatis.model.Role" >
    insert into role
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      </if>
      <if test="roleId != null" >
        role_id,
      </if>
      <if test="roleName != null" >
        role_name,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{id,jdbcType=BIGINT},
      </if>
      <if test="roleId != null" >
        #{roleId,jdbcType=VARCHAR},
      </if>
      <if test="roleName != null" >
        #{roleName,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.example.mybatis.model.Role" >
    update role
    <set >
      <if test="roleId != null" >
        role_id = #{roleId,jdbcType=VARCHAR},
      </if>
      <if test="roleName != null" >
        role_name = #{roleName,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.example.mybatis.model.Role" >
    update role
    set role_id = #{roleId,jdbcType=VARCHAR},
      role_name = #{roleName,jdbcType=VARCHAR}
    where id = #{id,jdbcType=BIGINT}
  </update>
</mapper>

Позже автор проведет некоторый анализ исходного кода процесса выполнения mybatis и продолжит добавлять ссылки на эту статью.