一:mybatis概述

  MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。

二:手写mybatis

实现思路::

1创建SqlSessionFactory实例

2:实例化过程中,加载配置文件创建configuration对象

3:通过factory创建SqlSeesion

---------------------初始化阶段完成--------------------------

4:通过SqlSeesion获取mapper接口动态代理

5:动态代理回调sqlsession中某查询方法

---------------------代理阶段完成--------------------------

6:SqlSession将查询方法转发给Executor

7:Executor基于jdbc访问数据库获取数据

8:Executor通过反射将数据转化成POJP并返回给SqlSession

9:将数据返回给调用者

手写源码展示:

     2.1、初始化阶段

     将db.properties,以及*mapper.xml读取到内存中,主要通过SqlSessionFactory实现

/*** 初始化加载配置信息映射* 生成sqlsession* @param: mybatis03* @description:* @author: tangl* @create: 2019-05-13 12:07*/
public class SqlSessionFactory {public final Configuration  conf = new Configuration();public SqlSessionFactory(){loadDbInfo();loadMappersInfo();//加载
    }//mappers.xml文件保存路径public static final  String MAPPER_CONFIG_LOCATION="mappers";//数据库连接配置文件路径public static final String DB_CONFIG_FILE="db.properties";private void loadDbInfo(){InputStream inputStream =  SqlSessionFactory.class.getClassLoader().getResourceAsStream(DB_CONFIG_FILE);Properties p = new Properties();try {p.load(inputStream);} catch (IOException e) {e.printStackTrace();}conf.setJdbcDriver(p.get("jdbc.driver").toString());conf.setJdbcUrl(p.get("jdbc.url").toString());conf.setJdbcUsername(p.get("jdbc.username").toString());conf.setJdbcPassword(p.get("jdbc.password").toString());}//读取xml并解析成mapprivate void loadMappersInfo(){URL resources =null;resources =  SqlSessionFactory.class.getClassLoader().getResource(MAPPER_CONFIG_LOCATION);File mappers =  new File(resources.getFile());if(mappers.isDirectory()){File[] listFiles = mappers.listFiles();for(File file:listFiles){loadMapperInfo(file);}}}//加载指定*.xml文件private  void loadMapperInfo(File file){SAXReader reader = new SAXReader();Document document=null;try {document = reader.read(file);} catch (DocumentException e) {e.printStackTrace();}Element root = document.getRootElement();//获取命名空间String namespace = root.attribute("namespace").getData().toString();//select子元素List<Element> selects = root.elements("select");//遍历所有查询语句for( Element element:selects){MappedStatement mappedStatement = new MappedStatement();String id =  element.attribute("id").getData().toString();String resultType =  element.attribute("resultType").getData().toString();String sql =  element.getData().toString();String sourceId = namespace+"."+id; //map的key是命名空间+id
            mappedStatement.setNamespace(namespace);mappedStatement.setSourceId(sourceId);mappedStatement.setResultType(resultType);mappedStatement.setSql(sql);conf.getMappedStatements().put(sourceId,mappedStatement);}}public SqlSession openSession(){return new DefuaultSqlSession(this.conf);}
}

SqlSessionFactory

/*** 数据库配置文件映射* *mapper.xml映射map* @param: mybatis03* @description:* @author: tangl* @create: 2019-05-13 12:10*/
public class Configuration {private String jdbcDriver;//数据库驱动private String jdbcUrl;//数据库url地址private String jdbcUsername;//数据库用户名private String jdbcPassword;//数据库密码private Map<String, MappedStatement> mappedStatements = new HashMap<>();//mapper.xml解析成对象public String getJdbcDriver() {return jdbcDriver;}public void setJdbcDriver(String jdbcDriver) {this.jdbcDriver = jdbcDriver;}public String getJdbcUrl() {return jdbcUrl;}public void setJdbcUrl(String jdbcUrl) {this.jdbcUrl = jdbcUrl;}public String getJdbcUsername() {return jdbcUsername;}public void setJdbcUsername(String jdbcUsername) {this.jdbcUsername = jdbcUsername;}public String getJdbcPassword() {return jdbcPassword;}public void setJdbcPassword(String jdbcPassword) {this.jdbcPassword = jdbcPassword;}public Map<String, MappedStatement> getMappedStatements() {return mappedStatements;}public void setMappedStatements(Map<String, MappedStatement> mappedStatements) {this.mappedStatements = mappedStatements;}
}

