Code前端首页关于Code前端联系我们

阿里巴巴面试题:如何创建Mybatik的Dao接口和XML文件SQL之间的关系?

terry 2年前 (2023-09-25) 阅读数 46 #后端开发

阿里巴巴的这样一个面试题:

如何创建Mybatis Dao接口和XML文件SQL之间的关系?如果有两个 XML 文件与这个 DAO 形成关系,那不是会发生冲突吗?

如果你看过作者之前关于Mybatis源码分析的博文,我想你绝对可以给出很好的答案。 ? 。

1。创建SqlSource

Mybatis将每个SQL标签封装成一个SqlSource对象。然后根据SQL语句的不同,分为动态SQL和静态SQL。其中,静态SQL包括String类型的SQL语句;而动态SQL则由SqlNode组成。 阿里面试题:Mybatis的Dao接口和XML文件SQL是如何建立关系的?

假设我们有这样的 SQL:

<select id="getUserById" resultType="user">
	select * from user 
	<where>
		<if test="uid!=null">
			and uid=#{uid}
		</if>
	</where>
</select>	

相应的 SqlSource 对象应如下所示:阿里面试题:Mybatis的Dao接口和XML文件SQL是如何建立关系的?

2。创建MappedStatement

XML文件中的每个SQL标签都对应一个MappedStatement对象,这两个属性非常重要。

  • id

ID由完整的类名+方法名组成。

  • sqlSource

与当前 SQL 标识符对应的 SqlSource 对象。

创建MappedStatement对象后,将其缓存在Configuration#mappedStatements中。
配置对象,我们知道它是Mybatis中的大管家,基本上所有的配置信息都维护在这里。解析完所有的XML之后,Configuration就包含了所有的SQL信息。 阿里面试题:Mybatis的Dao接口和XML文件SQL是如何建立关系的?

至此,XML解析完成。如果你看到上图,如果你足够聪明,你可能会有一个想法。当我们执行Mybatis方法时,通过全限定类名+方法名找到MappedStatement对象,并解析其中的SQL内容并执行。

2。 Dao 接口代理

我们的 Dao 接口没有实现类。那么,当我们调用它时,它最终是如何执行我们的SQL语句的呢?

首先,在Spring的配置文件中,我们通常定义如下:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="com.viewscenes.netsupervisor.dao" />
	<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>

或者如果你的项目是基于SpringBoot的,你一定见过这样的:MapperScan("com.xxx.dao")

他们做同样的事。注册Spring Bean中包路径下的所有类,并将其beanClass设置为MapperFactoryBean

有趣的是,MapperFactoryBean实现了FactoryBean接口,俗称工厂bean。然后当我们通过@Autowired添加这个Dao接口时,返回的对象就是这个工厂bean上的MapperFactoryBean方法对象。

那么这个方法有什么作用呢?
简单来说就是通过JDK动态代理返回一个Dao接口代理对象。该代理对象的处理器是 MapperProxy 对象。所有,当我们通过@Autowired添加Dao接口时,代理对象就被注入了。当我们调用Dao接口的方法时,会调用MapperProxy对象的call方法。

不了解此内容的朋友可以先熟悉一下spring中FactoryBean和JDK动态代理相关的资料。

曾经有朋友问过这样的问题:

Mapper实现的dao接口还用代理吗?

答案是肯定的,只要配置MapperScan,它就会扫描然后创建代理。但是,如果你的 dao 接口有一个实现类,并且该实现类也是一个 Spring Bean,则取决于你在Autowired 时添加哪一个。

这到底是什么意思?我们来看一个例子。
如果我们为userDao制作一个实现类并注册到Spring中。

@Component
public class UserDaoImpl implements UserDao{
	public List<User> getUserList(Map<String,Object> map){
		return new ArrayList<User>();
	}
}

然后我们将其添加到userDao的服务方法中。猜猜会发生什么?

@Service
public class UserServiceImpl implements UserService{

	@Autowired
	UserMapper userDao1;

	public List<User> getUserList(Map<String,Object> map) {
		return userDao1.getUserList(map);
	}
}

也许你猜对了,是的,它开始报错。因为在注入的时候发现了两个UserMapper实例对象。日志如下:

No valid bean type [com.viewscenes.netsupervisor.dao.UserDao] Defined: Expected a single Matching bean but found 2: userDaoImpl,userDao

当然,也许我们的命名不是规格非常正确。其实我们可以通过名称来添加,例如:@Autowired UserMapper userDao;@Autowired UserMapper userDaoImpl;或者将@Primary添加到Beannation中。

详细原理请找作者的文章:

了解Spring中的全自动配置和Autowired——掘金 juejin.im可能扯得太远了,我们还是回到Mybatis吧。所以现在我们还有一个通过 Dao 接口的代理实现来执行其上的方法。

3。执行

上面说过,当我们调用Dao接口方法时,实际上调用的是代理对象的call方法。这里,实际上调用的是SqlSession这个东西。

public class DefaultSqlSession implements SqlSession {

	public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
		try {
			MappedStatement ms = configuration.getMappedStatement(statement);
			return executor.query(ms, 
				wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
		}
	}
}

看到上面的代码就说明我们想得不错。它通过命令完全限定类名+方法名获取一个MappedStatement对象,然后通过执行器Executor执行指定的SQL并返回。 阿里面试题:Mybatis的Dao接口和XML文件SQL是如何建立关系的?

4。总结

说到这里我们又回到了开头提到的问题。也许你可以更好地回答他们。同时,笔者觉得,如果在这个问题中讨论以下关键词,面试官就能非常满意。

  • SqlSource 和动态头 SqlNode
  • MappedStatement 对象
  • Spring Factory Bean 和动态代理
  • SqlSession 和执行器 这里是两个带有 SqlSession 的文件。 o 关系,不是冲突吗?
    答案是显而易见的。无论有多少 XML 与 Dao 关联,只要确保 namespace+id 是唯一的即可。

    作者:青狂羽
    来源:知乎

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门