preface
Yesterday, I saw this interview question from Ali in an interview book:
How to establish the relationship between Dao interface in Mybatis and SQL in XML file? Wouldn’t it be a conflict if there were two XML files associated with the DAO?
If you read my previous blog about Mybatis source analysis, I believe you can give a good answer.
However, given the length of this series of articles, and the focus is on the interpretation of the source part, so THE author would like to address this problem, and comb through the whole process.
This article with the following articles, edible better.
XML parsing and annotation support
How is the DAO interface invoked
The execution process of SQL statements
Parsing XML
First, when Mybatis initializes SqlSessionFactoryBean, it finds the mapperLocations path to parse all the XML files in it. We’ll focus on two parts here.
1. Create SqlSource
Mybatis wraps each SQL tag into an SqlSource object. Then according to the different SQL statements, it is divided into dynamic SQL and static SQL. Static SQL contains a String SQL statement. Dynamic SQL is composed of sqlNodes.
Suppose we have an SQL like this:
<select id="getUserById" resultType="user">
select * from user
<where>
<if test="uid! =null">
and uid=#{uid}
</if>
</where>
</select>
Copy the code
The corresponding SqlSource object should look like this:
2. Create MappedStatement
Each SQL tag in an XML file corresponds to a MappedStatement object, and there are two attributes that are important.
- id
The ID of the fully qualified class name plus the method name.
- sqlSource
The SqlSource object corresponding to the current SQL tag.
After creating the MappedStatement object, cache it in Configuration#mappedStatements.
The Configuration object, as we know, is the big manager in Mybatis. Basically all the Configuration information is maintained here. After all the XML has been parsed, the Configuration contains all the SQL information.
At this point, the XML is parsed. If you’re smart enough to look at the picture above, you might have an idea. Mybatis = Mybatis = Mybatis = Mybatis = Mybatis = Mybatis = Mybatis = Mybatis = Mybatis = Mybatis
Dao interface proxy
Our Dao interface does not implement a class, so how does it end up executing into our SQL statement when we call it?
First, in our Spring configuration file, we typically configure this:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.viewscenes.netsupervisor.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
Copy the code
Or if your project is based on SpringBoot, you’ve probably seen this before: @mapperscan (“com.xxx.dao”)
They do the same thing. Register all classes in the package path into the Spring Bean and set their beanClass to MapperFactoryBean. Interestingly, MapperFactoryBean implements the FactoryBean interface, commonly known as factory Beans. So, when we inject the Dao interface via @AutoWired, the object returned is the getObject() method object in the MapperFactoryBean factory Bean.
So what does this approach do?
In simple terms, it is the JDK dynamic proxy that returns a PROXY object for the Dao interface. The handler of this proxy object is the MapperProxy object. So, when we inject the Dao interface via @AutoWired, we inject this proxy object, and when we call the Dao interface method, we call the MapperProxy object invoke method.
Spring factoryBeans and JDK dynamic proxies are a good place to start
A friend once asked this question:
Does Mapper still use proxies for implemented DAO interfaces?
The answer is yes, as long as you configure itMapperScan
, it will scan and generate the proxy. However, if your DAO interface has an implementation class, and that implementation class is also a Spring Bean, it depends on where you areAutowired
To inject which one.
What exactly does that mean? We come to an example.
If we give the userDao an implementation class and register it with Spring.
@Component
public class UserDaoImpl implements UserDao{
public List<User> getUserList(Map<String,Object> map){
returnnew ArrayList<User>(); }}Copy the code
We then inject the userDao in the Service method. Guess what?
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userDao1;
public List<User> getUserList(Map<String,Object> map) {
returnuserDao1.getUserList(map); }}Copy the code
As you might have guessed, yes, it will launch an error. Because at the time of injection, two instance objects of UserMapper were found. Log is this: No qualifying bean of type [com.viewscenes.net supervisor. Dao. UserDao] is defined: expected single matching bean but found 2: userDaoImpl,userDao
Of course, maybe we’re not naming things properly. In fact, we can inject it by name, like this: @autoWired UserMapper userDao; Or @autoWired UserMapper userDaoImpl; Or annotate one of your beans with @primary.
For details, see my article: Getting to the bottom of Autowiring and Autowired in Spring
Said said may be far away, we continue to return to Mybatis. So, so far, we also have a proxy implementation through the Dao interface, so we can execute the methods inside it.
Three, execute,
As mentioned above, when we call the Dao interface method, we actually call the invoke method of the proxy object. In this case, what is actually called is the stuff inside the SqlSession.
public class DefaultSqlSession implements SqlSession {
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
returnexecutor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); }}}Copy the code
Seeing the code above, we’re on to something. It fetches the MappedStatement object through the statement fully qualified type + method name, executes the specific SQL through the Executor and returns it.
Four,
Now, let’s go back to the question we asked at the beginning, and maybe you can answer it better. At the same time, I think the interviewer will be very satisfied if you cover the following key words.
- SqlSource and dynamic label SqlNode
- MappedStatement object
- Spring factory beans and dynamic proxies
- SqlSession and the executor
So, to answer the second question: if there are two XML files that have a relationship with the Dao, isn’t that a conflict?
The obvious answer is to keep namespace+ ID unique, regardless of how many XML relationships you have with daOs.