自己实现Mybatis底层机制-02

7.任务阶段4&5

阶段4任务:开发Mapper接口和Mapper.xml

阶段5任务:开发和Mapper接口相映射的MapperBean

(1)Mapper接口

package com.li.mapper;

import com.li.entity.Monster;

/**
* @author 李
* @version 1.0
* MonsterMapper:声明对数据库的crud方法
*/
public interface MonsterMapper {
//查询方法
public Monster getMonsterById(Integer id); }

(2)Mapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.li.mapper.MonsterMapper">
<!--实现配置接口方法getMonsterById-->
<select id="getMonsterById" resultType="com.li.entity.Monster">
select * from monster where id = ?
</select>
</mapper>

(3)Function.java,用于记录Mapper.xml文件实现的方法信息

package com.li.limybatis.config;

import lombok.Getter;
import lombok.Setter; /**
* @author 李
* @version 1.0
* Function:记录对应 Mapper.xml的方法信息
*/
@Getter
@Setter
@ToString
public class Function {
private String sqlType;//sql类型,如select,update,insert,delete
private String funcName;//方法名
private String sql;//执行的sql语句
private Object resultType;//返回类型
private String parameterType;//参数类型
}

(4)MapperBean.java,作用是读取Mapper接口对应的Mapper.xml,将该xml文件方法信息封装到MapperBean中。

package com.li.limybatis.config;

import lombok.Getter;
import lombok.Setter; import java.util.List; /**
* @author 李
* @version 1.0
* MapperBean:将我们的Mapper信息,进行封装
*/
@Setter
@Getter
@ToString
public class MapperBean {
private String interfaceName;//接口名
//接口下的所有方法
public List<Function> functions;
}

8.任务阶段6

阶段6任务:在MyConfiguration中读取xxMapper.xml,能够创建MapperBean对象

(1)修改 MyConfiguration.java,添加 readMapper() 方法

/**
* 读取xxMapper.xml,创建MapperBean对象
* @param path xml的路径+文件名,从类的加载路径开始计算,若xml文件放在resource目录下,直接传入文件名即可
* @return 返回MapperBean对象
*/
public MapperBean readMapper(String path) {
MapperBean mapperBean = new MapperBean();
try {
//获取到mapper.xml文件对应的InputStream
InputStream stream = loader.getResourceAsStream(path);
SAXReader reader = new SAXReader();
//获取到xml文件对应的document
Document document = reader.read(stream);
//得到xml的根节点
Element root = document.getRootElement();
//获取到 namespace
String namespace = root.attributeValue("namespace").trim();
//设置mapperBean的属性interfaceName
mapperBean.setInterfaceName(namespace);
//遍历获取root的子节点-生成 Function
Iterator rootIterator = root.elementIterator();
//保存接口下的所有方法信息
List<Function> list = new ArrayList<>();
while (rootIterator.hasNext()) {
//取出一个子元素
/**
* <select id="getMonsterById" resultType="com.li.entity.Monster">
* select * from monster where id = ?
* </select>
*/
Element e = (Element) rootIterator.next();
Function function = new Function();
String sqlType = e.getName().trim();
String funcName = e.attributeValue("id").trim();
//这里的resultType是返回类型的全路径-全类名
String resultType = e.attributeValue("resultType").trim();
String sql = e.getText().trim();
//将信息封装到 function对象中
function.setSql(sql);
function.setFuncName(funcName);
function.setSqlType(sqlType);
//这里的function.resultType应该为Object类型
//因此使用反射生成对象,再放入function中
Object instance = Class.forName(resultType).newInstance();
function.setResultType(instance);
//将封装好的function对象放到list中
list.add(function);
} mapperBean.setFunctions(list);
} catch (Exception e) {
e.printStackTrace();
}
return mapperBean;
}

(2)测试

@Test
public void readMapper() {
MyConfiguration myConfiguration = new MyConfiguration();
MapperBean mapperBean = myConfiguration.readMapper("MonsterMapper.xml");
System.out.println("mapperBean=" + mapperBean);
}

测试结果:

mapperBean=MapperBean(interfaceName=com.li.mapper.MonsterMapper, functions=[Function(sqlType=select, funcName=getMonsterById, sql=select * from monster where id = ?, resultType=Monster(id=null, age=null, name=null, email=null, birthday=null, salary=0.0, gender=null), parameterType=null)])

9.任务阶段7

阶段7任务:实现动态代理Mapper的方法-动态代理生成Mapper对象,调用MyExecutor方法

