day03-自己实现Mybatis底层机制-02
自己实现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); }

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!