最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

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文件

        

(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@ToStringpublic 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@ToStringpublic class MapperBean {    private String interfaceName;//接口名    //接口下的所有方法    public List 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 list = new ArrayList<>();        while (rootIterator.hasNext()) {            //取出一个子元素            /**             *              */            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)测试

@Testpublic 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 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  * @return */public  T getMapper(Class 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)测试

@Testpublic 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);}

关键词: 任务阶段 测试结果