手写Mybatis,还需要后面调整下

参考博客

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包。

<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.创建测试表

 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=utf8mb4
12 
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     @Override
49     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 <?xml version="1.0" encoding="UTF-8" ?>
 2 <mapper namespace="com.jtfr.dao.UserDao">
 3     <select id="getUserInfo" resultType="com.jtfr.entity.User">
 4         select * from tb_user
 5         where id = ?;
 6     </select>
 7  
 8     <update id="updateUserName">
 9         update tb_user set name = ?
10         where id = ?
11     </update>
12  
13     <insert id="insertUser">
14         insert into tb_user
15         values(?, ?, ?, ?);
16     </insert>
17 </mapper>

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     @Override
52     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      * @return
10      */
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<String, MapperInfo> mi = null;
17  
18     SqlMappersHolder() {
19         if (mi != null)
20             return;
21         mi = new HashMap<String, MapperInfo>();
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.xml
30                 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      * 接口全路径+"."+方法名作为唯一id
59      */
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> T getMapper(Class<T> cls) {
 9         return (T) Proxy.newProxyInstance(cls.getClassLoader(),
10                 new Class[]{cls},
11                 new SqlExecuteHandler());
12     }
13 }

剩下的待补充

猜你喜欢

转载自www.cnblogs.com/jtfr/p/11020611.html