Введение
Я до сих пор помню, как в прошлом году у меня было телефонное интервью.После того, как я представил интервьюеру стек технологий проекта, он вдруг спросил меня: как 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 anSqlSession
. Once you have a session, you use it to execute yourmapped 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 witha 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 и продолжит добавлять ссылки на эту статью.