自定义mybatis框架

在主配置文件中
在这里插入图片描述
自定义mybatis框架_第1张图片
连接数据库的信息,有了他们就能创建Connection对象
自定义mybatis框架_第2张图片
有了它就有了映射配置信息,获得映射配置文件的位置,读到下面的配置文件。

在映射配置文件中
自定义mybatis框架_第3张图片
自定义mybatis框架_第4张图片
有了它就有了执行的SQL语句,就可以获取PreparedSatement
此配置中还有封装的实体类全限制类名。(用什么语句执行,封装到哪里去)

这些个配置信息是一上来我们要做的事情,也就是读成一个流,解析配置文件!
此处用的dom4j的技术,解析xml,读取配置信息。

selectList方法(被封装在session.getMapper中)

  1. 根据配置文件的信息创建Connection对象
    * 注册驱动,获取连接(主配置文件中获取)
  2. 获取预处理对象PreparedSatement
    * 此时需要SQL语句 conn.prepareStatement(sql);(在映射配置文件中获取sql语句)
  3. 执行查询
    * ResultSet resultSet=preparedStatement.executeQuery();
  4. 遍历结果集用于封装:
    Listlist=new ArrayList();
    while(reulstSet.next()){
    //创建对象,将查询出的结果封装到对象中
    E element=(E)Class.forName(配置的全限定类名).newInstance()
    (此时根据映射配置文件中的resultType,获取返回值的对象的全限定类名,实例化出来。我们的实体类属性和表中的列名是一致的,于是我们就可以把表的列名看成是实体类的属性名称,就可以使用反射的方式来根据名称获取每个属性,并把值赋值进去)
    进行封装,把每个rs的内容都添加到element中
    list.add(element);
    }

这些所有的方法都被mybatis封装,此时要想使上述方法执行,我们需要给方法提供两个信息。

  • 第一个:连接信息
  • 第二个:映射信息:
    • 它包含了两个部分:
      • 第一:执行的SQL语句
      • 第二: 封装结果的实体类的全限定类名
        把这两个信息组合起来定义成一个对象Mapper

由于将来会有多个查询方法,对应会有多个Mapper对象,所以将其存储到map集合中key: namespace + id value:Mapper对象
自定义mybatis框架_第5张图片
在这里插入图片描述

//根据dao接口的字节码创建dao的代理对象
Public T getMapper(Class daoInterfaceClass){

/*
类加载器:它使用的和被代理对象是相同的类加载器
代理对象要实现的接口:和被代理对象实现相同的接口
如何代理:它就是增强的方法、我们需要自己来提供。
此处是一个InvocationHandler的接口,我们需要写一个该接口的实现类,在实现类中调用selectList方法。

*/

Proxy.newProxyInstance(类加载器、代理对象要实现的接口的字节码数组,如何代理);

}
即上述的selectList方法在动态代理的InvocationHandler的接口中实现。

自定义练习实现mybatis
自定义mybatis框架_第6张图片

  1. 实现读取配置
    在这里插入图片描述
public class Resources {
    /** 使用类加载器读取配置文件的类
     * 根据传入的参数,获取一个字节输入流
     * @param filePath
     * @return
     */
    public static InputStream getResourceAsStream(String filePath){
        return Resources.class.getClassLoader().getResourceAsStream(filePath);
    }

  1. 创建生产者SqlSessionFactoryBuilder,其中有个方法应该返回的是SqlSessionFactory
    在这里插入图片描述
 
 public class SqlSessionFactoryBuilder {
    /**用于创建一个SqlSessionFactory对象
     * 根据参数的字节输入流来构建一个SqlSessionFactory工厂
     * @param config
     * @return
     */
    //返回值是SqlSessionFactory一个接口
    public SqlSessionFactory build(InputStream config){
      //XMLConfigBuilder是使用dom4j+xpath解析的主配置文件所提取的封装对象。
      Configuration cfg = XMLConfigBuilder.loadConfiguration(config);
        //DefaultSqlSessionFactory是SqlSessionFactory接口的实现类
        return  new DefaultSqlSessionFactory(cfg);
    }


