前言
其实对于解决这类复杂的项目问题,核心在于要将主干问题点缩小,具体的手段包括:分治、抽象和知识。运用设计模式和设计原则等相关知识,把问题空间合理切割为若干子问题,问题越小也就越容易理解和处理。就像你可以把很多内容做成单个独立的案例一样,最终在进行聚合使用。
上一节编码存在两问题
1:需要编码告知 MapperProxyFactory 要对哪个接口进行代理。
2:需要自己编写一个假的 SqlSession 处理实际调用接口时的返回结果。
所以我们来解决上述存在问题
当然我们还要把上一章节中简化的 SqlSession 进行完善,由 SqlSession
定义数据库处理接口和获取 Mapper 对象的操作,并把它交给映射器代理
类进行使用。
映射器注册机
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 工厂定义和实现
package com.lm.mybatis.session;
public interface SqlSessionFactory {
/**
* 打开一个 session
*
* @return SqlSession
*/
SqlSession openSession();
}
package com.lm.mybatis.session.defaults;
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 时获取每个代理类的映射器对象了。
增加接口测试
package com.lm.test.dao;
public interface ISchoolDao {
String querySchoolName(String uId);
}
@Test
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);
}
}
测试结果如下
好了到这里就结束了手写mybatis之实现映射器的注册和使用的学习,大家一定要跟着动手操作起来。需要的源码的 可si我获取;