手写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() {@Overridepublic 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> {@Overridepublic void setParameter(PreparedStatement statement, int i, Integer value) throws SQLException {statement.setInt(i,value);}@Overridepublic 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.//重置复用expressionif (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();}
}