Configuration

/*** mapper.xml映射文件* @param: mybatis03* @description:* @author: tangl* @create: 2019-05-13 12:13*/
public class MappedStatement {private String namespace;//命名空间private String sourceId;//方法id名称private String resultType;//返回数据类型private String sql;//sql语句public String getNamespace() {return namespace;}public void setNamespace(String namespace) {this.namespace = namespace;}public String getSourceId() {return sourceId;}public void setSourceId(String sourceId) {this.sourceId = sourceId;}public String getResultType() {return resultType;}public void setResultType(String resultType) {this.resultType = resultType;}public String getSql() {return sql;}public void setSql(String sql) {this.sql = sql;}
}

MappedStatement

 2.2、代理阶段

主要通过DefuaultSqlSession实现

/*** 1: 对外提供访问的api* 2:对内请求转发给Executor实现* @param: mybatis03* @description:* @author: tangl* @create: 2019-05-13 14:51*/
public interface SqlSession {<T> T selectOne(String statement,Object parameter) throws Exception;<E> List<E> selectList(String statement, Object parameter);<T> T getMapper(Class<T> type);
}

SqlSession

/*** 1: 对外提供访问的api* 2:对内请求转发给Executor实现* 3: 通过动态代理,面向接口编程* @param: mybatis03* @description:* @author: tangl* @create: 2019-05-13 14:51*/
public class DefuaultSqlSession implements SqlSession {private final Configuration conf;private Executor executor;public DefuaultSqlSession(Configuration conf){super();this.conf=conf;executor = new DefaultExcutor(conf);}@Overridepublic <T> T selectOne(String statement, Object parameter) throws Exception {MappedStatement  ms = conf.getMappedStatements().get(statement);List<Object> selectList =  executor.query(ms,parameter);if(null==selectList || selectList.size()==0){return null;}if(selectList.size()==1){return (T)selectList.get(0);}else{throw new Exception();}}@Overridepublic <E> List<E> selectList(String statement, Object parameter) {MappedStatement  ms = conf.getMappedStatements().get(statement);return executor.query(ms,parameter);}@Overridepublic <T> T getMapper(Class<T> type) {return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},new MapperProxy(this));}public class MapperProxy implements InvocationHandler{private SqlSession session;public MapperProxy(SqlSession session){super();this.session = session;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Class<?>  returnType = method.getReturnType();System.out.println("returnType="+returnType.toString());if(Collection.class.isAssignableFrom(returnType)){//返回参数是不是集合return session.selectList(method.getDeclaringClass().getName()+"."+method.getName(),null==args?null:args[0]);}else{return session.selectOne(method.getDeclaringClass().getName()+"."+method.getName(),null==args?null:args[0]);}}}
}

DefuaultSqlSession

  2.3、数据库操作返回数据并做映射处理阶段