(1)MyMapperProxy.java

package com.li.limybatis.sqlsession;

import com.li.limybatis.config.Function;
import com.li.limybatis.config.MapperBean; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List; /**
* @author 李
* @version 1.0
* MyMapperProxy:动态代理生成 Mapper对象,调用 MyExecutor方法
*/
public class MyMapperProxy implements InvocationHandler {
private MySqlSession mySqlSession;
private String mapperFile;
private MyConfiguration myConfiguration; //构造器
public MyMapperProxy(MySqlSession mySqlSession, MyConfiguration myConfiguration, Class clazz) {
this.mySqlSession = mySqlSession;
this.myConfiguration = myConfiguration;
this.mapperFile = clazz.getSimpleName() + ".xml";
} //当执行Mapper接口的代理对象方法时,会执行到invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MapperBean mapperBean = myConfiguration.readMapper(this.mapperFile);
//判断是否是xml文件对应的接口
if (!method.getDeclaringClass().getName().equals(mapperBean.getInterfaceName()))
{
//通过method拿到执行的方法所在的接口的名称,与MapperBean存放的接口名比较
return null;
}
//取出MapperBean的functions
List<Function> functions = mapperBean.getFunctions();
//判断当前mapperBean解析对应的XML文件后,有方法
if (null != functions && 0 != functions.size()) {
for (Function function : functions) {
//如果当前要执行的方法和function.getFuncName()一样
//说明我们可以从当前遍历的function对象中,取出相应的信息sql,并执行方法
if (method.getName().equals(function.getFuncName())) {
//如果当前function要执行的SqlType是select,就去执行selectOne
/*
* 说明:
* 1.如果要执行的方法是select,就对应执行selectOne
* 因为我们在MySqlSession只写了一个方法(selectOne)
* 2.实际上原生的MySqlSession中应该有很多的方法,只是这里简化了,
* 实际上应该根据不同的匹配情况调用不同的方法,并且还需要进行参数解析处理,
* 还有比较复杂的字符串处理,拼接sql,处理返回类型等工作
* 3.因为这里主要想实现mybatis生成mapper动态代理对象,调用方法的机制,所以简化
*/
if ("select".equalsIgnoreCase(function.getSqlType())) {
return mySqlSession
.selectOne(function.getSql(), String.valueOf(args[0]));
}
}
}
}
return null;
}
}

(2)修改MySqlSession.java,添加方法,返回动态代理对象

/**
* 1.回 mapper的动态代理对象
* 2.这里的 clazz到时传入的类似 MonsterMapper.class
* 3.返回的就是 MonsterMapper 接口的代理对象
* 4.当执行接口方法时(通过代理对象调用),
* 根据动态代理机制会执行到MyMapperProxy的invoke()方法
* @param clazz
* @param <T>
* @return
*/
public <T> T getMapper(Class<T> clazz) {
//返回动态代理对象
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz},
new MyMapperProxy(this, myConfiguration, clazz));
}

(3)创建 MySessionFactory.java

package com.li.limybatis.sqlsession;

/**
* @author 李
* @version 1.0
* MySessionFactory-会话工厂-返回会话SqlSession
*/
public class MySessionFactory {
public static MySqlSession openSession() {
return new MySqlSession();
}
}

(4)测试

@Test
public void openSession() {
MySqlSession mySqlSession = MySessionFactory.openSession();
MonsterMapper mapper = mySqlSession.getMapper(MonsterMapper.class);
System.out.println("mapper的运行类型=" + mapper.getClass());
Monster monster = mapper.getMonsterById(1);
System.out.println("monster--" + monster);
}

