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

java 复制代码
<T> T getMappper(Class<?> mapperClass);

修改完成后,对应的截图如下:

DefaultSqlSession

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

java 复制代码
@Override
public <T> T getMapper(Class<?> mapperClass) {
    Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
        @Override
        public 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 接口的类型。
java 复制代码
Object proxyInstance = Proxy.newProxyInstance(
    DefaultSqlSession.class.getClassLoader(),
    new Class[]{mapperClass},
    new InvocationHandler() { ... }
);
  • Proxy.newProxyInstance:创建动态代理对象。
  • DefaultSqlSession.class.getClassLoader():指定类加载器,用于加载动态生成的代理类。
  • new Class[]{mapperClass}:指定代理类需要实现的接口数组,这里仅实现 mapperClass。
  • new InvocationHandler():传入一个 InvocationHandler,用于处理方法调用逻辑。

动态代理逻辑

java 复制代码
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ... }

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

参数:

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

方法调用逻辑

java 复制代码
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 语句标识符

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

方法返回类型判断

java 复制代码
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 等方法来进行查询测试:

java 复制代码
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));
    }

}

对应的截图如下所示:

运行结果

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

shell 复制代码
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)

对应的截图如下所示:

相关推荐
黑子哥呢?3 分钟前
安装Bash completion解决tab不能补全问题
开发语言·bash
青龙小码农8 分钟前
yum报错:bash: /usr/bin/yum: /usr/bin/python: 坏的解释器:没有那个文件或目录
开发语言·python·bash·liunx
大数据追光猿13 分钟前
Python应用算法之贪心算法理解和实践
大数据·开发语言·人工智能·python·深度学习·算法·贪心算法
南宫生32 分钟前
力扣每日一题【算法学习day.132】
java·学习·算法·leetcode
小刘|1 小时前
深入理解 SQL 注入漏洞及解决方案
数据库·sql
计算机毕设定制辅导-无忧学长1 小时前
Maven 基础环境搭建与配置(一)
java·maven
彳卸风1 小时前
Unable to parse timestamp value: “20250220135445“, expected format is
开发语言
数巨小码人1 小时前
QT SQL框架及QSqlDatabase类
jvm·sql·qt
dorabighead1 小时前
JavaScript 高级程序设计 读书笔记(第三章)
开发语言·javascript·ecmascript
天上掉下来个程小白2 小时前
案例-14.文件上传-简介
数据库·spring boot·后端·mybatis·状态模式