Mybatis注解手写源码篇

简介: Mybatis注解手写源码篇

通过手写模拟Mybatis底层源码实现,了解参数解析底层原理,了解执行SQL底层原理,了解Mybatis结果处理底层原理

目录

启动

public class MyApplication {
    public static void main(String[] args) {
        UserMapper mapper = ProxyFactory.getMapper(UserMapper.class);
        List<User> list = mapper.getUser("root", "123");
        System.out.println(list);
        User user = mapper.getUser(2);
        System.out.println(user);
    }
}

代理工厂类ProxyFactory

package com.example.demo.mybatis;
import com.example.demo.User;
import java.lang.reflect.*;
import java.sql.*;
import java.util.*;
public class ProxyFactory {
    static String url="jdbc:mysql://localhost:3306/springcloud?characterEncoding=utf8";
    static String username="root";
    static String userpwd="123456";
    private static Map<Class,TypeHandler> typeHandlerMap=new HashMap<>();
    static {
        typeHandlerMap.put(String.class,new StringTypeHandler());
        typeHandlerMap.put(Integer.class,new IntegerTypeHandler());
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static <T> T getMapper(Class<T> mapper){
        Object proxyInstance = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{mapper}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Connection connection = DriverManager.getConnection(url, username, userpwd);
                Select annotation = method.getAnnotation(Select.class);
                String value = annotation.value();
                Map<String,Object> map=new HashMap<>();
                //获得方法参数名称存放到map集合中,键为参数名称,值为参数数值
                Parameter[] parameters = method.getParameters();
                for (int i=0;i<parameters.length;i++) {
                    String value1 = parameters[i].getAnnotation(Param.class).value();
                    map.put(value1,args[i]);
                    map.put(parameters[i].getName(),args[i]);
                }
                //Mybatis占位符解析器
                ParameterMappingTokenHandler parameterMappingTokenHandler=new ParameterMappingTokenHandler();
                GenericTokenParser genericTokenParser=new GenericTokenParser("#{","}",parameterMappingTokenHandler);
                String parse = genericTokenParser.parse(value);
                //执行sql语句
                PreparedStatement preparedStatement = connection.prepareStatement(parse);
                //获得sql语句{}内的参数名称
                List<ParameterMapping> parameterMappingList = parameterMappingTokenHandler.getParameterMappingList();
                for (int i=0;i<parameterMappingList.size();i++) {
                    String property = parameterMappingList.get(i).getProperty();
                    Object o = map.get(property);
                    Class<?> type = o.getClass();
                    //根据参数的不同类型执行不同preparedStatement的set方法
                    typeHandlerMap.get(type).setParameter(preparedStatement,i+1,o);
                }
                ResultSet resultSet = preparedStatement.executeQuery();
                //获得方法的返回类型,根据是否是泛型进行判断,获得实体类的Class对象
                Class resultType=null;
                Type genericReturnType = method.getGenericReturnType();
                if(genericReturnType instanceof Class){
                    resultType=(Class)genericReturnType;
                }
                else if(genericReturnType instanceof ParameterizedType){
                    Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                    resultType=(Class) actualTypeArguments[0];
                }
                //获取数据库字段
                ResultSetMetaData metaData = resultSet.getMetaData();
                List<String> list=new ArrayList<>();
                for(int i=0;i<metaData.getColumnCount();i++){
                    list.add(metaData.getColumnName(i+1));
                }
                //将实体类的所有的set方法放到map集合中,键为属性名,值为对应的set方法
                Map<String,Method> methodMap=new HashMap<>();
                for (Method declaredMethod : resultType.getDeclaredMethods()) {
                    if(declaredMethod.getName().startsWith("set")){
                        String substring = declaredMethod.getName().substring(3);
                        substring=substring.substring(0,1).toLowerCase(Locale.ROOT)+substring.substring(1);
                        methodMap.put(substring,declaredMethod);
                    }
                }
                List<Object> resultlist=new ArrayList<>();
                Object result=null;
                while (resultSet.next()){
                    Object instance = resultType.newInstance();//实体类必须有无参构造对象
                    for(int i=0;i<list.size();i++){
                        String column = list.get(i);//数据库字段
                        Method method1 = methodMap.get(column);//获得对应的set方法
                        Class clazz = method1.getParameterTypes()[0];//获得set方法参数
                        TypeHandler typeHandler = typeHandlerMap.get(clazz);
                        method1.invoke(instance,typeHandler.getResult(resultSet,column));
                    }
                    resultlist.add(instance);
                }
                if(method.getReturnType().equals(List.class)){
                    result=resultlist;
                }else {
                    result=resultlist.get(0);
                }
                connection.close();
                return result;
            }
        });
        return (T) proxyInstance;
    };
}

TypeHandler接口

public interface TypeHandler<T> {
    void setParameter(PreparedStatement preparedStatement,int i,T t);
    T getResult(ResultSet resultSet,String columnName);
}

StringTypeHandler

