互联网架构-Mybatis深入源码分析-018:高仿Mybatis框架

1 纯手写Mybatis框架终极版本效果演示

课程内容:
1、纯手写MappedStatement
2、纯手写BaseExecutor
3、纯手写SimpleExecutor
4、纯手写Mybatis源码分析课程总结

2 纯手写MappedStatement

Select注解

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

UserMapper

public interface UserMapper {
    @Select("SELECT * FROM USER WHERE id = ?")
    UserEntity selectUser(Integer id);
}

UserEntity

@Data
public class UserEntity {

    private Integer id;
    private String name;
    private Integer age;

    public UserEntity(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public UserEntity() {
    }

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

MappedStatement 关联sql语句

@Data
public class MappedStatement {

    /**
     * 数据库相关配置
     */
    private Configuration configuration;
    /**
     * sql语句
     */
    private String sqlValue;
    /**
     * sql语句类型
     */
    private Annotation sqlType;
    /**
     * 返回类型
     */
    private Class<?> returnType;
    /**
     * 传递参数
     */
    private Object[] args;

    public MappedStatement(Method method, Object[] args) {
        this.returnType = method.getReturnType();
        this.args = args;
        // 问题:注解获取很麻烦  用一个集合存放当前定义所有的注解 策略模式循环判断
        this.sqlType = method.getAnnotation(Select.class);
        setSqlValue();
    }

    /**
     * 从注解上获取sql语句
     */
    public void setSqlValue() {
        if (sqlType instanceof Select) {
            Select selectAnnotation = (Select) this.sqlType;
            this.sqlValue = selectAnnotation.value();
        }
    }

    public String getMappedStatementKey() {
        // 问题存在,虽然args值是相同的,但是每次args.toString() class地址不同
//        return sqlValue + args == null ? "" : args.toString() + returnType;
        String mappedStatementKey = sqlValue + arrayToString() + returnType.toString();
        System.out.println("mappedStatementKey:" + mappedStatementKey);
        return mappedStatementKey;
    }

    public String arrayToString() {
        String str = "";
        for (int i = 0; i < args.length; i++) {
            Object obj = (Object) args[i];
            str += obj + "";
        }
        return str;
    }
}

3 纯手写BaseExecutor01

SqlSession

public interface SqlSession {
    <T> T getMapper(Class<T> type) throws Exception;
    <T> T selectOne(MappedStatement mappedStatement) throws Exception;
    <E> List<E> selectList(MappedStatement mappedStatement);

}

DefaultSqlSession

public class DefaultSqlSession implements SqlSession {
    private final Configuration configuration;
    private BaseExecutor baseExecutor;

    public DefaultSqlSession(Configuration configuration, BaseExecutor baseExecutor) {
        this.configuration = configuration;
        this.baseExecutor = baseExecutor;
    }

    @Override
    public <T> T getMapper(Class<T> type) throws Exception {
        // 查询configuration是否有注册mapper接口
        MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) configuration.getMapper(type);
        if (mapperProxyFactory == null) {
            throw new Exception("Type" + type + "is not known to the MapperProxyFactory.");
        }
        return (T) mapperProxyFactory.newInstance(this);
    }

    @Override
    public <T> T selectOne(MappedStatement mappedStatement) throws Exception {
        List<T> list = this.selectList(mappedStatement);
        if (list.size() == 1) {
            return list.get(0);
        } else if (list.size() > 1) {
            throw new Exception("Expected one result (or null) to be returned by selectOne(), but not found: " + list.size());
        } else {
            return null;
        }
    }

    @Override
    public <E> List<E> selectList(MappedStatement mappedStatement) {
        mappedStatement.setConfiguration(configuration);
        return baseExecutor.query(mappedStatement);
    }
}

BaseExecutor

public abstract class BaseExecutor {

    private Map<Object, Object> cache = new HashMap<Object, Object>();

