当前位置: 首页 > news >正文

15-17手写持久层框架优化

学习视频资料来源:https://www.bilibili.com/video/BV1R14y1W7yS

文章目录

  • 1.问题
  • 2. 解决思路分析
  • 3. 生成代理对象

1.问题

上一章框架使用端在测试类中使用框架查询数据库的代码如下:

public class IpersistentTest {

    @Test
    public void test1() throws Exception {
        InputStream resource = Resources.getResource("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
        // 创建SqlSession,过程中创建了executor,executor用于执行sql语句
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = new User();
        user.setId(1);
        user.setUsername("xiaoming");
        Object o = sqlSession.selectOne("user.selectOne", user);
        System.out.println("selectOne返回结果:" + o.toString());
        List<Object> users = sqlSession.selectList("user.selectList", null);
        System.out.println("selectLit返回结果:" + users.toString());
        sqlSession.close();
    }
}

不过这是在测试类中执行的,如果要在项目中实际使用,需要定义UserDao 接口并实现,将上述操作封装进实现类方法中。如下:
接口

public interface UserDao {
    List<User> findAll() throws Exception;
    User findByCondition(User user) throws Exception;
}

实现类


public class UserDaoImpl implements UserDao {

    @Override
    public List<User> findAll() throws Exception {
        InputStream resource = Resources.getResource("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
        // 创建SqlSession,过程中创建了executor,executor用于执行sql语句
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<User> users = sqlSession.selectList("user.selectList", null);
        sqlSession.close();
        return users;
    }

    @Override
    public User findByCondition(User user) throws Exception {
        InputStream resource = Resources.getResource("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
        // 创建SqlSession,过程中创建了executor,executor用于执行sql语句
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User res= sqlSession.selectOne("user.selectOne", user);
        sqlSession.close();
        return res;
    }
}

可以很明显这种方式查询数据库存在的3个问题:
1. 实现类中存在重复代码。包括:加载配置文件,创建会话,关闭会话。
2. selectOne和selectList参数statmentId存在硬编码问题。如果配置文件中改了,代码也要改。
3. 对于每一个表,都需要一个dao。每个dao都需要也给实现类,显然这是比较麻烦的。这一点视频中没有提到,但我觉得是很重要的一个问题。

2. 解决思路分析

  1. 第1个问题比较好解决,因为我们可以使用单例模式保证执行一次后生成的sqlSessionFactory 对象,可以复用 。而且项目大概率会集成spring,所以加载配置文件,执行一次就可以了。
  2. 第2 个问题我们想到可以通过将mapper.xml中的namespace和select标签id 与类全路径和方法名进行绑定(这是非常关键的一点,有一种依赖导致的感觉!!之前是xml里配置什么,我们写代码时候就要传什么,现在是代码写什么,xml里就配置什么)。即xml中的namespace为dao接口的全路径,select标签id 为方法名。这样再调用方法的时候,就能根据类和方法名拼接出statmentId,委托给excutor执行。
  3. 解决了1和2,就会发现我们不需要dao接口的实现类了。只需要创建一个接口的代理对象即可。代理对象去执行真正的操作。同时,就解决了问题3。

3. 生成代理对象

生成代理的对象具体执行逻辑也有2个小问题:

  1. 怎么知道是调哪种SQL类型的方法?如select ? update? delete等
    这个可以在解析xml加下标签类型,比如是select标签,那就是调用selectOne或者selectList。
  2. 怎么知道是调用selectOne还是selectList,如果选择的不对,返回值就会和方法的返回值类型不一致而报错。
    这个可以根据调用方法的返回值确定。获取方法的返回值类型,如果返回值类型是个泛型。如List 、List、Map<String,String>等,那就是 selectList,否则selectOne。

最终核心代码如下:

public class DefaultSqlSession implements SqlSession {

	//......
    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {

            /**
             * porxy代理对象的引用,
             * method被代理对象的方法
             * objects
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
                // 具体的逻辑:执行JDBC
                String methodName = method.getName();//接口中的全路径要和namespace的值一致
                String className = method.getDeclaringClass().getName();
                String statementId = className + "." + methodName;
                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                String sqlCommandType = mappedStatement.getSqlCommandType();
                if ("select".equals(sqlCommandType)) {
                    Type genericReturnType = method.getGenericReturnType();
                    // 通过判断方法的返回值是否为泛型,决定调用selectOne或者selectList
                    if (genericReturnType instanceof ParameterizedType) {
                        return selectList(statementId, objects == null ? null : objects[0]);
                    }
                    return selectOne(statementId, objects == null ? null : objects[0]);
                } else {
                    return null;
                }
            }
        });
        return (T) o;
    }
}

public class IpersistentTest {

    @Test
    public void test1() throws Exception {
        InputStream resource = Resources.getResource("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
        // 创建SqlSession,过程中创建了executor,executor用于执行sql语句
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = new User();
        user.setId(1);
        user.setUsername("xiaoming");
        Object o = sqlSession.selectOne("user.selectOne", user);
        System.out.println("selectOne返回结果:" + o.toString());
        List<Object> users = sqlSession.selectList("user.selectList", null);
        System.out.println("selectLit返回结果:" + users.toString());
        sqlSession.close();
    }
    
	// 使用代理类的方式调用
    @Test
    public void test2() throws Exception {
        InputStream resource = Resources.getResource("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
        // 创建SqlSession,过程中创建了executor,executor用于执行sql语句
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserDao userDaoProxy = sqlSession.getMapper(UserDao.class);
        List<User> all = userDaoProxy.findAll();
        System.out.println("findAll返回结果:" + all.toString());
        User user = new User();
        user.setId(1);
        user.setUsername("xiaoming");
        User use1 = userDaoProxy.findByCondition(user);
        System.out.println("findByCondition返回结果:" + use1.toString());
    }
}

可以看到结果也可以正确返回了
在这里插入图片描述

相关文章:

  • 【Linux高级IO(三)】Reactor
  • Java基础编程练习第38题-除法器
  • C++隐式转换的机制、风险与消除方法
  • LLMs之Llama:Llama 4(Llama 4 Maverick Scout)的简介、安装和使用方法、案例应用之详细攻略
  • 基于人工智能的高中教育评价体系重构研究
  • MATLAB2024a超详细图文安装教程(2025最新版保姆级教程)附安装钥
  • 模板方法模式详解
  • Django学习记录-1
  • 利用 schedule 模块在每日上午每 3 秒执行任务
  • 【接口自动化_数据格式与类型】
  • 【C】结构体初始化方法
  • Linux网络多进程并发服务器和多线程并发服务器
  • 全面解析https代理服务器URL
  • Linux | I.MX6ULL外设功能验证(第10天)
  • synchronized锁升级详解
  • Leedcode刷题 | Day27_贪心算法01
  • 机器学习课堂6交叉熵代价函数的逻辑回归模型
  • RNN、LSTM、GRU汇总
  • IDEA 中右侧没有显示Maven
  • ecplise 工具 没有Java EE Tools 选项
  • 虚拟主机怎么做淘客网站/百度信息流推广是什么意思
  • 域名购买万网/网站关键字优化公司
  • 教育培训门户网站模板下载/营销软文100字
  • 如何做文档附网站/免费产品推广网站
  • 三门网站制作/前端培训费用大概多少
  • 网站分为哪几个部分/交换友情链接的方法