public class StringTypeHandler implements TypeHandler<String>{
    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, String s) {
        try {
            preparedStatement.setString(i,s);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    @Override
    public String getResult(ResultSet resultSet, String columnName) {
        try {
            return resultSet.getString(columnName);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }
}

注:源码下载链接在我的文件资源:https://download.csdn.net/download/qq_43649937/87513406

番外篇


由于文字代码太多,文字太少,这里写个java学习路线总结


对于想要学习Java编程语言的人来说,制定学习路线是非常重要的。在这篇文章中,我将为您提供一个基础的Java学习路线,帮助您更快地掌握Java编程语言。


Java 基础


首先,您需要了解Java基础知识。这些知识包括Java语法、变量和数据类型、运算符、流程控制语句、数组、类和对象等。这个阶段最好通过阅读一些Java入门书籍或者在线教程来学习。我的建议是,如果您已经有编程经验,可以选择一些更深入的学习资源,例如《Java核心技术》或者《Thinking in Java》。


面向对象编程


Java 是一种面向对象的程序设计语言。因此,在学习Java时,必须熟悉面向对象编程思想。这个阶段,您应该学习对象、类、封装、继承、多态等面向对象编程的概念。我的建议是在学习Java的同时,也可以去学习其他语言的面向对象编程,如C++或Python等。


Java 集合框架


Java集合框架是Java中最重要的部分之一。它包括了Java中大部分用于数据存储和处理的类和接口。了解集合框架非常重要,因为它们是您在编写Java程序时必须使用的最常见工具之一。我的建议是学习ArrayList、LinkedList、HashMap、HashSet等基本的集合类。


Java 输入输出流


在Java编程中,输入输出流是必不可少的。Java提供了各种各样的类和接口来访问文件、网络和其他设备上的数据。这个阶段,您应该学习如何使用Java的输入输出流来读取和写入文件、网络连接和其他数据源。我的建议是学习Java.io包中的InputStream和OutputStream类。


Java 网络编程


Java 是一个广泛用于网络编程的语言。了解Java网络编程对于开发网络应用程序非常重要。这个阶段,您应该去了解Java网络编程所需要的Socket编程、TCP协议和UDP协议。我的建议是跟随一些使用Java进行网络编程的教程学习,例如《Java网络编程》或者《Head First Java》。


Java 多线程编程


Java 实现多线程编程非常容易,并且可以有效地提高程序的性能。了解多线程编程对于开发复杂的应用程序至关重要。这个阶段,您应该学习Java中的线程模型、线程安全、同步和锁等概念。我的建议是学习Java中的Thread类和Runnable接口。


Java 数据库编程


Java 与数据库的连接对于许多应用程序来说是至关重要的。Java提供了一个称为JDBC(Java Database Connectivity)的API,它允许您使用Java编写与各种关系型数据库进行交互的应用程序。这个阶段,您应该学习如何使用JDBC API来连接数据库、执行SQL语句和处理结果集。我的建议是学习Java中的JDBC技术。


Servlet 和 JSP


Servlet 和 JSP 是Java Web开发的基础。Servlet 允许在Web服务器上运行Java代码,而JSP 允许您在HTML文档中嵌入Java代码。这个阶段,您应该学习如何编写Servlet和JSP以及如何将它们部署到Web服务器上。我的建议是学习Java EE平台相关的知识,如Tomcat或者其他应用服务器。


SSM ,Springboot, Springcloud等框架


Spring 是一个非常流行的Java应用程序开发框架。它提供了大量的组件和工具,以帮助您更轻松地编写高质量的Java应用程序。Spring家族非常强大。


总结


以上就是基础的Java学习路线总结。学习Java需要长时间的投入和练习,但如果您按照上述路线进行学习,您将能够快速地掌握Java编程语言并开始开发自己的Java应用程序。记住,不要害怕犯错,不断尝试和实践,这是成为一名优秀Java程序员的关键。

相关文章
|
19天前
|
SQL XML Java
【mybatis】第二篇:@Select注解中加入字段判断
【mybatis】第二篇:@Select注解中加入字段判断
|
19天前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——实体层(User.java)
mybatis简单案例源码详细【注释全面】——实体层(User.java)
16 0
|
7天前
|
XML Java 数据库连接
【MyBatisPlus】快速入门、常用注解、常用配置
【MyBatisPlus】快速入门、常用注解、常用配置
13 0
|
19天前
|
SQL Java 数据库连接
一文细说Mybatis八大核心源码
以上 是V哥给大家整理的8大核心组件的全部内容,为什么说选择 Java 就是选择未来,真正爱 Java 的人,一定喜欢深入研究,学习源码只是第一步,要有一杆子捅到操作系统才够刺激。
|
19天前
|
SQL 缓存 Java
|
19天前
|
XML Java 数据库连接
探秘MyBatis:手写Mapper代理的源码解析与实现
探秘MyBatis:手写Mapper代理的源码解析与实现
26 1
|
19天前
|
SQL Java 数据库连接
深入源码:解密MyBatis数据源设计的精妙机制
深入源码:解密MyBatis数据源设计的精妙机制
42 1
深入源码:解密MyBatis数据源设计的精妙机制
|
19天前
|
存储 关系型数据库 MySQL
【mybatis-plus】Springboot+AOP+自定义注解实现多数据源操作(数据源信息存在数据库)
【mybatis-plus】Springboot+AOP+自定义注解实现多数据源操作(数据源信息存在数据库)
|
19天前
|
Java 数据库连接 mybatis
mybatis plus字段为null或空字符串把原来的数据也更新了,只需要注解
mybatis plus字段为null或空字符串把原来的数据也更新了,只需要注解
27 0
|
19天前
|
Java 数据库连接 网络安全
mybatis使用全注解的方式案例(包含一对多关系映射)
mybatis使用全注解的方式案例(包含一对多关系映射)
13 0