参考博客

https://blog.csdn.net/Kurozaki_Kun/article/details/81482212

个人理解

 读取Mybatis配置文件
数据库连接信息
读取Mapper.xml文件
1.把增删改查节点信息封装成一个MapperInfo
2.创建一个Map存放(接口全路径名+方法名, MapperInfo);
3.提供方法,通过接口获取MapperInfo信息
创建一个动态搭理接口SqlSession
通过传入核心Sql执行类SqlExecuteHandler获得被代理对象。
创建SqlExecuteHandler执行类
1.通过接口方法,获取接口名称和接口方法名称,然后获得MapperInfo信息
2.获得了MapperInfo信息后,获取执行的sql语句,把参数赋值进去,然后执行得到结果。
主要核心点
接口和Mapper之间的关系,用Map(接口+方法名, MapperInfo)进行关联
sql的执行,通过接口生成的代理对象调用方法的时候,执行InvokerHandler方法,通过代理的方法名获取MapperInfo信息,然后获取其中的sql进行执行操作。
查询结果返回的是ResultSet, 需要映射成实体类。

实现步骤

1.创建一个maven项目,导入dom4j和mysql包。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.jtfr</groupId>
<artifactId>SimpleMybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>SimpleMybatis</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> <dependencies>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency> </dependencies>
</project>

2.创建测试表

 CREATE DATABASE db_test;

 USE db_test;

 CREATE TABLE `tb_user` (
`id` INT(11) NOT NULL,
`name` VARCHAR(20) NOT NULL,
`age` TINYINT(4) DEFAULT '',
`addr` VARCHAR(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 INSERT INTO tb_user(id,NAME,age,addr) VALUES(1,'ckm1', 100, 'sz1');
INSERT INTO tb_user(id,NAME,age,addr) VALUES(2,'ckm2', 100, 'sz2');
INSERT INTO tb_user(id,NAME,age,addr) VALUES(3,'ckm3', 100, 'sz3');
INSERT INTO tb_user(id,NAME,age,addr) VALUES(4,'ckm4', 100, 'sz4');
INSERT INTO tb_user(id,NAME,age,addr) VALUES(5,'ckm5', 100, 'sz5');
COMMIT;

3.创建测试用的实体类

 package com.jtfr.entity;

 /**
* 用户实体
* @author ckm
*
*/
public class User { private int id;
private String name;
private int age;
private String addr; public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getAddr() {
return addr;
} public void setAddr(String addr) {
this.addr = addr;
} @Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", addr='" + addr + '\'' +
'}';
}
}

4.创建操作的接口

 package com.jtfr.dao;

 import com.jtfr.entity.User;

 public interface UserDao {

     User getUserInfo(int id);

     int updateUserName(String newName, int id);

     int insertUser(int id, String name, int age, String addr);
}

5.模仿Mybaitis编写对应接口的Mapper.xml

 <?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.jtfr.dao.UserDao">
<select id="getUserInfo" resultType="com.jtfr.entity.User">
select * from tb_user
where id = ?;
</select> <update id="updateUserName">
update tb_user set name = ?
where id = ?
</update> <insert id="insertUser">
insert into tb_user
values(?, ?, ?, ?);
</insert>
</mapper>

6.读取和记录Mapper.xml文件

  6.1需要有一个MapperInfo来存放Mapper.xml里面的每一个操作信息。

 package com.jtfr.core;

 public class MapperInfo {

     private QueryType queryType;
private String interfaceName;
private String methodName;
private String sql;
private String resultType; public QueryType getQueryType() {
return queryType;
} public void setQueryType(QueryType queryType) {
this.queryType = queryType;
} public String getSql() {
return sql;
} public void setSql(String sql) {
this.sql = sql;
} public String getResultType() {
return resultType;
} public void setResultType(String resultType) {
this.resultType = resultType;
} public String getInterfaceName() {
return interfaceName;
} public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
} public String getMethodName() {
return methodName;
} public void setMethodName(String methodName) {
this.methodName = methodName;
} @Override
public String toString() {
return "MapperInfo{" +
"queryType=" + queryType +
", interfaceName='" + interfaceName + '\'' +
", methodName='" + methodName + '\'' +
", sql='" + sql + '\'' +
", resultType='" + resultType + '\'' +
'}';
}
}

  6.2需要一个枚举类,记录Mybatis的操作。

 package com.jtfr.core;

 public enum QueryType {
SELECT, UPDATE, INSERT, DELETE; /**
* 这里的作用,判断标签是否是四种其中的一种
* @param v
* @return
*/
public static QueryType value(String v) {
return valueOf(v.toUpperCase());
}
}

  6.3读取Mapper.xml文件,并且处理相关数据,最后形成一个Map<String, MapperInfo>存放。

 package com.jtfr.core;

 import java.io.File;
