自定义ORM(mybatis)源码(四)-MapperProxy
模仿mybatis
MapperProxyFactory
通过生成代理生成 mapper 接口代理对象
public class MapperProxyFactory<T> {
private Class<T> mapper;
public MapperProxyFactory(Class<T> mapper) {
this.mapper = mapper;
}
public T getInstance(SqlSession sqlSession) {
return (T) Proxy.newProxyInstance(MapperProxyFactory.class.getClassLoader(), new Class[]{mapper},
new MapperProxy(sqlSession,mapper));
}
}
MapperProxy
对 mapper 中方法进行代理,最后 委托 SqlSession 执行sql交互
/**
* @Date: 2023/12/14 17:02
* 1、mapper 代理对象,代理 mapper 中的方法,
* 2.最后通过 sqlSession进行数据库操作
*/
@Slf4j
public class MapperProxy implements InvocationHandler {
private SqlSession sqlSession;
private Class<?> mapper;
public <T> MapperProxy(SqlSession sqlSession, Class<T> mapper) {
this.sqlSession = sqlSession;
this.mapper = mapper;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
PreparedStatement pstmt = null;
try {
//解析方法参数及绑定
//获取sql
Configuration configuration = sqlSession.getConfiguration();
String stmtId = mapper.getName().concat(".").concat(method.getName());
MappedStatement mappedStatement = configuration.getMappedStatement(stmtId);
BoundSql boundSql = mappedStatement.getBoundSql();
//解析sql占位符,替换成?, 并记录 参数位置
String sql = boundSql.getParsedSql();
log.info("bound sql: {}", boundSql.getSql());
log.info("bound sql after parsed: {}",sql);
//sql 中 占位符参数
List<ParameterMapping> parameterMappings = boundSql.getParameterMappers();
//select * from user where a=#{a} and b=#{a},如何将 占位符和 ? 一一对应
//方法参数值和占位符对应,这样在sql中可以从这里根据参数名称取参数值
Map<String, Object> parameterValuesMap = getParameterValueMap(method, args);
//statementHandler(简化人executor委托statementHandler)
pstmt = sqlSession.getConnection().prepareStatement(sql);
// parameterHandler
for (int i = 0; i < parameterMappings.size(); i++) {
//从方法参数map中取对应参数对应的参数值
Object arg = parameterValuesMap.get(parameterMappings.get(i).getProperty());
//处理参数类型,根据参数值获取类型
TypeHandler h = TypeHandlerRegister.getHandler(arg.getClass());
//设置参数
h.setParameter(pstmt, i + 1, arg);
}
log.info("executing sql: {}", pstmt.toString());
//处理响应 resultSetHandler
return handleResult(pstmt, method);
} finally {
if (pstmt!=null){
pstmt.close();
}
}
}
/**
* 方法参数名与参数值一一对应
* id:1
* address:test
* @param method
* @param args
* @return
*/
private static Map<String, Object> getParameterValueMap(Method method, Object[] args) {
Map<String, Object> parameterValuesMap = new HashMap<>();
for (int i = 0; i < method.getParameters().length; i++) {
Parameter parameter = method.getParameters()[i];
//默认取参数名称
parameterValuesMap.put(parameter.getName(), args[i]);
if (parameter.isAnnotationPresent(Param.class)) {
Param annotation = parameter.getAnnotation(Param.class);
if (annotation != null) {
parameterValuesMap.put(annotation.value(), args[i]);
}
}
}
return parameterValuesMap;
}
public Object handleResult(PreparedStatement pstmt, Method method) throws Exception {
ResultSet resultSet = pstmt.executeQuery();
Type genericReturnType = method.getGenericReturnType();
Class<?> returnType = null;
boolean single = true;
if (genericReturnType instanceof ParameterizedType) {
//List<UserModel>
ParameterizedType pt = (ParameterizedType) genericReturnType;
Type actualTypeArgument = pt.getActualTypeArguments()[0];
//拿到userModel.class
returnType = (Class<?>) actualTypeArgument;
single = false;
} else {
//UserModel.class
returnType = method.getReturnType();
}
log.info("反射对象类型: {}", returnType);
List<Object> list = new ArrayList<>();
while (resultSet.next()) {
//实例化对象
Object o = returnType.newInstance();
Field[] declaredFields = returnType.getDeclaredFields();
for (Field f : declaredFields) {
//取字段类型
TypeHandler h = TypeHandlerRegister.getHandler(f.getType());
f.setAccessible(true);
f.set(o, h.getResult(resultSet, StrUtil.toUnderlineCase(f.getName())));
}
list.add(o);
}
if (single) {
if (CollectionUtils.isEmpty(list)){
return null;
}
return list.get(0);
}
return list;
}
public static void main(String[] args) throws ClassNotFoundException {
Class<?> aClass = Class.forName("org.example.sample.dal.UserModel");
System.out.println(aClass.getName());
System.out.println(Integer.class);
}
}
注意事项:
- sql 占位符解析
- 方法参数与参数值绑定
- 参数类型解析器
- 结果类型处理器