手写mybatis

手写mybatis
mybatis仓库地址点击文字即可跳转

package com.mybatis.factory;

import com.mybatis.mapper.ParameterMapping;
import com.mybatis.retention.Selcet;
import com.mybatis.token.GenericTokenParser;
import com.mybatis.token.ParameterMappingTokenHandler;
import com.mybatis.token.TypeHander;
import com.mybatis.type.*;
import com.mybatis.util.MapperUtil;
import java.lang.reflect.*;
import java.sql.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class MapperProxyFactory {
    private static Map<Class, TypeHander> typeHanderMap=new ConcurrentHashMap<>();

    //第一种请求方式
    static {
        typeHanderMap.put(String.class,new StringTypeHander());
        typeHanderMap.put(Integer.class,new IntegerTypeHander());
        try {
            Class.forName("come.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException classNotFoundException) {
            classNotFoundException.printStackTrace();
        }
    }

    public static <T> T getMapper(Class<T> tClass){

     return(T) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{tClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //处理sql
                //第一种sql链接方式
                Connection connection = MapperUtil.getConnection();
                //第二种
              //  Connection connection1 = DataSourceFactory.connection();
                Map<String, Object> map = MapperUtil.getStringObjectMap(method, args);
                ParameterMappingTokenHandler parameterMappingTokenHandler=new ParameterMappingTokenHandler();
                GenericTokenParser parser=new GenericTokenParser("#{","}",parameterMappingTokenHandler);

                List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
                PreparedStatement statement = connection.prepareStatement(MapperUtil.sql(method,parser));

                updateStatement(map, parameterMappings, statement);
                statement.execute();

                if (!method.getAnnotations()[0].equals(Selcet.class)){
                    connection.close();
                    return null;
                }

                Class resultType=null;
                resultType = MapperUtil.getResultType(method, resultType);

                ResultSet resultSet = statement.getResultSet();
                List<String> columList =MapperUtil. getColumList(resultSet);

                Map<String, Method> methodMap = MapperUtil.getStringMethodMap(resultType);
                Object result=null;
                List<Object> list=new ArrayList<>();
                Map<Integer,Object> handerMap=new ConcurrentHashMap<>();
                while (resultSet.next()){
                    if (resultType!=null) {
                        Object instance = resultType.newInstance();
                        for (int i=0;i<columList.size();i++){
                            selecteInstance(resultSet, columList, methodMap, instance, i);
                            list.add(instance);
                            handerMap.put(i,instance);
                        }
                    }
                }

                result = MapperUtil.getResult(method, list, handerMap);
                connection.close();
                return result;
            }
        });
    }
    private static void selecteInstance(ResultSet resultSet, List<String> columList, Map<String, Method> methodMap, Object instance, int i) throws IllegalAccessException, InvocationTargetException, SQLException {
        String column = columList.get(i);
        Method set = methodMap.get(column);
        Class<?> parameterType = set.getParameterTypes()[0];
        TypeHander typeHander = typeHanderMap.get(parameterType);
        set.invoke(instance,typeHander.getResult(resultSet,column));
    }

    private static void updateStatement(Map<String, Object> map, List<ParameterMapping> parameterMappings, PreparedStatement statement) throws SQLException {
        for (int i = 0; i< parameterMappings.size(); i++){
            String parameter= parameterMappings.get(i).getProperty();
            Object o = map.get(parameter);
            Class<?> aClass = o.getClass();
            typeHanderMap.get(aClass).setParameter(statement,i+1,o);
        }
    }
}

package com.mybatis.type;

import com.mybatis.token.TypeHander;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class IntegerTypeHander implements TypeHander<Integer> {
    @Override
    public void setParameter(PreparedStatement statement, int i, Integer value) throws SQLException {
        statement.setInt(i,value);
    }

    @Override
    public Integer getResult(ResultSet resultSet, String cloumn) throws SQLException {
        return resultSet.getInt(cloumn);
    }
}
package com.mybatis.token;

public class GenericTokenParser {
    /**
     * 占位符开始标志
     */
    private final String openToken;
    /**
     * 占位符结束标志
     */
    private final String closeToken;
    /**
     *
     */
    private final TokenHandler handler;

