简易Mybatis

实现简易Mybatis

MyBatis介绍

MyBatis 作为持久层框架,主要解决了传统 JDBC 操作中的繁琐和重复性工作,提高了开发效率,同时也提供了灵活的 SQL 编写和结果映射功能,使得开发人员可以更加专注于业务逻辑的实现。

这篇文章将会带着大家来实现一个简易的MyBatis,实现MyBatis中的:

  • 简化数据库操作
  • 灵活的SQL编写
  • 参数映射
  • 结果集处理

JDBC

我们知道MyBatis是JDBC的一个再封装,那么接下来我会先贴出一段JDBC的代码,在这个基础上进行封装。
实体类

public class User {
    private Integer id;
    private String name;
    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

JDBC
获取链接

public class GetJDBC {
    // 数据库连接相关信息
    private static final String jdbcUrl = "jdbc:mysql://localhost:3306/test";
    private static final String user = "root";
    private static final String password = "root";

    // 获取数据库连接的方法
    public static Connection getConnection() throws SQLException {
        // 注册 JDBC 驱动程序
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new SQLException("JDBC Driver not found", e);
        }

        // 获取连接对象
        return DriverManager.getConnection(jdbcUrl, user, password);
    }
}

JDBC查询代码例子

		Connection connection = GetJDBC.getConnection();
        PreparedStatement statement = connection.prepareStatement("select * from user where name = ?");
        statement.setString(1,"张三");
        ResultSet resultSet = statement.executeQuery();
        List<User> list = new ArrayList<>();
        while(resultSet.next()){
            User user = new User();
            user.setAge(resultSet.getInt("age"));
            user.setId(resultSet.getInt("id"));
            user.setName(resultSet.getString("name"));
            list.add(user);
        }
        System.out.println(list);

实现

接下来需要我们要开始对以上代码进行再处理了。
首先我们知道MyBatis有一个mapper层,来进行sql映射,那么我们将创建一个UserMapper。

public interface UserMapper {

    @Select(value="select * from user where name = #{name} or age = #{age}")
    List<User> getAllUser(@Param("name")String name,@Param("age") Integer age);

    @Select(value="select * from user where name = #{name}")
    User getUser(@Param("name")String name);
}

可以看到上述代码中出现了两个注解 @Select@Param,下面是两个注解的代码

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Param {
    String value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
    String value();
}

我们可以发现,UserMapper是一个接口,他没有办法帮我们干任何事,所以我们需要一个东西——代理MapperFactoryProxy来帮助UserMapper进行数据库操作与值映射。

public class MapperFactoryProxy {

    //存储类型
    private static Map<Class, TypeHandler> typeHandlerMap = new HashMap<>();

    //初始化
    static {
        typeHandlerMap.put(Integer.class,new IntegerHandler());
        typeHandlerMap.put(String.class,new StringHandler());
    }

    public static <T> T getMapper(Class<T> mapper){
        Object o = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{mapper}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Select annotation = method.getAnnotation(Select.class);
                String sql = annotation.value(); //获取注解值


                //需要执行的sql
                ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
                GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler); //解析与存储规则
                String parseSql = genericTokenParser.parse(sql); //解析
                //获取jdbc链接
                Connection connection = GetJDBC.getConnection();
                PreparedStatement statement = connection.prepareStatement(parseSql);
                //存储对应的参数结构
                Map<String, Object> parameterMap = new HashMap<>();
                //获取参数
                Parameter[] parameters = method.getParameters();
                for (int i = 0; i < parameters.length; i++) {
                    String name = parameters[i].getName();
                    Param param = parameters[i].getAnnotation(Param.class);
                    String value = param.value();
                    parameterMap.put(name, args[i]); //与参数是一一对应的args
                    parameterMap.put(value, args[i]);
                }
                //接下来就是赋值的事,我需要知道第几个问号 赋值的是哪一个 总共有多少个问号
                List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
                for (int i = 0; i < parameterMappings.size(); i++) {//存储了问号对应的名字
                    Object value = parameterMap.get(parameterMappings.get(i).getProperty()); //获取值
                    //接下来就需要进行类型的处理 判断他是什么类型的 要怎么判断呢 根据类型
                    Class<?> aClass = value.getClass();
                    typeHandlerMap.get(aClass).setParameter(statement, i + 1, value);
                }
                //执行sql
                statement.execute();
                //获取查询结果
                ResultSet resultSet = statement.getResultSet();
                //封装结果
                List<Object> result = new ArrayList<>();
                //获取方法的返回类型
                Class resultType = null;
                Type genericReturnType = method.getGenericReturnType();
                Boolean mark = false;
                if(genericReturnType instanceof  Class){
                    //不是泛型
                    resultType = (Class) genericReturnType;
                }else if(genericReturnType instanceof ParameterizedType){
                    //是泛型 取集合中的一个
                    Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                    resultType = (Class)actualTypeArguments[0];
                    mark = true;
                }
                //查询返回的字段信息
                ResultSetMetaData metaData = resultSet.getMetaData();
                List<String> columnList = new ArrayList<>();
                for (int i = 0; i < metaData.getColumnCount(); i++) {
                    columnList.add(metaData.getColumnName(i+1));
                }
                //要解析set方法
                Method[] methods = resultType.getMethods();
                //存储set方法
                Map<String,Method> setterMap = new HashMap<>();
                for (int i = 0; i < methods.length; i++) {
                    if(methods[i].getName().contains("set")){ //如果方法名中存在set
                        String substring = methods[i].getName().substring(3);
                        String property = substring.substring(0,1).toLowerCase(Locale.ROOT) + substring.substring(1);
                        setterMap.put(property,methods[i]);
                    }
                }
                while (resultSet.next()) {
                    Object obj = resultType.newInstance();
                    for (int i = 0; i < columnList.size(); i++) {
                        String column = columnList.get(i);
                        Method setMethod = setterMap.get(column);
                        Class<?>[] parameterTypes = setMethod.getParameterTypes();
                        Class<?> returnType =  parameterTypes[0]; //set方法只有一个参数
                        TypeHandler typeHandler = typeHandlerMap.get(returnType);
                        setMethod.invoke(obj,typeHandler.getResult(resultSet,column));
                    }
                    result.add(obj);
                }
                if(mark){
                    return result;
                }else{
                    return result.get(0);
                }
            }
        });
        return (T) o;
    }
}

