【手写Mybatis】step02:实现映射器的注册和使用

一、设计

思考:1、映射器的注册提供注册机处理,满足用户可以在使用的时候提供一个包的路径即可完成扫描和注册,2、对 SqlSession 进行规范化处理,让它可以把我们的映射器代理和方法调用进行包装
在这里插入图片描述

sqlsessionFactory:sqlSession的创建工厂,通过openSession()创建出sqlsession
sqlsession:规范化处理,其中selectOne是数据库的查询操作,getMapper获取对应的映射器
MapperRegister:扫描包路径下的所有接口,并对其进行代理,注册到mapperProxyMap上
MapperProxyFactory:上一步有说明,映射器MapperProxy工厂,newInstance()方法创建映射器
MapperProxy:代理对象,对数据库进行操作

二、实现

在这里插入图片描述

MapperRegistry 提供包路径的扫描和映射器代理类注册机服务,完成接口对象的代理类注册处理。

SqlSession、DefaultSqlSession 用于定义执行 SQL 标准获取映射器以及将来管理事务等方面的操作。基本我们平常使用 Mybatis 的 API 接口也都是从这个接口类定义的方法进行使用的。

SqlSessionFactory 是一个简单工厂模式,用于提供 SqlSession 服务,屏蔽创建细节,延迟创建过程。

2.1代码结构


mybatis-step-02
└── src
    ├── main
    │   └── java
    │       └── com.qf.mybatis
    │           ├── bind
    │           │   ├── MapperProxy.java
    │           │   ├── MapperProxyFactory.java
    │           │   └── MapperRegistry.java
    │           └── session
    │               ├── defaults
    │               │   ├── DefaultSqlSession.java
    │               │   └── DefaultSqlSessionFactory.java
    │               ├── SqlSession.java
    │               └── SqlSessionFactory.java
    └── test
        └── java
            └──com.qf.mybatis
                ├── dao
                │   ├── ISchoolDao.java
                │   └── IUserDao.java
                └── ApiTest.java


2.2映射器注册机类

package com.qf.mybatis.bind;

import cn.hutool.core.lang.ClassScanner;
import com.qf.mybatis.session.SqlSession;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 扫描指定路径下面的所有接口,并以map的形式储存(接口,实现的mapperProxyFactory)
 */
public class MapperRegister {
    /**
     * 将已添加的映射器代理加入到 HashMap
     */
    private Map<Class<?>,MapperProxyFactory<?>> mapperProxyMap=new HashMap<Class<?>, MapperProxyFactory<?>>();

    /**
     * 扫描指定路径下所有的包(添加mapper)
     */
    public void addMapper(String packageName){
        //注册所有的mapper到hashMap
        Set<Class<?>> classes = ClassScanner.scanPackage(packageName);
        for (Class<?> aClass : classes) {
           addMapper(aClass);
        }
    }

    /**
     * 获取代理对象(映射器)缓存设计
     * @param
     * @param <T>
     */
    public <T> T getMapper(Class<T> type, SqlSession sqlSession){
        MapperProxyFactory<?> tMapperProxyFactory = mapperProxyMap.get(type);
        if (tMapperProxyFactory==null){
            throw new RuntimeException("Type"+type+"is not yet register to the known map");
        }
        try{
            return (T) tMapperProxyFactory.newInstance(sqlSession);
        }catch (Exception e){
            throw new RuntimeException("Error in getting mapper instance:"+e);
        }
    }

    public <T> void addMapper(Class<T> aClass){
        //判断是否为接口
        if (aClass.isInterface()){
            if (!hasRegister(aClass)){
                throw new RuntimeException("Type"+aClass+"is already register to know map!");
            }
            mapperProxyMap.put(aClass,new MapperProxyFactory(aClass));
        }
    }

    public boolean hasRegister(Class<?> aClass){
        return mapperProxyMap.get(aClass)==null;
    }
}

MapperRegistry 映射器注册类的核心主要在于提供了 ClassScanner.scanPackage 扫描包路径,调用 addMapper 方法,给接口类创建 MapperProxyFactory 映射器代理类,并写入到 knownMappers 的 HashMap 缓存中。

另外就是这个类也提供了对应的 getMapper 获取映射器代理类的方法,其实这步就包装了我们上一章节手动操作实例化的过程,更加方便在 DefaultSqlSession 中获取 Mapper 时进行使用

2.3SqlSession的标准定义

package com.qf.mybatis.session;

public interface SqlSession {

    /**
     *先完善查找一个selectone,后续会有更多
     */
   <T> T selectOne(String statement);

    /**
     * 据指定的SqlID获取一条记录的封装对象,只不过这个方法容许我们可以给sql传递一些参数
     *      * 一般在实际使用中,这个参数传递的是pojo,或者Map或者ImmutableMap
     */
    <T> T selectOne(String statement,Object param);

    /**
     * 获取映射器,代理的实例对象
     */
     <T> T getMapper(Class<T> type);
}


目前只定义了selectOne查询,后续会新增

getMapper获取已注册在MapperRegistry 的代理对象

2.3SqlSession标准实现

package com.qf.mybatis.session.bind;

import com.qf.mybatis.bind.MapperRegister;
import com.qf.mybatis.session.SqlSession;