    public <E> List<E> query(MappedStatement ms) {
        String mappedStatementKey = ms.getMappedStatementKey();
        // 1.先查询一级缓存,有数据直接返回一级缓存的
        List<E> list = (List<E>) cache.get(mappedStatementKey);
        // 2.如果一级缓存没有查询数据库
        if (list != null) {
            return list;
        }
        return queryFromDatabase(ms);
    }

    /**
     * 查询数据库
     *
     * @param ms
     * @param <E>
     * @return
     */
    private <E> List<E> queryFromDatabase(MappedStatement ms) {
        List<E> list = doQuery(ms);
        // 如果数据库查询不为空 添加到一级缓存中
        if (list != null) {
            cache.put(ms.getMappedStatementKey(), list);
        }
        return list;
    }

    protected abstract <E> List<E> doQuery(MappedStatement ms);
}

4 纯手写BaseExecutor02

DefaultSqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSqlSession() {
        // 使用默认的SqlSession
        return new DefaultSqlSession(configuration, new SimpleExecutor());
    }
}

5 纯手写SimpleExecutor

SimpleExecutor

public class SimpleExecutor extends BaseExecutor {
    @Override
    protected <E> List<E> doQuery(MappedStatement ms) {
        System.out.println("开始执行数据库..." + ms.toString());
        // 1.创建jdbc连接
        Configuration configuration = ms.getConfiguration();
        // 2.预编译执行
        MyBatisJDBCUtil myBatisJDBCUtil = new MyBatisJDBCUtil(configuration.getDataSource());
        List<E> list = (List<E>) myBatisJDBCUtil.queryListExecute(ms.getSqlValue(), ms.getArgs(), ms.getReturnType());
        // 3.封装结果集
        return list;
    }
}

MapperProxy

public class MapperProxy<T> implements InvocationHandler {