    public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
        this.openToken = openToken;
        this.closeToken = closeToken;
        this.handler = handler;
    }

    /**
     *这段代码主要处理{@code text}拥有多个符合以{@code openToken}开头,{@code closeToken}结尾的字符窜的情况
     *
     * 同时还要处理拥有{@code openToken}{@code closeToken},但是使用了转义字符的情况。
     *
     * @param text
     * @return
     */
    public String parse(String text) {
        //非空判断
        if (text == null || text.isEmpty()) {
            return "";
        }
        // search open token
        //获取第一个{@code openToken}的位置
        int start = text.indexOf(openToken);
        //如果这个位置不存在 则直接返回原字符窜
        if (start == -1) {
            return text;
        }
        //生成原字符窜的数组
        char[] src = text.toCharArray();
        //字符窜拥有多个符合条件的{@code openToken}时,将会进行多轮分析,以确认每一轮的{@code openToken}在原字符窜的位置,而
        //offset就表示每一轮解析时,应该从原字符窜的哪个位置开始
        int offset = 0;
        //builder是拼接最后结果,进行输出的
        final StringBuilder builder = new StringBuilder();
        //expression的内容表示{@code openToken}{@code closeToken}之间的内容
        StringBuilder expression = null;
        //下面这个循环 就是循环处理多个{@code openToken}{@code closeToken}的情况
        while (start > -1) {

            if (start > 0 && src[start - 1] == '\\') {
                //寻找{@code openToken}的条件分支一:这一个条件判断 是处理出现了{@code openToken},但是这个{@code openToken}前面出现了转移字符
                // this open token is escaped. remove the backslash and continue.
                //这里表示既然遇到了转义字符 那么这个开始标识符不能当做开始标识符
                // 因此它不是需要替换的部分,所以就要将从本轮开启的位置 到{@code openToken}结束位置的字符都直接拼接到{@code builder}上
                builder.append(src, offset, start - offset - 1).append(openToken);
                //确认新一轮的开始位置
                offset = start + openToken.length();
            } else {
                //寻找{@code openToken}的条件分支二:下面的条件判断表示 出现了{@code openToken} 且 这个{@code openToken}前面没有转移字符的情况===
                // found open token. let's search close token.
                //重置复用expression
                if (expression == null) {
                    expression = new StringBuilder();
                } else {
                    expression.setLength(0);
                }
                //这里表示如果有转义字符 则拼接转义的开始字符到真正的开始字符之间的部分
                builder.append(src, offset, start - offset);
                //{@code openToken}找到了,接下来来需要找{@code closeToken},其实{@code closeToken}的状况和{@code openToken}
                //一样的情况
                offset = start + openToken.length();
                int end = text.indexOf(closeToken, offset);
                //遍历循环一直找{@code closeToken}的位置
                while (end > -1) {
                    if (end > offset && src[end - 1] == '\\') {
                        //寻找{@code closeToken}的分支条件一:如果找到的{@code closeToken}是拥有转义字符的,则继续寻找,但是expression需要拼接本轮解析开始
                        //位置到{@code openToken}间的字符,因为这个也属于{@code openToken}{@code closeToken}间的内容,然后进行下一轮
                        // this close token is escaped. remove the backslash and continue.
                        expression.append(src, offset, end - offset - 1).append(closeToken);
                        offset = end + closeToken.length();
                        end = text.indexOf(closeToken, offset);
                    } else {
                        //寻找{@code closeToken}的分支条件二:这里表示找到了符合条件的{@code closeToken},那么将内容拼接到{@code expression}里
                        expression.append(src, offset, end - offset);
                        break;
                    }
                }
                if (end == -1) {
                    //综合评定 条件分支一:{@code closeToken}位置没有找到,那么结束了,直接拼接
                    // close token was not found.
                    builder.append(src, start, src.length - start);
                    offset = src.length;
                } else {
                    //综合评定 条件分支二:{@code closeToken}位置也找到了,那么说明expression里也存放好了{@code openToken}{@code closeToken}
                    //内容,这时候用handler去处理。
                    builder.append(handler.handleToken(expression.toString()));
                    offset = end + closeToken.length();
                }
            }
            //这里表示 从offset位置 从新获取start的位置,很显然如果为0,start还是不变
            start = text.indexOf(openToken, offset);
        }
        if (offset < src.length) {
            builder.append(src, offset, src.length - offset);
        }
        return builder.toString();
    }
}

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值