type
status
date
slug
summary
tags
category
icon
password

一、设计

思考:1、映射器的注册提供注册机处理,满足用户可以在使用的时候
提供一个包的路径即可完成扫描和注册
,2、对 SqlSession 进行规范化处理,让它可以把我们的映射器代理和方法调用进行包装
notion image
sqlsessionFactory:sqlSession的创建工厂,通过openSession()创建出sqlsession sqlsession:规范化处理,其中selectOne是数据库的查询操作,getMapper获取对应的映射器 MapperRegister:扫描包路径下的所有接口,并对其进行代理,注册到mapperProxyMap上 MapperProxyFactory:上一步有说明,映射器MapperProxy工厂,newInstance()方法创建映射器 MapperProxy:代理对象,对数据库进行操作

二、实现

notion image
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 的过程中非常重要的部分,了解和熟悉才能更好的在自己的业务中进行使用。
B树与二叉树【手写Mybatis】step01:创建简单的代理工厂