We only need a few simple configurations to use MyBatis in Springboot: 1. Introduce myBatis starter in poM file. 2. Configure the database connection pool. 3. Configure mybatis parameters in the Springboot configuration file. Write your own DAO and Mapper configuration files. How does myBatis get loaded by Springboot and perform JDBC operations when we inject dao and CRUD in our Spring project? Here is the configuration:
- Configure a datasource, such as the druid link pool configuration
Spring: a datasource: # druid related configuration type: com. Alibaba. Druid. Pool. DruidDataSource driver -class-name: com.mysql.jdbc.DriverConfigure the database connectiondruid:
url: jdbc:mysql: / /localhost: 3306 /test-db?useUnicode=true&allowMultiQueries=true&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
initial-size: 10
max-active: 100
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
connectionInitSqls: set names utf8mb4
Copy the code
- Configure mybatis parameter to specify mapper file path
mybatis:
mapper-locations: classpath:mapper/*.xml
Copy the code
- Define the DAO interface as required by the business and add
@Mapper
annotations
@Mapper
public interface UserInfoDao {
int insert(UserInfoDO userInfoDO)
UserInfoDO getById(long id);
int update(UserInfoDO userInfoDO);
int delete(long id);
}
Copy the code
- Configure mapper file, write the MAPPING relationship between SQL and DAO interface, where
namespace
Is the full class name of the corresponding DAO interface
<? 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.hj.dao.UserInfoDao">
<resultMap id="BaseResultMap" type="com.hj.DO.UserInfoDO">
<id column="id" property="id"/>
<result column="userId" property="userId"/>
<result column="name" property="name"/>
</resultMap>
<insert id="insert" parameterType="com.hhdd.DO.UserInfoDO">
insert ...
</insert>
<select id="getById" resultMap="BaseResultMap" parameterType="java.lang.Long">
select ...
</select>
<update id="update" parameterType="com.hj.DO.UserInfoDO">
update ...
</update>
<delete id="delete">
delete ...
</delete>
</mapper>
Copy the code
The loading process
1, readMETA-INF/spring.factories
Classes need to be loaded automatically in the configuration file
mybatis-spring-boot-starter
The effect of a dependency is to provide a POM file that containsmybatis
All the dependencies you need, but the most important ones aremybatis-spring-boot-autoconfigure
, as shown below:
inmybatis-spring-boot-autoconfigure
This package containsMETA-INF/spring.factories
Configuration file, Springboot is through the startup class defined in the configuration file to pull up Mybatis, as shown below:
And Springboot trigger logic of reads the configuration file in @ EnableAutoConfiguration annotations on @ Import annotations introduced AutoConfigurationImportSelector. Class class selectImports method, You can use this method to debug the flow with a breakpoint.
2,MybatisAutoConfiguration
Class to register the beans you want to manage with the Spring container
The process of parsing a class into a BeanDefinition and eventually instantiating it as a Bean is described in Springboot Bean Loading. This registers several important classes with the Spring container:
2.1 registeredSqlSessionFactory
And according to the Mapper configuration file, the mapping relationship between DAO and specific JDBC operations, resultMap and entity classes is resolved
The code is as follows:
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
Configuration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new Configuration();
}
if(configuration ! =null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
for (ConfigurationCustomizer customizer : this.configurationCustomizers) { customizer.customize(configuration); } } factory.setConfiguration(configuration); .if(! ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
return factory.getObject();
}
Copy the code
SqlSessionFactory instantiated by SqlSessionFactoryBean. GetObject (), the class will be injected into a DataSource object (responsible for the management of database connection pool, the Session is a Session, The session is on a Connection provided by the DataSource. SqlSessionFactory. GetObject () method based on we mybatis related configuration (such as the above mybatis. Mapper – locations configuration) to find and resolve our mapper file, The mapping between SQL and DAO methods and between ResultMap and concrete entity classes are parsed and cached in the Configuration of SqlSessionFactory. These information will be used to match JDBC operations during subsequent calls.
2.2 Register the implementation of CRUD operationsSqlSessionTemplate
class
This class implements common CRUD operations. During CRUD operations, the SqlSessionFactory object is used to obtain the Session, so it holds the SqlSessionFactory object
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if(executorType ! =null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return newSqlSessionTemplate(sqlSessionFactory); }}Copy the code
2.3 registeredAutoConfiguredMapperScannerRegistrar
Class to scan by@Mapper
Mark of class
This class is responsible for iterating through the classes annotated by @Mapper and parsing the scanned classes intoBeanDefinition
Register into the Spring container, and the core logic is inregisterBeanDefinitions
, one thing to note is that when classes labeled by @mapper are scanned, they will be resolved to beanClass asMapperFactoryBean
The BeanDefinition of, and tells the Spring container to define thisBeanDefinition
When instantiating beans, injection is requiredSqlSessionFactory
andSqlSessionTemplate
Object, as shown in the following screenshot:
You can see from this that the DAO we’re injecting into our code is actually a dynamic proxy class, generated by the getObject() method of the FactoryBean
3 When the DAO is injected, the corresponding daoMapperFactoryBean.getObject()
Method to inject dynamic proxy classes
MapperFactoryBean. GetObject () logic by the BeanFactory getBean (string beanName) trigger, getObject () code is as follows:
public T getObject(a) throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
Copy the code
GetSqlSession () obtains the SqlSessionTemplate object. This. mapperInterface is our DAO layer interface, such as the @mapper UserInfoDao in the demo. MapperProxyFactory generates the dynamic proxy class as follows:
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
Copy the code
SqlSession = SqlSessionTemplate; SqlSession = SqlSessionTemplate; The result is handled by the Invoke (Object Proxy, Method Method, Object[] args) Method of the MapperProxy class (which implements the InvocationHandler interface).
Call the process
Through the loading process above, we know that the last thing injected into the business code is a dynamic proxy class. Let’s look at the call process of this dynamic proxy class. The main logic is in the Invoke (Object Proxy, Method Method, Object[] args) Method of the MapperProxy class (which implements the InvocationHandler interface). Here I use the select request as an example, the code is as follows:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
returninvokeDefaultMethod(proxy, method, args); }}catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
Copy the code
SqlSessionTemplate
In the loading stage, the corresponding mapping relationship will be resolved according to the configured Mapper file, and the metadata information (including the SQL to be executed, return type, etc.) will be encapsulatedMapperMethod
When the constructor is calledSqlSessionTemplate
Take the metadata information and encapsulate it intoSqlCommand
Object that passes when performing JDBC operationsSqlCommand
To obtain JDBC information to execute subsequent logic. The debug screenshot is as follows:
Finally throughSqlSessionTemplate
Class to implement JDBC operations, the debug diagram is as follows:
The sqlSession. The selectOne Method will be called to SqlSessionTemplate inner classes SqlSessionInterceptor. Invoke (Object proxy, Method Method, Object[] args) ¶
- through
SqlSessionFactory
fromDataSource
From the connection poolsqlSession
I’m going to start with oneThreadLocal
Because if the transaction is enabled,sqlSession
throughThreadLocal
If the transaction is not started, a new Session is fetched from the connection pool - Call the obtained by reflection
sqlSession
Object (which is obtained hereDefaultSqlSession
)selectList
methods - Obtain the previously parsed metadata information, including the corresponding SQL and return type, using the DAO interface name and method name key. The debug screenshot is as follows:
- Through one of the four componentsExecutorThe implementation of the class
CachingExecutor
Class (because Mybatis enables caching by default, so this implementation class will be used) to perform JDBC operations. This class encapsulates cache-related operations, and first parses the SQL required to execute this method. The debug diagram is as follows:
The following debug figure shows whether level-2 cache is enabled. If level-2 cache is enabled, the following figure shows whether level-2 cache is enabled:
The level-1 cache (session granularity) is queried. If no cache match is found, subsequent operations continue. The debug diagram is as follows:
- Through one of the four componentsstatementHandlerThe implementation of the class
RoutingStatementHandler
Class to perform CRUD operations. This class is a wrapper class that provides no implementation, but creates a different type StatementHandler based on the Executor typePreparedStatementHandler
Class, the debug diagram is as follows:
- ParameterHandler is used to concatenate SQL parameters. The debug diagram is as follows:
- The return value is processed by ResultSetHandler, one of the four components, and the database return value is bound to the corresponding entity class. The debug diagram is as follows:
- Logic for handling cached information, freeing resources, and so on