     主要通过Executor实现

/*** @param: mybatis03* @description: 封装对数据库操作,以及对返回数据的封装* @author: tangl* @create: 2019-05-13 15:25*/
public interface Executor {<E> List<E> query(MappedStatement mappedStatement, Object args);
}

Executor

/*** @param: mybatis03* @description: 封装对数据库操作,以及对返回数据的封装* @author: tangl* @create: 2019-05-13 15:25*/
public class DefaultExcutor implements Executor {private Configuration conf;public DefaultExcutor(Configuration conf){super();this.conf=conf;}@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter) {List<E> ret = new ArrayList<>();try {Class.forName(conf.getJdbcDriver());} catch (ClassNotFoundException e) {e.printStackTrace();}Connection connection=null;PreparedStatement preparedStatement =null;ResultSet resultSet=null;try {connection =  DriverManager.getConnection(conf.getJdbcUrl(),conf.getJdbcUsername(),conf.getJdbcPassword());preparedStatement = connection.prepareStatement(ms.getSql());//处理sql语句中的占位符
            parameterize(preparedStatement,parameter);resultSet = preparedStatement.executeQuery();//通过发射技术,填充到list
            handlerResultSet(resultSet,ret,ms.getResultType());} catch (SQLException e) {e.printStackTrace();}finally {try {resultSet.close();preparedStatement.close();connection.close();} catch (SQLException e) {e.printStackTrace();}}return ret;}public void parameterize(PreparedStatement preparedStatement,Object parameter) throws SQLException {if(parameter instanceof String){preparedStatement.setString(1,(String)parameter);}else if(parameter instanceof Long){preparedStatement.setLong(1,(Long)parameter);}else if(parameter instanceof Integer){preparedStatement.setInt(1,(int)parameter);}}public <E> void handlerResultSet(ResultSet resultSet,List<E> ret,String className){Class<E> clazz = null;try {clazz = (Class<E>)Class.forName(className);} catch (ClassNotFoundException e) {e.printStackTrace();}try {while(resultSet.next()){//通过反射实例化对象Object entity  =  clazz.newInstance();//使用反射工具将resultSet中的数据填充到entity中//id,name,sex,age
//                Integer id  =  resultSet.getInt(1);
//                String  name  =  resultSet.getString(2);
//                String  sex  =  resultSet.getString(3);
//                Integer age  =  resultSet.getInt(4);
//
//                Integer id02 =  resultSet.getInt("id");
//                String name02 =  resultSet.getString("name");
//                String sex02 =  resultSet.getString("id");
//                Integer age02  =  resultSet.getInt("age");//  ReflectionUtils.// 获取实体类的所有属性,返回Field数组Field[] fields = clazz.getDeclaredFields();for(Field field:fields){field.setAccessible(true);String fname  =  field.getName();Type  type = field.getGenericType();System.out.println("fname="+fname+" type="+type.toString());if(type.toString().equals("class java.lang.String")){String column = resultSet.getString(fname);field.set(entity,column);}else if(type.toString().equals("class java.lang.Integer")){Integer column = resultSet.getInt(fname);field.set(entity,column);}}ret.add((E)entity);}} catch (SQLException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}
}

DefaultExcutor

三:测试

/*** @param: mybatis03* @description:手写mybatis测试* @author: tangl* @create: 2019-05-13 14:57*/
public class TestMybatis {public static void main(String[] args) throws ClassNotFoundException {//第一阶段,加载配置信息放入内存SqlSessionFactory factory = new SqlSessionFactory();SqlSession sqlSession = factory.openSession();System.out.println(sqlSession);//第二阶段 封装ibatis编程模式,使用mapper接口开发的初始化工作TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);//第三阶段TUser tUser = mapper.getUserById(1);System.out.println(tUser);List<TUser> allList  = mapper.getUserAllList();if(null!=allList && allList.size()>0){for (TUser tUser02:allList){System.out.println(tUser02);}}}
}

View Code

四:项目补充

1:pom.xml文件

<dependencies>

  <!-- 解析xml文件-->  <dependency>  <groupId>dom4j</groupId>  <artifactId>dom4j</artifactId>  <version>1.6.1</version></dependency>  <!--mysql-->  <dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>    <version>8.0.11</version>  </dependency></dependencies>

2:db.properties
jdbc.driver=com.mysql.cj.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&verifyServerCertificate=false&useSSL=false&serverTimezone=UTCjdbc.username=rootjdbc.password=123456

五:项目说明

这仅仅是参考源码完成mybatis的核心功能,mybatis还有一些其它辅助功能,如:数据库连接池等。

转载于:https://www.cnblogs.com/tanglinsheng/p/10861539.html

了解mybatis源码手写mybatis相关推荐

  1. 【MyBatis源码解析】MyBatis一二级缓存

    MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...

  2. MyBatis源码-深入理解MyBatis Executor的设计思想

    文章目录 Pre JDBC的执行过程 JDBC Demo JDBC Statement 接口 MyBatis执行过程 四大组件 组件之间的关系 Executor 执行器组件 架构总览 接口继承关系 P ...

  3. Mybatis源码解析之Mybatis初始化过程

    一.搭建一个简单的Mybatis工程 为了了解Mybatis的初始化过程,这里需要搭建一个简单的Mybatis工程操作数据库,工程结构如下: 一个UserBean.java private int i ...

  4. Mybatis 源码解析(六) Mybatis方言支持

    背景 现实中,我们经常会遇到使用oracle.mysql的客户,那么我们一般就需要工程同时支持这两种数据库.所以我们需要mybatis对方言进行支持,可以根据不同的数据源执行不同的sql语句. 实现 ...

  5. Pytorch二维卷积 conv2d 使用/源码/手写实现conv2d/手写向量内积实现conv2d/转置卷积实现——学习笔记

    这里是一份学习笔记- 学习视频点这里

  6. MyBatis源码-解读Executor的三个实现类之SimpleExecutor(简单执行器)

    文章目录 Pre Executor 执行器 接口继承关系 SimpleExecutor(简单执行器) 入门小demo 实例化SimpleExecutor doQuery方法 Pre MyBatis源码 ...

  7. Mybatis源码分析: MapperMethod功能讲解

    canmengqian </div><!--end: blogTitle 博客的标题和副标题 --> <div id="navigator"> ...

  8. MyBatis源码分析(一)MyBatis整体架构分析

    文章目录 系列文章索引 一.为什么要用MyBatis 1.原始JDBC的痛点 2.Hibernate 和 JPA 3.MyBatis的特点 4.MyBatis整体架构 5.MyBatis主要组件及其相 ...

  9. MyBatis源码-解读Executor的三个实现类之BatchExecutor(批处理执行器)

    文章目录 Pre Executor 执行器 接口继承关系 BatchExecutor(重用执行器) 入门小demo 源码 BatchExecutor VS ReuseExecutor Pre MyBa ...

最新文章

  1. 腾讯大佬用了12小时讲完的Python,整整400集,拿走不谢!
  2. 经典C语言程序100例之七一
  3. 使用powermock 测试static 方法,jacoco统计覆盖率问题
  4. oracle与mysql的区别总结(一)
  5. django 设置登录成功后的页面
  6. 【福利】PyTorch中文版官方教程来啦(附下载)
  7. Mac OS使用技巧之四:修改打开不同格式视频的默认播放器
  8. 会议主视觉_揭秘!2018杭州云栖大会主视觉设计——光锥之内皆命运
  9. 智芯传感ZXP0电容式大气压力传感器 拓展多领域创新应用
  10. 微信好友排行榜 最简教程 二
  11. Simulink仿真计算中保留特定位数的小数
  12. C语言编程>第二十三周 ③ 下列给定程序中,函数fun的功能是:利用插入排序法对字符串中的字符按从小到大的顺序进行排序。插入法的基本算法是:先对字符串中的头两个元素进行排序;然后把第三字符插入
  13. 激光雷达还是摄影测量?两者数据融合如何提高点云质量
  14. 打开桌面计算机投屏到扩展屏,将Win10电脑屏幕内容投屏到小米电视的操作方法...
  15. 10年后,从新开始...
  16. .netframewor金山卫士推送微软8月补丁 IE所有版本存在高危漏洞
  17. 红黑树的性质以及时间复杂度证明
  18. 安装Carthage
  19. 耐克人脸识别_人脸识别启动,全系9个车型随意选,买它立马有1.2万现金拿
  20. 杰理蓝牙芯片AC6950的使用-按键功能设置

热门文章

  1. jadx工具介绍及使用
  2. 拼多多开店怎么做推广?四川万顿思
  3. SpringCloud微服务之间使用Feign调用不通情况举例
  4. 微信小游戏接入遇到的坑
  5. python在mac模拟鼠标点击_如何使用Python在Mac中控制鼠标?
  6. 海底捞:服务喧宾夺主,盈利不见起色
  7. 批量压缩图片大小 – Caesium简体中文
  8. 癌症/肿瘤免疫治疗最新研究进展(2022年4月)
  9. 对话旷视科技孙剑:iPhone X之外 人脸识别到底安不安全? | AI英雄
  10. 在线客服系统解决方案:物流行业