public class DefaultSqlSession implements SqlSession {
    private MapperRegister mapperRegister;
    public DefaultSqlSession(MapperRegister mapperRegister) {
        this.mapperRegister=mapperRegister;
    }

    /**
     * 目前是返回方法
     * @param statement
     * @param <T>
     * @return
     */

    public <T> T selectOne(String statement) {
        return (T) ("你被代理了"+statement);
    }

    public <T> T selectOne(String statement, Object param) {
        return (T) ("你被代理了"+"方法:"+statement+"入参:"+param);
    }

    public <T> T getMapper(Class<T> type) {
        return mapperRegister.getMapper(type,this);
    }
}

MapperRegistry 获取代理对象

2.4SqlSessionFactory 工厂定义

package com.qf.mybatis.session;


public interface SqlSessionFactory {

    /**
     * 开启session,获取session对象
     */
    SqlSession openSession();
}

简单工厂的接口,定义了开启sqlSession的能力

package com.qf.mybatis.session.bind;

import com.qf.mybatis.bind.MapperRegister;
import com.qf.mybatis.session.SqlSession;
import com.qf.mybatis.session.SqlSessionFactory;

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private MapperRegister mapperRegister;
    public DefaultSqlSessionFactory(MapperRegister mapperRegister) {
        this.mapperRegister=mapperRegister;
    }
    public SqlSession openSession() {
        return new DefaultSqlSession(mapperRegister);
    }
}

简单工厂实现,处理开启 SqlSession 时,对 DefaultSqlSession 的创建以及传递 mapperRegistry,这样就可以在使用 SqlSession 时获取每个代理类的映射器对象了

3.、测试

定义的接口

package com.qf.mybatis.dao;

public interface UserDao {
    String queryName(String uid);
    String queryUser(String uid);
}




package com.qf.mybatis.dao;

import java.math.BigDecimal;

public interface ProductDao {
    BigDecimal calPrice();
    String productName();
}

ApiTest

package com.qf.mybatis.test;

import com.qf.mybatis.bind.MapperRegister;
import com.qf.mybatis.dao.UserDao;
import com.qf.mybatis.session.SqlSession;
import com.qf.mybatis.session.SqlSessionFactory;
import com.qf.mybatis.session.bind.DefaultSqlSessionFactory;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ApiTest {
    private Logger logger=LoggerFactory.getLogger(ApiTest.class);

    @Test
    public void sqlSessionTest(){
        //1、注册指定包路径下的dao接口,注册
        MapperRegister mapperRegister=new MapperRegister();
        mapperRegister.addMapper("com.qf.mybatis.dao");

        //2、从sqlSession工厂里面获取sqlSession
        SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(mapperRegister);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //3、获取映射器
        UserDao userDao = sqlSession.getMapper(UserDao.class);

        //4、验证结果
        String queryName = userDao.queryName("111L");
        logger.info("结果:{}",queryName);
    }
}


结果:

"C:\Program Files\Java\jdk1.8.0_301\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\idea2020\IntelliJ IDEA 2020.2.4\lib\idea_rt.jar=27634:D:\idea2020\IntelliJ IDEA 2020.2.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\idea2020\IntelliJ IDEA 2020.2.4\lib\idea_rt.jar;D:\idea2020\IntelliJ IDEA 2020.2.4\plugins\junit\lib\junit5-rt.jar;D:\idea2020\IntelliJ IDEA 2020.2.4\plugins\junit\lib\junit-rt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\rt.jar;D:\zhoutao\handwrite-mybatis\step-02-mapperregistery\target\test-classes;D:\zhoutao\handwrite-mybatis\step-02-mapperregistery\target\classes;C:\Users\Administrator\.m2\repository\com\alibaba\fastjson\1.2.75\fastjson-1.2.75.jar;C:\Users\Administrator\.m2\repository\org\dom4j\dom4j\2.1.3\dom4j-2.1.3.jar;C:\Users\Administrator\.m2\repository\junit\junit\4.7\junit-4.7.jar;C:\Users\Administrator\.m2\repository\cn\hutool\hutool-all\5.5.0\hutool-all-5.5.0.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\1.7.5\slf4j-api-1.7.5.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jcl-over-slf4j\1.7.5\jcl-over-slf4j-1.7.5.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-classic\1.0.9\logback-classic-1.0.9.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-core\1.0.9\logback-core-1.0.9.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.qf.mybatis.test.ApiTest,sqlSessionTest
09:40:22.578 [main] INFO  com.qf.mybatis.test.ApiTest - 结果:你被代理了方法:queryName入参:[Ljava.lang.Object;@5fe5c6f

Process finished with exit code 0

从结果来看,我们自定义的接口已经被MapperRegister 扫描到,完成了简单注册和代理功能

4、总结

SqlSessionFactory 的工厂实现类包装了 SqlSession 的标准定义实现类,并由 SqlSession 完成对映射器对象的注册和使用。

本章学习要注意几个重要的知识点,包括:映射器、代理类、注册机、接口标准、工厂模式、上下文。这些工程开发的技巧都是在手写 Mybatis 的过程中非常重要的部分,了解和熟悉才能更好的在自己的业务中进行使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

舒克日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值