    private SqlSession sqlSession;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
        this.sqlSession = sqlSession;
    }

    /**
     * 执行invoke 如何知道具体要执行哪个sql语句
     * 使用method对象获取UserMapper对应sql语句
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 关联对应的StateMent
//        // 1.判断method方法上是否有加注解
//        Annotation annotation = method.getAnnotation(Select.class);
//        if (annotation == null) {
//            throw new Exception("该方法上没有定义sql语句,无法关联MappedStatement");
//        }

        // 后期可以加上缓存控制 mappedStatement 没必要每次new一个MappedStatement
        MappedStatement mappedStatement = new MappedStatement(method, args);
        // 2.获取注解上的sql语句
        return sqlSession.selectOne(mappedStatement);
    }
}

TestMyBatis

public class TestMyBatis {
    public static void main(String[] args) throws Exception {
        // 1.获取默认sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build("my_config.properties");
        SqlSession sqlSession = sqlSessionFactory.openSqlSession();
        // 2.生成userMapper代理类
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 3.执行MapperProxy的invoke方法
        System.out.println("第一次查询>>>>");
        UserEntity userEntity1 = userMapper.selectUser(2);
        System.out.println("userEntity1:" + userEntity1.toString());
        System.out.println("第二次查询>>>>");
        UserEntity userEntity2 = userMapper.selectUser(2);
        System.out.println("userEntity2:" + userEntity2.toString());

    }
}

测试结果:
在这里插入图片描述

6 纯手写Mybatis源码分析课程总结

Mybatis中的一级缓存底层怎么实现? HashMap
存在哪些缺陷?线程安全问题; 集群不共享
实际项目中一级缓存sqlSession是每次都会初始化吗? 是
一级/二级缓存区别? 二级缓存属于第三方缓存,可以存redis或ehCache
一级/二级缓存用到哪些设计模式? 装饰者,二级缓存属于开发者扩展增强,不影响原有一级缓存代码
mybatis mapper接口如何初始化? Jdk动态代理

mybatis用到哪些设计模式?
建造者模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;
工厂模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;
单例模式,例如ErrorContext和LogFactory;
代理模式,Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果;
组合模式,例如SqlNode和各个子类ChooseSqlNode等;
模板方法模式,例如BaseExecutor和SimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler;
适配器模式,例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现;
装饰者模式,缓存设计,例如Cache包中的cache.decorators子包中等各个装饰者的实现;
迭代器模式,例如迭代器模式PropertyTokenizer;

工具类:MyBatisJDBCUtil

package com.mayikt.utils;

import com.mayikt.session.DataSource;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class MyBatisJDBCUtil {
    public static String url = null;
    public static String username = null;
    public static String password = null;
    public static String driver = null;


    public MyBatisJDBCUtil(DataSource dataSource) {
        url = dataSource.getUrl();
        username = dataSource.getUsername();
        password = dataSource.getPassword();
        driver = dataSource.getDriver();
    }

    /**
     * 获取数据库连接对象
     *
     * @return
     */
    public static Connection getConnection() {
        try {
            Connection conn = DriverManager.getConnection(url, username, password);
            return conn;
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 添加删除修改数据
     *
     * @return
     */
    public static boolean insertDeleteUpdateExecute(String sql, ArrayList<Object> paras) {
        Connection conn = getConnection();
        PreparedStatement pst = null;
        boolean flag = false;
        int index = 1;
        try {
            pst = conn.prepareStatement(sql);
            if (paras != null && paras.size() > 0) {
                pst.clearParameters();
                for (int i = 0; i < paras.size(); i++) {
                    pst.setObject(index++, paras.get(i));
                }
            }
            int result = pst.executeUpdate();
            flag = result > 0 ? true : false;
        } catch (SQLException e) {
            flag = false;
            e.printStackTrace();
        } finally {
            close(conn, pst);
        }
        return flag;
    }

    /**
     * 获取单个对象 可用于登录注册验证
     *
     * @param sql
     * @param paras
     * @param cls
     * @return
     */
    public  <T> T findBySingleObject(String sql, ArrayList<Object> paras, Class<T> cls) {
        Connection conn = getConnection();
        PreparedStatement pst = null;
        ResultSet rs = null;
        T singleObject = null;
        int index = 1;

        try {
            pst = conn.prepareStatement(sql);
            if (paras != null && paras.size() > 0) {
                pst.clearParameters();
                for (int i = 0; i < paras.size(); i++) {
                    pst.setObject(index++, paras.get(i));
                }
            }
            rs = pst.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            while (rs.next()) {
                singleObject = cls.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    String columnName = rsmd.getColumnName(i + 1);
                    Object columnValue = rs.getObject(columnName);
                    Field field = cls.getDeclaredField(columnName);
                    field.setAccessible(true);
                    field.set(singleObject, columnValue);
                }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            close(conn, pst);

        }
        return singleObject;

    }

    /**
     * 列表查询
     */
    public  <T> List<T> queryListExecute(String sql, Object[] paras, Class<T> cls) {
        Connection conn = getConnection();
        PreparedStatement pst = null;
        ResultSet rs = null;
        T singleObject = null;
        int index = 1;
        List<T> list = new ArrayList<T>();
        try {
            pst = conn.prepareStatement(sql);
            if (paras != null && paras.length > 0) {
                pst.clearParameters();
                for (int i = 0; i < paras.length; i++) {
                    pst.setObject(index++, paras[i]);
                }
            }
            rs = pst.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            while (rs.next()) {
                singleObject = cls.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    String columnName = rsmd.getColumnName(i + 1);
                    Object columdValue = rs.getObject(columnName);
                    Field field = cls.getDeclaredField(columnName);
                    field.setAccessible(true);
                    field.set(singleObject, columdValue);
                }
                list.add(singleObject);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            close(conn, pst, rs);
        }

        return list;
    }


    /*
     * 释放资源
     */
    public static void close(Connection conn, PreparedStatement pst) {
        if (pst != null) {
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection conn, PreparedStatement pst,
                             ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (pst != null) {
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值