Mybatis Plus的sql语句执行分析(三)
group : com.baomidou
version:3.5.2.2-SNAPSHOT
目标
目的很简单,根据查询语句来反推执行过程。我们需要先了解整体脉络长什么样子,才能更加深入的了解源码内容,来方便我们后续进行二次开发。
查询语句
定义了AirBaseMapper:
public interface AirBaseMapper extends BaseMapper<AirBase> {
}
不想写sql,直接使用的LambdaQueryChainWrapper来执行count的查询。
UserService bean = annotationConfigApplicationContext.getBean(UserService.class);
AirBaseMapper airBaseMapper = annotationConfigApplicationContext.getBean(AirBaseMapper.class);
LambdaQueryChainWrapper<AirBase> airBaseLambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(airBaseMapper);
Long one = airBaseLambdaQueryChainWrapper.count();
开始分析
直接查看airBaseLambdaQueryChainWrapper.count的实现,代码如下:
default Long count() {
return SqlHelper.retCount(getBaseMapper().selectCount(getWrapper()));
}
SqlHelper.retCount不重要,直接跳过。
getBaseMapper()方法是LambdaQueryChainWrapper类中的方法,返回的是类的baseMapper属性。
public class LambdaQueryChainWrapper<T> extends AbstractChainWrapper<T, SFunction<T, ?>, LambdaQueryChainWrapper<T>, LambdaQueryWrapper<T>>
implements ChainQuery<T>, Query<LambdaQueryChainWrapper<T>, T, SFunction<T, ?>> {
private final BaseMapper<T> baseMapper;
public LambdaQueryChainWrapper(BaseMapper<T> baseMapper) {
super();
this.baseMapper = baseMapper;
super.wrapperChildren = new LambdaQueryWrapper<>();
}
@Override
public BaseMapper<T> getBaseMapper() {
return baseMapper;
}
}
也就是调用的AirBaseMapper的selectCount方法。
关于AirBaseMapper
根据第一章我们可以知道我们自定义的AirBaseMapper在定义BeanDefinition的时候BeanClass会被设置为MapperFactoryBean。MapperFactoryBean实现了FactoryBean,所以创建对象的时候会执行getObject方法;同时MapperFactoryBean继承了SqlSessionDaoSupport并且最终实现了InitializingBean,所以在初始化完之后会执行afterPropertiesSet方法,继而最终执行MapperFacotyBean的checkDaoConfig方法。
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
private boolean addToConfig = true;
public MapperFactoryBean() {
// intentionally empty
}
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
/**
* {@inheritDoc}
*/
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
/**
* {@inheritDoc}
*/
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSingleton() {
return true;
}
// ------------- mutators --------------
/**
* Sets the mapper interface of the MyBatis mapper
*
* @param mapperInterface
* class of the interface
*/
public void setMapperInterface(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
/**
* Return the mapper interface of the MyBatis mapper
*
* @return class of the interface
*/
public Class<T> getMapperInterface() {
return mapperInterface;
}
/**
* If addToConfig is false the mapper will not be added to MyBatis. This means it must have been included in
* mybatis-config.xml.
* <p>
* If it is true, the mapper will be added to MyBatis in the case it is not already registered.
* <p>
* By default addToConfig is true.
*
* @param addToConfig
* a flag that whether add mapper to MyBatis or not
*/
public void setAddToConfig(boolean addToConfig) {
this.addToConfig = addToConfig;
}
/**
* Return the flag for addition into MyBatis config.
*
* @return true if the mapper will be added to MyBatis in the case it is not already registered.
*/
public boolean isAddToConfig() {
return addToConfig;
}
}
public abstract class DaoSupport implements InitializingBean {
protected final Log logger = LogFactory.getLog(this.getClass());
public DaoSupport() {
}
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
this.checkDaoConfig();
try {
this.initDao();
} catch (Exception var2) {
throw new BeanInitializationException("Initialization of DAO failed", var2);
}
}
protected abstract void checkDaoConfig() throws IllegalArgumentException;
protected void initDao() throws Exception {
}
}
这里有个疑问,就是MapperFactoryBean继承了抽象类SqlSessionDaoSupport,SqlSessionDaoSupport中有属性sqlSessionTemplate。那这个sqlSession是怎么获取到值的呢?其实就是ClassPathMapperSanner的processBeanDefinitions方法中设置的AutowireMode为AbstractBeanDefinition.AUTOWIRE_BY_TYPE,所以创建对象的时候会执行createSqlSessionTemplate方法。
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSessionTemplate sqlSessionTemplate;
/**
* Set MyBatis SqlSessionFactory to be used by this DAO. Will automatically create SqlSessionTemplate for the given
* SqlSessionFactory.
*
* @param sqlSessionFactory
* a factory of SqlSession
*/
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
}
}
/**
* Create a SqlSessionTemplate for the given SqlSessionFactory. Only invoked if populating the DAO with a
* SqlSessionFactory reference!
* <p>
* Can be overridden in subclasses to provide a SqlSessionTemplate instance with different configuration, or a custom
* SqlSessionTemplate subclass.
*
* @param sqlSessionFactory
* the MyBatis SqlSessionFactory to create a SqlSessionTemplate for
* @return the new SqlSessionTemplate instance
* @see #setSqlSessionFactory
*/
@SuppressWarnings("WeakerAccess")
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
/**
* Return the MyBatis SqlSessionFactory used by this DAO.
*
* @return a factory of SqlSession
*/
public final SqlSessionFactory getSqlSessionFactory() {
return (this.sqlSessionTemplate != null ? this.sqlSessionTemplate.getSqlSessionFactory() : null);
}
/**
* Set the SqlSessionTemplate for this DAO explicitly, as an alternative to specifying a SqlSessionFactory.
*
* @param sqlSessionTemplate
* a template of SqlSession
* @see #setSqlSessionFactory
*/
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
/**
* Users should use this method to get a SqlSession to call its statement methods This is SqlSession is managed by
* spring. Users should not commit/rollback/close it because it will be automatically done.
*
* @return Spring managed thread safe SqlSession
*/
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
/**
* Return the SqlSessionTemplate for this DAO, pre-initialized with the SessionFactory or set explicitly.
* <p>
* <b>Note: The returned SqlSessionTemplate is a shared instance.</b> You may introspect its configuration, but not
* modify the configuration (other than from within an {@link #initDao} implementation). Consider creating a custom
* SqlSessionTemplate instance via {@code new SqlSessionTemplate(getSqlSessionFactory())}, in which case you're
* allowed to customize the settings on the resulting instance.
*
* @return a template of SqlSession
*/
public SqlSessionTemplate getSqlSessionTemplate() {
return this.sqlSessionTemplate;
}
/**
* {@inheritDoc}
*/
@Override
protected void checkDaoConfig() {
notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
}
}
Mapper接口的调用过程
首先画了一个基础的流程图,从创建Mapper对象到执行的过程,其中重点就是两个代理对象的使用。接下来就根据这个图来进行源码分析。
- SqlSessionTemplate代理对象的创建(其实说的是内部的sqlSessionProxy属性的代理对象)
在AirbaseMapper接口对象的创建过程中,会调用MapperFactoryBean的getObject方法。因为BeanClass被设置为MapperFactoryBean,同时AutowireMode为AbstractBeanDefinition.AUTOWIRE_BY_TYPE,有疑问查看深入理解Spring AutoWireMode文章。
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
然后这个方法将被执行。
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
也就是每一个我们定义的mapper接口都会在内部创建一个SqlSessionProxy。核心就是SqlSessionInterceptor()。这个的sqlSession属性就是DefaultSqlSession了。因为方法内部执行了DefaultSqlSessionFactory的openSession。
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
method.invoke就会执行DefaultSqlSession类中去。
- MybatisMapperProxy对象创建
通过上面的sqlSessionTemplate获取Mapper对象。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// TODO 这里换成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
// fix https://github.com/baomidou/mybatis-plus/issues/4247
MybatisMapperProxyFactory<T> mapperProxyFactory = (MybatisMapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
mapperProxyFactory = (MybatisMapperProxyFactory<T>) knownMappers.entrySet().stream()
.filter(t -> t.getKey().getName().equals(type.getName())).findFirst().map(Map.Entry::getValue)
.orElseThrow(() -> new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry."));
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
非常简单就是代理对象的创建。
public T newInstance(SqlSession sqlSession) {
final MybatisMapperProxy<T> mapperProxy = new MybatisMapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
总结
通过反推的方式能知道大概的执行过程,了解内部的实现方式。然后我们就可以梳理里面有哪些细节,分别的作用是什么,来完善知识结构。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!