基于Mapper代理完成查询

2023-10-07 源码探究Mybatis

上一章我们最终的实现效果是传递参数调用Mapper中的SQL语句,即sqlSession.selectList("user.selectList", null);;但这是传统方式,还是存在硬编码问题,user.selectList就是硬编码,接下来我们来优化这个问题。

我们平时是怎样使用Mybatis的?首先定义一个接口,然后Mapper.xml和接口达成映射关系;我们来看getMapper方法接收的参数是Class对象,如何根据Class对象寻找mapper文件,并根据方法找到对应的SQL语句呢?

  • 所有的SQL语句都保存在Configuration的mappedStatementMap对象中,这是一个HashMap。

  • 所以只有要key就能找到SQL语句及相关属性,key是构成是namespcae.标签id

  • 因此我们强制规定:namespace是dao接口的全路径名;标签上的id则必须是方法名称









 
 
 





































package com.xk857.session;
public class DefaultSqlSession implements SqlSession {

    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        // 使用JDK动态代理生成基于接口的代理对象
        Object proxy = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, (o, method, objects) -> {
            // 1.获取方法名称
            String methodName = method.getName();
            // 2.获取所在类的全路径 com.xk857.dao.IUserDao
            String className = method.getDeclaringClass().getName();
            // 3.根据Key获取MappedStatement对象
            String statementId = className + "." + methodName;

            // 4.获取MappedStatement对象,判断当前是进行增删改查哪一项操作
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            String sqlCommandType = mappedStatement.getSqlCommandType();
            switch (sqlCommandType) {
                case "select":
                    // 5.获取返回值类型
                    Class<?> returnType = method.getReturnType();
                    boolean isCollection = Collection.class.isAssignableFrom(returnType);
                    // 6.如果是集合类,及返回值是一个数组则查询多个
                    if (isCollection) {
                        // 6.1 如果参数不为空,则传递参数
                        if (objects != null) {
                            return selectList(statementId, objects[0]);
                        }
                        // 6.2 否则传递null
                        return selectList(statementId, null);
                    }
                    return selectOne(statementId, objects[0]);
                case "update":
                    // 执行更新方法调用
                    break;
                case "delete":
                    // 执行delete方法调用
                    break;
                case "insert":
                    // 执行insert方法调用
                    break;
            }
            return null;
        });
        return (T) proxy;
    }
}

更改后的mapper文件

<mapper namespace="com.xk857.dao.IUserDao"> <!--namespace="user"-->
    <select id="selectList" resultType="com.xk857.domain.User">
        select * from user
    </select>
</mapper>

创建一个Dao接口

public interface IUserDao {
    List<User> selectList();
}

# 测试

public class MainTest {
    @Test
    public void test2() throws Exception {
        // 1.读取配置文件信息
        InputStream is = Resources.getResourceAsSteam("sqlMapConfig.xml");
        // 2.解析配置文件封装成JavaBean对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        // 3.配置执行器及默认实现DefaultSqlSessionFactory
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 4.通过接口调用完成查询
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);
        List<User> userList = userDao.selectList();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}
上次更新: 4 个月前