import java.util.HashMap;
import java.util.Map; import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader; public enum SqlMappersHolder { INSTANCE; private Map<String, MapperInfo> mi = null; SqlMappersHolder() {
if (mi != null)
return;
mi = new HashMap<String, MapperInfo>();
// 获取mapper.xml文件所在路径
File dir = new File(SqlMappersHolder.class.getClassLoader().getResource(Config.DEFAULT.getMapperPath()).getFile()); // 用dom4j解析
SAXReader reader = new SAXReader();
try {
for (String file : dir.list()) {
// 读取mapper.xml
Document doc = reader.read(new File(dir, file));
Element root = doc.getRootElement();
// 获取接口全路径名
String className = root.attributeValue("namespace"); for (Object o : root.elements()) {
Element e = (Element) o; MapperInfo info = new MapperInfo();
info.setQueryType(QueryType.value(e.getName()));
info.setInterfaceName(className);
info.setMethodName(e.attributeValue("id"));
info.setResultType(e.attributeValue("resultType"));
info.setSql(e.getText()); mi.put(idOf(className, e.attributeValue("id")), info);
}
}
} catch (DocumentException e) {
e.printStackTrace();
}
} public MapperInfo getMapperInfo(String className, String methodName) {
return mi.get(idOf(className, methodName));
} /*
* 接口全路径+"."+方法名作为唯一id
*/
private String idOf(String className, String methodName) {
return className + "." + methodName;
}
}

7.通过接口生成动态代理对象

  7.1JDK动态代理需要先有一个实现InvocationHandler的处理类

  

 package com.jtfr.core;

 import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; public class SqlExecuteHandler implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
// 传入的要被执行的接口方法,通过获取接口名和方法名,然后获取要MapperInfo信息。
MapperInfo info = getMapperInfo(method); // 通过MapperInfo和传入的参数,执行Sql语句,并且返回结果。
return executeSql(info, params);
} private MapperInfo getMapperInfo(Method method) throws Exception {
MapperInfo mapperInfo = SqlMappersHolder.INSTANCE.getMapperInfo(method.getDeclaringClass().getName(), method.getName());
if (mapperInfo == null) {
throw new Exception("Mapper not found for method: " +
method.getDeclaringClass().getName() + "." + method.getName());
}
return mapperInfo;
} private Object executeSql(MapperInfo info, Object[] params)
throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Object result = null;
PreparedStatement pstat = ConnectionManager.get().prepareStatement(info.getSql());
for (int i = 0; i < params.length; i++) {
pstat.setObject(i + 1, params[i]);
} if (info.getQueryType() == QueryType.SELECT) {
ResultSet rs = pstat.executeQuery();
rs.first();
// 将查询结果映射为ava类或基本数据类型
// 目前简化版仅支持String和int两种类型
if (rs.getMetaData().getColumnCount() == 1) {
switch (info.getResultType()) {
case "int":
result = rs.getInt(1);
break;
default:
result = rs.getString(1);
}
} else {
Class<?> resultTypeClass = Class.forName(info.getResultType());
Object inst = resultTypeClass.newInstance();
for (Field field : resultTypeClass.getDeclaredFields()) {
String setterName = "set" +
field.getName().substring(0, 1).toUpperCase() +
field.getName().substring(1);
Method md; switch (field.getType().getSimpleName()) {
case "int":
// 获取方法
md = resultTypeClass.getMethod(setterName, new Class[]{int.class});
// 反射执行方法对 field进行赋值。
md.invoke(inst, rs.getInt(field.getName()));
break;
/* 其他类型还没来得及测试,后面修复
case "long":
md = resultTypeClass.getMethod(setterName, new Class[]{long.class});
md.invoke(inst, rs.getLong(field.getName()));
break;*/
case "string":
md = resultTypeClass.getMethod(setterName, new Class[]{String.class});
md.invoke(inst, rs.getString(field.getName()));
break; default:
md = resultTypeClass.getMethod(setterName, new Class[]{String.class});
md.invoke(inst, rs.getString(field.getName()));
}
}
result = inst;
}
} else {
result = pstat.executeUpdate();
}
pstat.close();
return result;
} }

  7.2生成动态代理对象。

 package com.jtfr.core;

 import java.lang.reflect.Proxy;

 public class SqlSession {

     @SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> cls) {
return (T) Proxy.newProxyInstance(cls.getClassLoader(),
new Class[]{cls},
new SqlExecuteHandler());
}
}

剩下的待补充

