手写mybatis之实现映射器的注册和使用

109 阅读3分钟

前言

其实对于解决这类复杂的项目问题,核心在于要将主干问题点缩小,具体的手段包括:分治、抽象和知识。运用设计模式和设计原则等相关知识,把问题空间合理切割为若干子问题,问题越小也就越容易理解和处理。就像你可以把很多内容做成单个独立的案例一样,最终在进行聚合使用。 上一节编码存在两问题

1:需要编码告知 MapperProxyFactory 要对哪个接口进行代理。 2:需要自己编写一个假的 SqlSession 处理实际调用接口时的返回结果。 所以我们来解决上述存在问题 当然我们还要把上一章节中简化的 SqlSession 进行完善,由 SqlSession 定义数据库处理接口和获取 Mapper 对象的操作,并把它交给映射器代理

image.png 映射器注册机

package com.lm.mybatis.binding;

import cn.hutool.core.lang.ClassScanner; import com.lm.mybatis.session.SqlSession;

import java.util.HashMap; import java.util.Map; import java.util.Set;

public class MapperRegistry { /** * 将已添加的映射器代理加入到 HashMap */ private final Map<Class, MapperProxyFactory> knownMappers = new HashMap<>();

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new RuntimeException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new RuntimeException("Error getting mapper instance. Cause: " + e, e);
    }
}

public <T> void addMapper(Class<T> type) {
    /* Mapper 必须是接口才会注册 */
    if (type.isInterface()) {
        if (hasMapper(type)) {
            // 如果重复添加了,报错
            throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");
        }
        // 注册映射器代理工厂
        knownMappers.put(type, new MapperProxyFactory<>(type));
    }
}

public <T> boolean hasMapper(Class<T> type) {
    return knownMappers.containsKey(type);
}

public void addMappers(String packageName) {
    Set<Class<?>> mapperSet = ClassScanner.scanPackage(packageName);
    for (Class<?> mapperClass : mapperSet) {
        addMapper(mapperClass);
    }
}

}

MapperRegistry 映射器注册类的核心主要在于提供了 ClassScanner.scanPackage 扫描包路径,调用 addMapper 方法,给接口类创建 MapperProxyFactory 映射器代理类,并写入到 knownMappers 的 HashMap 缓存中。 另外就是这个类也提供了对应的 getMapper 获取映射器代理类的方法,其实这步就包装了我们上一章节手动操作实例化的过程,更加方便在 DefaultSqlSession 中获取 Mapper 时进行使用。 SqlSession 标准定义和实现. package com.lm.mybatis.session;

public interface SqlSession {

<T> T selectOne(String id);

<T> T selectOne(String statement, Object parameter);

<T> T getMapper(Class<T> type);

}

在 SqlSession 中定义用来执行 SQL、获取映射器对象以及后续管理事务操作的标准接口。
目前这个接口中对于数据库的操作仅仅只提供了 selectOne,后续还会有相应其他方法的定义。 package com.lm.mybatis.session.defaults;

import com.lm.mybatis.binding.MapperRegistry; import com.lm.mybatis.session.SqlSession;

public class DefaultSqlSession implements SqlSession {

/**
 * 映射器注册机
 */
private MapperRegistry mapperRegistry;
public DefaultSqlSession(MapperRegistry mapperRegistry) {
    this.mapperRegistry = mapperRegistry;
}


@Override
public <T> T selectOne(String statement) {
    return (T) ("你被代理了!" + statement);
}

@Override
public <T> T selectOne(String statement, Object parameter) {
    return (T) ("你被代理了!" + "方法:" + statement + " 入参:" + parameter);
}

@Override
public <T> T getMapper(Class<T> type) {
    return mapperRegistry.getMapper(type, this);
}

}

通过 DefaultSqlSession 实现类对 SqlSession 接口进行实现。
在 selectOne 中是一段简单的内容返回,目前还没有与数据库进行关联,这部分在我们渐进式的开发过程中逐步实现。
SqlSessionFactory 工厂定义和实现


import com.lm.mybatis.binding.MapperRegistry;
import com.lm.mybatis.session.SqlSession;
import com.lm.mybatis.session.SqlSessionFactory;

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private final MapperRegistry mapperRegistry;

    public DefaultSqlSessionFactory(MapperRegistry mapperRegistry) {
        this.mapperRegistry = mapperRegistry;
    }

    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(mapperRegistry);
    }

}


默认的简单工厂实现,处理开启 SqlSession 时,对 DefaultSqlSession 的创建以及传递 mapperRegistry,这样就可以在使用 SqlSession 时获取每个代理类的映射器对象了。
增加接口测试


public interface ISchoolDao {
    String querySchoolName(String uId);
}


    public void test02() {
        // 1. 注册 Mapper
        MapperRegistry registry = new MapperRegistry();
        registry.addMappers("com.lm.test.dao");

        // 2. 从 SqlSession 工厂获取 Session
        SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(registry);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 3. 获取映射器对象
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);
        // 4. 测试验证
        String res = userDao.queryUserName("10001");
        logger.info("测试结果:{}", res);

    }
}

测试结果如下

image.png

好了到这里就结束了手写mybatis之实现映射器的注册和使用的学习,大家一定要跟着动手操作起来。需要的源码的 可si我获取;