欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > Java-23 深入浅出 MyBatis - 手写ORM框架4 框架优化 SqlSession DefaultSqlSession

Java-23 深入浅出 MyBatis - 手写ORM框架4 框架优化 SqlSession DefaultSqlSession

2024/12/21 23:40:12 来源:https://blog.csdn.net/w776341482/article/details/144393017  浏览:    关键词:Java-23 深入浅出 MyBatis - 手写ORM框架4 框架优化 SqlSession DefaultSqlSession

点一下关注吧!!!非常感谢!!持续更新!!!

大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html

在这里插入图片描述

目前已经更新到了:

  • MyBatis(正在更新)

框架实现

上节已经实现了部分内容 下面我们继续

框架优化

上述我们编写了自定义的框架,我们解决了 JDBC 带来的一系列的问题,但是目前也出现了一些问题:

  • DAO 的实现类存在重复的代码,整过操作模板重复,创建 SqlSession等等
  • DAO 的实现类中有硬编码,调用 SqlSession 的方法时,参数 Statement的 ID 硬编码。

SqlSession

解决:使用代理模式来创建接口的代理对象,我们在 SqlSession 中加入新的方法:getMapper,我们修改 SqlSession

<T> T getMappper(Class<?> mapperClass);

修改完成后,对应的截图如下:
在这里插入图片描述

DefaultSqlSession

我们在实现类中进行实现刚才的 getMapper 的方法:

@Override
public <T> T getMapper(Class<?> mapperClass) {Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);}String className = method.getDeclaringClass().getName();String statementId = className + "." + methodName;Type genericReturnType = method.getGenericReturnType();if(genericReturnType instanceof ParameterizedType){List<Object> objects = selectList(statementId, args);return objects;}return selectOne(statementId,args);}});return (T) proxyInstance;
}

对应的截图如下所示:
在这里插入图片描述

  • Override:表示该方法是对父类或接口方法的重写。
  • T:定义了一个泛型方法,T 是返回值的类型,占位符在调用时会被具体类型替换。
  • getMapper(Class<?> mapperClass):方法接收一个 Class 类型参数,表示需要生成的 Mapper 接口的类型。
Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(),new Class[]{mapperClass},new InvocationHandler() { ... }
);
  • Proxy.newProxyInstance:创建动态代理对象。
  • DefaultSqlSession.class.getClassLoader():指定类加载器,用于加载动态生成的代理类。
  • new Class[]{mapperClass}:指定代理类需要实现的接口数组,这里仅实现 mapperClass。
  • new InvocationHandler():传入一个 InvocationHandler,用于处理方法调用逻辑。

动态代理逻辑

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ... }

invoke 方法:当代理对象调用任何方法时,都会触发此方法。
参数:

  • Object proxy:代理对象本身。
  • Method method:当前被调用的方法对象。
  • Object[] args:调用方法时传递的参数。

方法调用逻辑

String methodName = method.getName();
if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);
}
  • 获取方法名:通过 method.getName() 获取被调用的方法名。
  • if 判断:检查方法是否是 Object 类的方法(如 toString()、equals() 等)。如果是,直接调用原始实现,通过 method.invoke(this, args) 执行。

SQL 语句标识符

String className = method.getDeclaringClass().getName();
String statementId = className + "." + methodName;
  • className:获取方法所在类的全限定名。
  • statementId:拼接方法所在类名和方法名,生成唯一的 SQL 语句标识符,用于在 Mapper 中定位具体的 SQL 语句。

方法返回类型判断

Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType) {List<Object> objects = selectList(statementId, args);return objects;
}
  • genericReturnType:获取方法的返回值类型。
  • instanceof 检查:如果返回类型是参数化类型(例如 List),则调用 selectList 方法执行查询。
  • selectList(statementId, args):模拟批量查询方法,传入 statementId 和参数 args。

这段代码实现了一个动态代理机制,用于生成符合特定 Mapper 接口(mapperClass)的动态代理对象。
代理对象的方法调用逻辑包括以下几部分:

  • 检查方法是否属于 Object,如果是则直接调用原始实现。
  • 根据方法名和类名生成唯一的 statementId,用于定位 SQL 语句。
  • 根据返回值类型决定调用批量查询(selectList)还是单结果查询(selectOne)。
  • 返回查询结果。

测试方法

我们编写一个方法,来通过 SqlSession的 openSession、getMapper 等方法来进行查询测试:

package icu.wzk.test;import icu.wzk.bean.Resources;
import icu.wzk.bean.SqlSession;
import icu.wzk.bean.SqlSessionFactory;
import icu.wzk.bean.SqlSessionFactoryBuilder;
import icu.wzk.dao.UserInfoMapper;
import icu.wzk.model.UserInfo;import java.io.InputStream;public class Test02 {public static void main(String[] args) throws Exception {InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserInfo userInfo = new UserInfo();userInfo.setUsername("wzk");UserInfoMapper userInfoMapper = sqlSession.getMapper(UserInfoMapper.class);System.out.println("userInfoMapper: " + userInfoMapper);System.out.println(userInfoMapper.selectOne(userInfo));}}

对应的截图如下所示:
在这里插入图片描述

运行结果

执行之后,控制台输出的结果如下所示:

log4j:WARN No appenders could be found for logger (com.mchange.v2.log.MLog).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
userInfoMapper: icu.wzk.bean.DefaultSqlSession$1@61dc03ce
SimpleExecutor getBoundSql: SELECT * FROM user_info WHERE username=?
UserInfo(id=1, username=wzk, password=icu, age=18)

对应的截图如下所示:
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com