手写实现Mybatis 单表查询

# 手写实现Mybatis —>单表查询

1. Mybatis核心概念

名称意义
Configuration管理mysql-config.xml 全局配置关系类
SqlSessionFactorySession管理工厂接口SessionSqlSession
SessionSqlSession 是一个面向用户(程序员)的接口。SqlSession 中提
Executor作用:SqlSession 内部通过执行器操作数据库
MappedStatement底层封装对象,作用:对操作数据库存储封装,包括 sql 语句、输入输出参数
StatementHandler具体操作数据库相关的 handler 接口
ResultSetHandler具体操作数据库返回结果的 handler 接口

2. Mybatis处理流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mRgaYSdG-1591682568264)(https://img2020.cnblogs.com/blog/1999821/202004/1999821-20200430204019808-2075197123.png)]

  • 各层包搭建树状图

  • │  │  │  └─com
    │  │  │      └─mybatis
    │  │  │          ├─binding
    |  |  |              └─ MapperMethod.class
    |  |  |				 ├─ MapperProxy.class
    |  |  |				 └─ MapperRegister.class
    │  │  │          ├─executor
    |  |  |              └─ MapperMethod.class
    |  |  |				 ├─ MapperProxy.class
    |  |  |				 └─ MapperRegister.class
    │  │  │          ├─resultset
    |  |  |				 └─ DefaultResultHandler.class
    |  |  |				 └─ ResultSetHandler.class
    │  │  │          ├─session
    |  |  |				 └─ Configuration.class
    |  |  |				 ├─ DefaultSQLSession.class
    |  |  |				 ├─ SQLSession.class
    |  |  |				 ├─ SQLSessionFactory.class
    |  |  | 			 └─ sqlSessionFactoryBuilder.class
    │  │  │          └─statement
    │  │  │				 └─ StatementHandler.class
    |  |  |             
    │  │  └─resources
    │  │      └─mybatis
    │  └─test
    │      └─java
    │          └─com
    │              └─qitian
    │                  ├─mapper
    │                  └─pojo
    

3. Mybatis 实现的主要类

  • Configuration

  • SqlSessionFactory

  • Executor

1. Configuration

configuration类的作用主要是读取mybatis-congig.xml的配置文件,以及mapper注册(将配置文件的XXXMapper.xml存储到一个Map集合中)。

Mappper注册

MapperRegister.class

@Data
public class MapperRegister {
    private  Map<String, MapperMethod> knownMappers = new HashMap<String, MapperMethod>();

}

Configuration.class

/**
 * 读取xml文件
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Configuration {
    /**
     * 定义io 读取xml文件
     */
    private InputStream inputStream;
    private MapperRegister mapperRegister = new MapperRegister();

    public void loadCOnfigurations() throws Exception {
        try {
            Document document = new SAXReader().read(inputStream);
            Element root = document.getRootElement();
            //读取mappers下配置的mapper
            List<Element> mappers = root.element("mappers").elements("mapper");
            for (Element mapper : mappers) {
                //获取mapper里的属性resource
                /**
                <!--mapper映射-->
    			<mappers>
        			<mapper resource="XXXMapper.xml"/>
    			</mappers>
                */
                if (mapper.attribute("resource") != null) {
                    mapperRegister.setKnownMappers((Map<String, MapperMethod>) loadXMLConfiguration(mapper.attributeValue("resource")));
                }
                if (mapper.attribute("class") != null) {
                }
            }
        } catch (DocumentException e) {
            System.out.println("读取配置文件出错");
            e.printStackTrace();
        } finally {
            inputStream.close();
        }
    }

    /**
     * 读取XXXMapper.xml的信息
     *
     * @param reource
     * @return
     */
    private Object loadXMLConfiguration(String reource) throws Exception {
        Map<String, MapperMethod> map = new HashMap<String, MapperMethod>();
        InputStream is = null;
        try {
            //获取XXXMapper.xml文件
            is = this.getClass().getClassLoader().getResourceAsStream(reource);
            Document document = new SAXReader().read(is);
            Element root = document.getRootElement();
            if (root.getName().equalsIgnoreCase("mapper")) {
                //读取mapper中的命名空间
                String namespace = root.attribute("namespace").getText();
                //读取查询方法的关键字select
                for (Element select : (List<Element>) root.elements("select")) {
                    MapperMethod mapperMethod = new MapperMethod();
                    mapperMethod.setSql(select.getText().trim());
 //获取返回值的类型
                    mapperMethod.setType(Class.forName(select.attribute("resultType").getText()));
                    map.put(namespace + "." + select.attribute("id").getText(), mapperMethod);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            is.close();
        }
        return map;
    }
}

2. SqlSessionFactory

SqlSessionFactory利用SqlSessionFactoryBuilder.builer(configuration)方法创建对象,将configuration作为参数传入,以便获取mybatis-config.xml的信息,该类的主要作用是用于构建SqlSession对象,SqlSession对象可以通过方法openSession获得。(sqlSessionFactory.openSession(configuration)😉

SqlSessionFactory.class

public class SqlSessionFactory {
    public SqlSession openSession(Configuration configuration){
        return new DefaultSqlSession(configuration,new SimpleExecutor(configuration));
    }
}
public class SqlSessionFactoryBuilder {
    public  SqlSessionFactory build(Configuration configuration) throws Exception {
        configuration.loadCOnfigurations();
        return new SqlSessionFactory();
    }
}

DefaultSqlSession为SqlSession的实现类,主要实现Mapper的动态代理,代理底层主要通过Cglib实现,以及查询方法的实现。

DefaultSqlSession.class

@AllArgsConstructor
@Data
public class DefaultSqlSession implements SqlSession {

    private Configuration configurauion;
    private Executor exceutor;


    public <T> T selectOne(MapperMethod mapperMethod, Object statement) throws Exception {
        System.out.println("=======DefaultSqlSession==========");
        return (T) exceutor.query(mapperMethod,statement);
    }

    public <T> T getMapper(Class<T> type) {
        return (T) Proxy.newProxyInstance(type.getClassLoader(),
                new Class[]{type}, new MapperProxy<T>(this, type));
    }
}

sqlSession.class

public interface SqlSession {
    public <T> T selectOne(MapperMethod mapperMethod, Object statement) throws Exception;

    <T> T  getMapper(Class<T> type);

}

3. Executor

Executor的作用主要是用于执行Sql语句,SimpleExecutor实现Executor的query方法。

Executor.class

public interface Executor {
    <T> T query(MapperMethod mapperMethod, Object statement) throws Exception;
}

SimpleExecutor.class

@Data
@AllArgsConstructor
public class SimpleExecutor<T> implements Executor {

    private Configuration configuration;
    public <T> T query(MapperMethod mapperMethod, Object statement) throws Exception {
        StatementHandler statementHandler = new StatementHandler(configuration);

        System.out.println("============SimpleExecutor===========");
        return statementHandler.query(mapperMethod,statement);

    }
}

DefaultSqlSession类通过sqlSession.getMapper(XXXX.class)获取接口的代理对象

MapperProxy.class 动态代理获取XXXMapper.class

@Data
@AllArgsConstructor
public class MapperProxy<T> implements InvocationHandler {
    private final DefaultSqlSession sqlSession;
    private final Class<T> mapperInterface;

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MapperMethod mapperMethod = sqlSession.getConfigurauion().getMapperRegister()
                .getKnownMappers().get(method.getDeclaringClass().getName() + "." + method.getName()); //获取接口的路径+方法名
        if (mapperMethod != null) {
            //返回存放SQL和类型的mapperMethod和传入参数
            return sqlSession.selectOne(mapperMethod, String.valueOf(args[0]));
        }
        return method.invoke(proxy, args);
    }
}

由getmapper(XXX.class)动态代理对象调用方法执行MapperProxy的invoke方法直接调用DefaultSqlSession的selectOne(MapperMethod mapperMethod, Object statement)查询,MapperMethod为存放SQL语句和对象类型的类,statement为动态代理对象调用方法时传入的参数

MapperMethod.class

/**
存放SQL语句和对象的类型
*/
@Data
public class MapperMethod<T>{
    private String sql;
    private Class<T> type;

    public MapperMethod() {
    }

    public MapperMethod(String sql, Class<T> type) {
        this.sql = sql;
        this.type = type;
    }
}


进入DefaultSQLSession的selectOne方法后,由selectOne方法调用Executor的实现类SimpleExecutor的query方法

SimpleExecutor.class

@Data
@AllArgsConstructor
public class SimpleExecutor<T> implements Executor {

    private Configuration configuration;
    public <T> T query(MapperMethod mapperMethod, Object statement) throws Exception {
        StatementHandler statementHandler = new StatementHandler(configuration);

        System.out.println("============SimpleExecutor===========");
        return statementHandler.query(mapperMethod,statement);

    }
}

之后执行statementHandler的query,执行数据库相关JDBC的方法,

StatementHandler.class

@Data
@AllArgsConstructor
public class StatementHandler {
    private Configuration configuration;
    private ResultSethandler resultSethandler;

    public StatementHandler(Configuration configuration) {
        resultSethandler = new DefaultResultSetHandler();
        this.configuration = configuration;
    }
//查询
    public <T> T query(MapperMethod mapperMethod, Object statement) throws Exception {
        //1.加载驱动程序
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2. 获得数据库连接
        Connection conn = DriverManager.getConnection("url", "admin", "passsword");
        String format = String.format(mapperMethod.getSql(), Integer.valueOf((String) statement));
        PreparedStatement preparedStatement = conn.prepareStatement(format);
        preparedStatement.execute();
        ResultSet resultSet = preparedStatement.getResultSet();
        return resultSethandler.handle(preparedStatement, mapperMethod);
    }
}


返回对象根据ResultSethandler的实现类DefaultResultSetHandler的handle方法判断再返回

DefaultResultSetHandler.class

public class DefaultResultSetHandler implements ResultSethandler {


    public <T> T handle(PreparedStatement preparedStatement, MapperMethod mapperMethod) throws Exception {
        Object resultObj = new DefaultObjectFactory().create(mapperMethod.getType());
        ResultSet resultSet = preparedStatement.getResultSet();
        if (resultSet.next()) { //todo
            int i = 0;
            for (Field field : resultObj.getClass().getDeclaredFields()) {
                setValue(resultObj, field, resultSet, i);
            }
        }
        return (T) resultObj;
    }

    private void setValue(Object resultObj, Field field, ResultSet resultSet, int i) throws Exception {
        Method method = resultObj.getClass().getMethod("set" + upperCapital(field.getName()), field.getType());
        method.invoke(resultObj, getResult(field, resultSet));
    }

    //判断字段的类型
    private Object getResult(Field field, ResultSet resultSet) throws SQLException {
        Class<?> type = field.getType();
        if (Integer.class == type) {
            return resultSet.getInt(field.getName());
        }
        if (String.class == type) {
            return resultSet.getString(field.getName());
        }
        if (Long.class == type) {
            return resultSet.getString(field.getName());
        }
         if (Date.class == type) {
            return resultSet.getString(field.getName());
        }
         if (Dubble.class == type) {
            return resultSet.getString(field.getName());
        }
         if (Float.class == type) {
            return resultSet.getString(field.getName());
        }
         if (Bigdicail.class == type) {
            return resultSet.getString(field.getName());
        }
        return resultSet.getString(field.getName());
    }
	//将拼接字段
    private String upperCapital(String name) {
        String s = name.substring(0, 1);
        String substring = name.substring(1);
        return s.toUpperCase() + substring;
    }
}

测试代码

public class TestMybatis {
    public static void main(String[] args) throws Exception {
        //读取配置文件
        InputStream inputStream = TestMybatis.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        Configuration configuration = new Configuration();
        //将读取到的配置文件字符流设置到configuration中
        configuration.setInputStream(inputStream);

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
        SqlSession sqlSession = sqlSessionFactory.openSession(configuration);
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        Account user =  userMapper.getUserById(123456L);
        System.err.println(user);

    }

}


效果实现图

在这里插入图片描述转载:请标注:https://blog.csdn.net/qq_40482053/article/details/106640184

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值