博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
手写Mybatis,还需要后面调整下
阅读量:5019 次
发布时间:2019-06-12

本文共 12076 字,大约阅读时间需要 40 分钟。

参考博客

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

个人理解

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

实现步骤

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

4.0.0
com.jtfr
SimpleMybatis
0.0.1-SNAPSHOT
jar
SimpleMybatis
http://maven.apache.org
UTF-8
dom4j
dom4j
1.6.1
mysql
mysql-connector-java
5.1.47

2.创建测试表

1 CREATE DATABASE db_test; 2   3 USE db_test; 4   5 CREATE TABLE `tb_user` ( 6   `id` INT(11) NOT NULL, 7   `name` VARCHAR(20) NOT NULL, 8   `age` TINYINT(4) DEFAULT '0', 9   `addr` VARCHAR(20) NOT NULL,10   PRIMARY KEY (`id`)11 ) ENGINE=INNODB DEFAULT CHARSET=utf8mb412 13 INSERT INTO tb_user(id,NAME,age,addr) VALUES(1,'ckm1', 100, 'sz1');14 INSERT INTO tb_user(id,NAME,age,addr) VALUES(2,'ckm2', 100, 'sz2');15 INSERT INTO tb_user(id,NAME,age,addr) VALUES(3,'ckm3', 100, 'sz3');16 INSERT INTO tb_user(id,NAME,age,addr) VALUES(4,'ckm4', 100, 'sz4');17 INSERT INTO tb_user(id,NAME,age,addr) VALUES(5,'ckm5', 100, 'sz5');18 COMMIT;

3.创建测试用的实体类

1 package com.jtfr.entity; 2  3 /** 4  * 用户实体 5  * @author ckm 6  * 7  */ 8 public class User { 9     10     private int id;11     private String name;12     private int age;13     private String addr;14  15     public int getId() {16         return id;17     }18  19     public void setId(int id) {20         this.id = id;21     }22  23     public String getName() {24         return name;25     }26  27     public void setName(String name) {28         this.name = name;29     }30  31     public int getAge() {32         return age;33     }34  35     public void setAge(int age) {36         this.age = age;37     }38  39     public String getAddr() {40         return addr;41     }42  43     public void setAddr(String addr) {44         this.addr = addr;45     }46  47  48     @Override49     public String toString() {50         return "User{" +51                 "id=" + id +52                 ", name='" + name + '\'' +53                 ", age=" + age +54                 ", addr='" + addr + '\'' +55                 '}';56     }57 }

4.创建操作的接口

1 package com.jtfr.dao; 2  3 import com.jtfr.entity.User; 4  5 public interface UserDao { 6       7     User getUserInfo(int id); 8   9     int updateUserName(String newName, int id);10  11     int insertUser(int id, String name, int age, String addr);12 }

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

1 
2
3
7 8
9 update tb_user set name = ?10 where id = ?11
12 13
14 insert into tb_user15 values(?, ?, ?, ?);16
17

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

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

1 package com.jtfr.core; 2  3 public class MapperInfo { 4   5     private QueryType queryType; 6     private String interfaceName; 7     private String methodName; 8     private String sql; 9     private String resultType;10  11     public QueryType getQueryType() {12         return queryType;13     }14  15     public void setQueryType(QueryType queryType) {16         this.queryType = queryType;17     }18  19     public String getSql() {20         return sql;21     }22  23     public void setSql(String sql) {24         this.sql = sql;25     }26  27     public String getResultType() {28         return resultType;29     }30  31     public void setResultType(String resultType) {32         this.resultType = resultType;33     }34  35     public String getInterfaceName() {36         return interfaceName;37     }38  39     public void setInterfaceName(String interfaceName) {40         this.interfaceName = interfaceName;41     }42  43     public String getMethodName() {44         return methodName;45     }46  47     public void setMethodName(String methodName) {48         this.methodName = methodName;49     }50  51     @Override52     public String toString() {53         return "MapperInfo{" +54                 "queryType=" + queryType +55                 ", interfaceName='" + interfaceName + '\'' +56                 ", methodName='" + methodName + '\'' +57                 ", sql='" + sql + '\'' +58                 ", resultType='" + resultType + '\'' +59                 '}';60     }61 }

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

1 package com.jtfr.core; 2  3 public enum QueryType { 4     SELECT, UPDATE, INSERT, DELETE; 5   6     /** 7      * 这里的作用,判断标签是否是四种其中的一种 8      * @param v 9      * @return10      */11     public static QueryType value(String v) {12         return valueOf(v.toUpperCase());13     }14 }

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