SQL解析

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;
    }

    public String parse(String text) {
        if (text != null && !text.isEmpty()) {
            int start = text.indexOf(this.openToken);
            if (start == -1) {
                return text;
            } else {
                char[] src = text.toCharArray();
                int offset = 0;
                StringBuilder builder = new StringBuilder();
                StringBuilder expression = null;

                do {
                    if (start > 0 && src[start - 1] == '\\') {
                        builder.append(src, offset, start - offset - 1).append(this.openToken);
                        offset = start + this.openToken.length();
                    } else {
                        if (expression == null) {
                            expression = new StringBuilder();
                        } else {
                            expression.setLength(0);
                        }

                        builder.append(src, offset, start - offset);
                        offset = start + this.openToken.length();

                        int end;
                        for(end = text.indexOf(this.closeToken, offset); end > -1; end = text.indexOf(this.closeToken, offset)) {
                            if (end <= offset || src[end - 1] != '\\') {
                                expression.append(src, offset, end - offset);
                                break;
                            }

                            expression.append(src, offset, end - offset - 1).append(this.closeToken);
                            offset = end + this.closeToken.length();
                        }

                        if (end == -1) {
                            builder.append(src, start, src.length - start);
                            offset = src.length;
                        } else {
                            builder.append(this.handler.handleToken(expression.toString()));
                            offset = end + this.closeToken.length();
                        }
                    }

                    start = text.indexOf(this.openToken, offset);
                } while(start > -1);

                if (offset < src.length) {
                    builder.append(src, offset, src.length - offset);
                }

                return builder.toString();
            }
        } else {
            return "";
        }
    }
}

存储参数

public class ParameterMapping {

    private String property;

    public ParameterMapping(String property) {
        this.property = property;
    }

    public String getProperty() {
        return property;
    }

    public void setProperty(String property) {
        this.property = property;
    }
}

记录参数

public class ParameterMappingTokenHandler implements TokenHandler{

    private List<ParameterMapping> parameterMappings = new ArrayList<>();

    @Override
    public String handleToken(String content) {
        parameterMappings.add(new ParameterMapping(content));
        return "?";
    }

    public List<ParameterMapping> getParameterMappings() {
        return parameterMappings;
    }
}
public interface TokenHandler {
    String handleToken(String content);
}

参数类型解析及参数返回值解析,此处只列举Integer和String两种类型

public interface TypeHandler<T> {
    void setParameter(PreparedStatement parameter,Integer index,T value) throws SQLException;

    T getResult(ResultSet resultSet,String columnName) throws SQLException;
}
public class StringHandler implements TypeHandler<String>{
    @Override
    public void setParameter(PreparedStatement parameter, Integer index, String value) throws SQLException {
        parameter.setString(index,value);
    }

    @Override
    public String getResult(ResultSet resultSet, String columnName) throws SQLException {
        return resultSet.getString(columnName);
    }
}
public class IntegerHandler implements TypeHandler<Integer>{
    @Override
    public void setParameter(PreparedStatement parameter, Integer index, Integer value) throws SQLException {
        parameter.setInt(index,value);
    }
    @Override
    public Integer getResult(ResultSet resultSet, String columnName) throws SQLException {
        return resultSet.getInt(columnName);
    }
}

以上就是全部代码实现。

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值