手写Mybatis,还需要后面调整下的更多相关文章

  1. 要想精通Mybatis?从手写Mybatis框架开始吧!

    1.Mybatis组成 动态SQL Config配置 Mapper配置 2.核心源码分析 Configuration源码解析 SqlSessionFactory源码解析 SqlSession源码解析 ...

  2. 手写mybatis框架笔记

    MyBatis 手写MyBatis流程 架构流程图 封装数据 封装到Configuration中 1.封装全局配置文件,包含数据库连接信息和mappers信息 2.封装*mapper.xml映射文件 ...

  3. 手写MyBatis流程

    MyBatis 手写MyBatis流程 架构流程图 封装数据 封装到Configuration中 1.封装全局配置文件,包含数据库连接信息和mappers信息 2.封装*mapper.xml映射文件 ...

  4. ClownFish:比手写代码还快的通用数据访问层

    http://www.cnblogs.com/fish-li/archive/2012/07/17/ClownFish.html 阅读目录 开始 ClownFish是什么? 比手写代码还快的执行速度 ...

  5. JDK动态代理深入理解分析并手写简易JDK动态代理(下)

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-05/27.html 作者:夜月归途 出处:http://www.guitu ...

  6. 手写MyBatis ORM框架实践

    一.实现手写Mybatis三个难点 1.接口既然不能被实例化?那么我们是怎么实现能够调用的? 2.参数如何和sql绑定 3.返回结果 下面是Mybatis接口 二.Demo实现 1.创建Maven工程 ...

  7. 手写mybatis框架-增加缓存&事务功能

    前言 在学习mybatis源码之余,自己完成了一个简单的ORM框架.已完成基本SQL的执行和对象关系映射.本周在此基础上,又加入了缓存和事务功能.所有代码都没有copy,如果也对此感兴趣,请赏个Sta ...

  8. 带码农《手写Mybatis》进度3:实现映射器的注册和使用

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!

  9. jQuery手写几个常见的滑动下拉菜单 分分秒秒学习JS

    一般的企业网站再我们再实际工作中,有些特效,用jQuery来做,显得极其简单,除非一些大的公司,需要封装自己的类. 今天,我们讲解jQuery入门知识,来写几个简单jQuery滑动下拉菜单.感受一下j ...

随机推荐

  1. iOS多媒体总结&进入后台播放音乐

    1. 播放mp3需要导入框架,AVFoundation支持音频文件(.caf..aif..wav..wmv和.mp3)的播放. #import <AVFoundation/AVFoundatio ...

  2. [WC2012]记忆中的水杉树

    https://www.luogu.org/problemnew/show/P4125 题解 首先一开始所有的线段互不相交. 那么对于第二问来说,一定存在一种方法使得所有线段都朝着一个方向动. 比如说 ...

  3. vue组件传值之父传子

    1.父组件给子组件传值  home父组件  header子组件  关键字props home代码 <template> <div> <v-header :title=&q ...

  4. 如何正确安装Mysql

    1.官网去下载 2.针对操作系统的不同下载不同的版本  安装步骤: 第一步解压文件:位置为你想要安装的盘符第二步加载环境变量加载的是bin目录第三步初始化:在cmd终端中输入 mysqld --ini ...

  5. DB-MDM:MDM/主数据管理 百科

    ylbtech-DB-MDM:MDM/主数据管理 百科 主数据管理(MDM Master Data Management)描述了一组规程.技术和解决方案,这些规程.技术和解决方案用于为所有利益相关方( ...

  6. day31—CSS Reset 与页面居中布局

    转行学开发,代码100天——2018-04-16 报名的网易前端开发课程今天正式开课了,这也是毕业后首次付费进行的正式培训课程学习.以此,记录每天学习内容. 今天学了两个方面的知识: 1. CSS   ...

  7. JNI-java native interface(java本地接口)

    什么是JNI java native interface(java本地接口) ABI: application binary interface (应用程序二进制接口) 为什么要使用JNI * 复用很 ...

  8. hihoCoder 1014 : Trie树(字典树)

    传送门 Description 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小 ...

  9. 20190820 On Java8 第十章 接口

    第十章 接口 接口和抽象类提供了一种将接口与实现分离的更加结构化的方法. 抽象类和方法 包含抽象方法的类叫做抽象类.如果一个类包含一个或多个抽象方法,那么类本身也必须限定为抽象的,否则,编译器会报错. ...

  10. Codeforces 1097D (DP+分解质因数)

    题目 传送门 分析 考虑\(n=p^q\)且p为质数的情况 设dp[i][j]表示经过i次变化后数为\(p^j\)的概率 则初始值dp[0][q]=1 状态转移方程为\(dp[i][j]=\sum{} ...