1 package com.jtfr.core; 2  3 import java.io.File; 4 import java.util.HashMap; 5 import java.util.Map; 6  7 import org.dom4j.Document; 8 import org.dom4j.DocumentException; 9 import org.dom4j.Element;10 import org.dom4j.io.SAXReader;11 12 public enum SqlMappersHolder {13      14     INSTANCE;15  16     private Map
mi = null;17 18 SqlMappersHolder() {19 if (mi != null)20 return;21 mi = new HashMap
();22 // 获取mapper.xml文件所在路径23 File dir = new File(SqlMappersHolder.class.getClassLoader().getResource(Config.DEFAULT.getMapperPath()).getFile());24 25 // 用dom4j解析26 SAXReader reader = new SAXReader();27 try {28 for (String file : dir.list()) {29 // 读取mapper.xml30 Document doc = reader.read(new File(dir, file));31 Element root = doc.getRootElement();32 // 获取接口全路径名33 String className = root.attributeValue("namespace");34 35 for (Object o : root.elements()) {36 Element e = (Element) o;37 38 MapperInfo info = new MapperInfo();39 info.setQueryType(QueryType.value(e.getName()));40 info.setInterfaceName(className);41 info.setMethodName(e.attributeValue("id"));42 info.setResultType(e.attributeValue("resultType"));43 info.setSql(e.getText());44 45 mi.put(idOf(className, e.attributeValue("id")), info);46 }47 }48 } catch (DocumentException e) {49 e.printStackTrace();50 }51 }52 53 public MapperInfo getMapperInfo(String className, String methodName) {54 return mi.get(idOf(className, methodName));55 }56 57 /*58 * 接口全路径+"."+方法名作为唯一id59 */60 private String idOf(String className, String methodName) {61 return className + "." + methodName;62 }63 }

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

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

  

1 package com.jtfr.core; 2  3 import java.lang.reflect.Field; 4 import java.lang.reflect.InvocationHandler; 5 import java.lang.reflect.InvocationTargetException; 6 import java.lang.reflect.Method; 7 import java.sql.PreparedStatement; 8 import java.sql.ResultSet; 9 import java.sql.SQLException;10 11 public class SqlExecuteHandler implements InvocationHandler {12  13     public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {14         // 传入的要被执行的接口方法,通过获取接口名和方法名,然后获取要MapperInfo信息。15         MapperInfo info = getMapperInfo(method);16  17         // 通过MapperInfo和传入的参数,执行Sql语句,并且返回结果。18         return executeSql(info, params);19     }20  21     private MapperInfo getMapperInfo(Method method) throws Exception {22         MapperInfo mapperInfo = SqlMappersHolder.INSTANCE.getMapperInfo(method.getDeclaringClass().getName(), method.getName());23         if (mapperInfo == null) {24             throw new Exception("Mapper not found for method: " +25                     method.getDeclaringClass().getName() + "." + method.getName());26         }27         return mapperInfo;28     }29  30     private Object executeSql(MapperInfo info, Object[] params)31             throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {32         Object result = null;33         PreparedStatement pstat = ConnectionManager.get().prepareStatement(info.getSql());34         for (int i = 0; i < params.length; i++) {35             pstat.setObject(i + 1, params[i]);36         }37  38  39         if (info.getQueryType() == QueryType.SELECT) {40             ResultSet rs = pstat.executeQuery();41             rs.first();42             // 将查询结果映射为ava类或基本数据类型43             // 目前简化版仅支持String和int两种类型44             if (rs.getMetaData().getColumnCount() == 1) {45                 switch (info.getResultType()) {46                     case "int":47                         result = rs.getInt(1);48                         break;49                     default:50                         result = rs.getString(1);51                 }52             } else {53                 Class
resultTypeClass = Class.forName(info.getResultType());54 Object inst = resultTypeClass.newInstance();55 for (Field field : resultTypeClass.getDeclaredFields()) {56 String setterName = "set" +57 field.getName().substring(0, 1).toUpperCase() +58 field.getName().substring(1);59 Method md;60 61 switch (field.getType().getSimpleName()) {62 case "int":63 // 获取方法64 md = resultTypeClass.getMethod(setterName, new Class[]{
int.class});65 // 反射执行方法对 field进行赋值。66 md.invoke(inst, rs.getInt(field.getName()));67 break;68 /* 其他类型还没来得及测试,后面修复69 case "long":70 md = resultTypeClass.getMethod(setterName, new Class[]{long.class});71 md.invoke(inst, rs.getLong(field.getName()));72 break;*/73 case "string":74 md = resultTypeClass.getMethod(setterName, new Class[]{String.class});75 md.invoke(inst, rs.getString(field.getName()));76 break;77 78 79 80 default:81 md = resultTypeClass.getMethod(setterName, new Class[]{String.class});82 md.invoke(inst, rs.getString(field.getName()));83 }84 }85 result = inst;86 }87 } else {88 result = pstat.executeUpdate();89 }90 pstat.close();91 return result;92 }93 94 }

  7.2生成动态代理对象。

1 package com.jtfr.core; 2  3 import java.lang.reflect.Proxy; 4  5 public class SqlSession { 6   7     @SuppressWarnings("unchecked") 8     public 
T getMapper(Class
cls) { 9 return (T) Proxy.newProxyInstance(cls.getClassLoader(),10 new Class[]{cls},11 new SqlExecuteHandler());12 }13 }

剩下的待补充

 

转载于:https://www.cnblogs.com/jtfr/p/11020611.html

你可能感兴趣的文章
Nodejs学习总结 -Express入门(一)
查看>>
web前端优化
查看>>
ssh 连接原理及ssh-keygen
查看>>
vs2013编译qt程序后中文出现乱码
查看>>
【转】IOS数据库操作SQLite3使用详解
查看>>
Android官方技术文档翻译——ApplicationId 与 PackageName
查看>>
设计网站大全
查看>>
JVM CUP占用率过高排除方法,windows环境
查看>>
【转】JAVA字符串格式化-String.format()的使用
查看>>
【转】ButterKnife基本使用--不错
查看>>
【转】VS2012编译出来的程序,在XP上运行,出现“.exe 不是有效的 win32 应用程序” “not a valid win32 application”...
查看>>
函数中关于const关键字使用的注意事项
查看>>
微信架构(转)
查看>>
Web项目中的路径问题
查看>>
js随机数的取整
查看>>
关于解析漏洞
查看>>
十大经典预测算法(六)---集成学习(模型融合算法)
查看>>
用php做一个简单的注册用户功能
查看>>
一款基于css3的3D图片翻页切换特效
查看>>
Feign使用Hystrix无效原因及解决方法
查看>>