day03-自己实现Mybatis底层机制-02的更多相关文章

  1. day12-实现Spring底层机制-02

    实现Spring底层机制-02 3.实现任务阶段1 3.1知识拓展-类加载器 Java的类加载器有三种: Bootstrap类加载器 ----- 对应路径 jre/lib Ext类加载器 ----- ...

  2. 聊聊MyBatis缓存机制【美团-推荐】

    聊聊MyBatis缓存机制 2018年01月19日 作者: 凯伦 文章链接 18778字 38分钟阅读 前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用My ...

  3. 聊聊MyBatis缓存机制

    https://tech.meituan.com/mybatis_cache.html 前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用MyBatis的默认 ...

  4. 【转】MyBatis缓存机制

    转载:https://blog.csdn.net/bjweimengshu/article/details/79988252. 本文转载自公众号 美团技术点评 前言 MyBatis是常见的Java数据 ...

  5. MyBatis 缓存机制(十三)

    什么是缓存 缓存就是内存中的一个对象,用于对数据库查询结果的保存,用于减少与数据库的交互次数从而降低数据库的压力,进而提高响应速度. MyBatis 缓存机制原理 Mybatis 缓存机制原理是将第一 ...

  6. MyBatis缓存机制[NO]

    前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用MyBatis的默认缓存配置,但是MyBatis缓存机制有一些不足之处,在使用中容易引起脏数据,形成一些潜在 ...

  7. mybatis底层源码分析之--配置文件读取和解析

    现在企业级开发中ssm是很常见的技术标配,mybatis比hibernate轻量了很多,而且学习成本相对较低,简单易上手. 那么,问题来了,简单好用的mybatis底层到底是如何实现的呢?都使用了什么 ...

  8. mybatis源代码分析:mybatis延迟加载机制改进

    在上一篇博客<mybatis源代码分析:深入了解mybatis延迟加载机制>讲诉了mybatis延迟加载的具体机制及实现原理. 可以看出,如果查询结果对象中有一个属性是需要延迟加载的,那整 ...

  9. 《深入理解mybatis原理》 Mybatis初始化机制具体解释

    对于不论什么框架而言.在使用前都要进行一系列的初始化,MyBatis也不例外. 本章将通过下面几点具体介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XM ...

  10. mybatis缓存机制

    目录 mybatis缓存机制 Executor和缓存 一级缓存 小结 二级缓存 小结 mybatis缓存机制 mybatis支持一.二级缓存来提高查询效率,能够正确的使用缓存的前提是熟悉mybatis ...

随机推荐

  1. php批量更新多条数据

    /** * @param $table 表名 * @param array $multipleData 拼接的批量更新的数组格式: * $students = [['id' => 1, 'cit ...

  2. CF526F Pudding Monsters 题解

    题目链接:CF 或者 洛谷 析合树真是连续段问题的降智神器 先看下题目的一些特殊性,每行每列恰好有一个棋子.考虑特殊性,\(n \times n\) 的棋盘,那么就该判断是否有 \(n\) 个棋子,容 ...

  3. HP T520 改装DoraOS瘦客户机系统评测

    HP T520 介绍 HP T520是一款瘦客户机产品.采用AMD GX-212JC 1.2 GHz 双核 SOC APU,带 AMD Radeon HD Graphics.配置4G 内存,8G SS ...

  4. 案例:使用sqlplus登录报ORA-12547错误

    现象:Exadata刷机之后grid/oracle用户的环境变量是没有设置的,需要手工进行设置,设置完成后发现grid用户执行报错ORA-12547: [grid@dbm0dbadm01 ~]$ sq ...

  5. SpringBoot-MyBatis - Java枚举类型 <---> MySQL Int,建立 类型处理器(typeHandlers)

    场景: MySQL里的某一个字段,比如:status状态,一共有5个状态,我们会在MySQL里 建立 status(int)字段,1.2.3.4.5 来标记5种状态:利用MyBatis在自动代码生成器 ...

  6. Apache Hudi 设计与架构解读

    1. 简介 Apache Hudi(简称:Hudi)允许您在现有的hadoop兼容存储之上存储大量数据,同时提供两种原语,使得除了经典的批处理之外,还可以在数据湖上进行流处理. 这两种原语分别是: 1 ...

  7. Net5 WorkService 继承 Quarzt 以及 Net5处理文件上传

    Net5 版本以Core为底层非framework框架的windowservice 服务. 在VS里叫WorkService 可以以CMD方式运行也可以以Windowservice方式运行,部署简单. ...

  8. 初步体验通过 Semantic Kernel 与自己部署的通义千问开源大模型进行对话

    春节之前被 Semantic Kernel 所吸引,开始了解它,学习它. 在写这篇博文之前读了一些英文博文,顺便在这里分享一下: Intro to Semantic Kernel – Part One ...

  9. JS Leetcode 530. 二叉搜索树的最小绝对差 题解分析,再次了解中序遍历

    壹 ❀ 引 本题来自LeetCode 783. 二叉搜索树节点最小距离,题目描述如下: 给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 . 示例 1: 输入:root ...

  10. NC21467 [NOIP2018]货币系统

    题目链接 题目 题目描述 在网友的国度中共有n种不同面额的货币,第i种货币的面额为a[i],你可以假设每一种货币都有无穷多张.为了方便,我们把货币种数为n.面额数组为a[1..n]的货币系统记作(n, ...