  1. 调用SqlSessionFactoryBuilder中的方法,得到SqlSessionFactory,其中应该有个方法是返回一个SqlSession对象。
    在这里插入图片描述
 
创建SqlSessionFactory接口:
public interface SqlSessionFactory {
    /**
     * 用于打开一个新的SqlSession对象
     * @return
     */
    SqlSession openSession();
}
创建SqlSessionFactory接口的实现类:
public class DefaultSqlSessionFactory implements SqlSessionFactory{
    private Configuration cfg;
    public DefaultSqlSessionFactory(Configuration cfg){
        this.cfg = cfg;
    }

    /**SqlSessionFactory接口的实现类
     * 用于创建一个新的操作数据库对象
     * @return
     */
    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(cfg);
    }
}

4.调用SqlSessionFactory中的方法,得到SqlSession对象,其中有个方法应该是getMapper,参数是dao的接口字节码
在这里插入图片描述

创建SqlSession接口:
public interface SqlSession {
    /**它里面可以创建dao接口的代理对象
     * 根据参数创建一个代理对象
     * @param daoInterfaceClass dao的接口字节码
     * @param  泛型先声明在使用,声明在使用之前
     * @return
     */
     T getMapper(Class daoInterfaceClass);
    /**
     * 释放资源
     */
    void close();
}
创建SqlSession接口的实现类:
public class DefaultSqlSession implements SqlSession {
    private Configuration cfg;
    private Connection connection;
    public DefaultSqlSession(Configuration cfg){
        this.cfg = cfg;
        connection = DataSourceUtil.getConnection(cfg);
    }
    /**SqlSession接口的实现类
     * 用于创建代理对象
     * @param daoInterfaceClass dao的接口字节码
     * @param 
     * @return
     */
    @Override
    public  T getMapper(Class daoInterfaceClass) {
        return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),
                new Class[]{daoInterfaceClass},new MapperProxy(cfg.getMappers(),connection));
    }
    /**
     * 用于释放资源
     */
    @Override
    public void close() {
        if(connection != null) {
            try {
                connection.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

创建动态代理的代理方法,实现InvocationHandler接口
public class MapperProxy implements InvocationHandler {

    //map的key是全限定类名+方法名
    private Map mappers;
    private Connection conn;

    public MapperProxy(Map mappers,Connection conn){
        this.mappers = mappers;
        this.conn = conn;
    }

    /**
     * 用于对方法进行增强的,我们的增强其实就是调用selectList方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1.获取方法名
        String methodName = method.getName();
        //2.获取方法所在类的名称
        String className = method.getDeclaringClass().getName();
        //3.组合key
        String key = className+"."+methodName;
        //4.获取mappers中的Mapper对象
        Mapper mapper = mappers.get(key);
        //5.判断是否有mapper
        if(mapper == null){
            throw new IllegalArgumentException("传入的参数有误");
        }
        //6.调用工具类执行查询所有
        return new Executor().selectList(mapper,conn);
    }


5.调用SqlSession对象中的getMapper方法,创建dao接口的代理对象
在这里插入图片描述
分析:
我们需要最后实现查询所有,查询所有的功能在Executor()工具类中,最后就是要调用Executor()工具类。
什么时候调用呢?就是创建dao实现类增强的时候。
那什么时候创建dao的实现类呢?就是创建代理对象实现getMapper方法的时候,在getMapper方法中调用InvocationHandler 的实现类MapperProxy,其中MapperProxy类就干了从某个地找出来要执行的语句和封装的结果。
从哪找要执行的语句和封装的结果呢?就是配置文件中,XMLConfigBuilder负责读取,读取完后存储到Configuration对象中。

你可能感兴趣的:(框架)