手撕实现Mybatis框架(1)

一.mybatis的配置信息

1.Mybatis的主配置文件

<configuration>
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境 -->
        <environment id="mysql">
            <!-- 配置事物类型 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 指定映射配置文件-->
    <mappers>
        <mapper resource="com/itheima/dao/UserDao.xml"/>
    </mappers>
</configuration>

2.Mybatis的映射配置文件

	<mapper namespace="com.itheima.Dao.UserDao">
    <select id="findAll" resultType="com.itheima.domain.User">
        select * from user
    </select>
</mapper>
  • 可以看出,一个主配置文件可以配多个映射配置文件,在每个映射配置文件中,通过全限类名和id名可以唯一的确定这个映射配置文件。
  • 每个映射配置文件中,有返回类型的全限类名,和sql语句。在以上配置中,我们不需要编写Dao的实现类,就可以执行sql语句,得到返回结果。这是因为mybatis内部使用动态代理和反射的方法来实现的。

二.mybatis的执行过程

    public static void main(String[] args) throws IOException {
        //1.读取配置文件
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建sqlsessionFactory工厂
        //可以看到SqlSession是一个接口,必然有一个实现类实现该接口
        SqlSessionFactory factory = null;
        //sqlsessionFactory构建者生成SqlSessionFactory对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        factory = builder.build(inputStream);
        //3.使用工厂生产sqlsession SqlSession是一个接口,通过工厂模式返回一个实例
        SqlSession sqlSession = factory.openSession();
        //4.使用sqlsession创建dao接口的代理对象
        //通过该对象的getMapper方法,
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        //5.使用代理对象执行方法
        List<User> userList = userDao.findAll();
        for(User u:userList){
            System.out.println("u = " + u);
        }public static void main(String[] args) throws IOException {
        //1.读取配置文件
        InputStream inputStream =   Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建sqlsessionFactory工厂
        //可以看到SqlSession是一个接口,必然有一个实现类实现该接口
        SqlSessionFactory factory = null;
        //sqlsessionFactory构建者生成SqlSessionFactory对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        factory = builder.build(inputStream);
        //3.使用工厂生产sqlsession SqlSession是一个接口,通过工厂模式返回一个实例
        SqlSession sqlSession = factory.openSession();
        //4.使用sqlsession创建dao接口的代理对象
        //通过该对象的getMapper方法,
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        //5.使用代理对象执行方法
        List<User> userList = userDao.findAll();
        for(User u:userList){
            System.out.println("u = " + u);
        }
        //6.释放资源
        sqlSession.close();
    }
  • 阅读源码知道,SqlSessionFactory是接口,SqlSessionFactoryBuilder是一个类,SqlSession是一个接口。接口类型的引用类型说明在内部就创建好了实例对象进行返回,根据编译看左边,运行看右边的机制,这其实是一种多态的使用,只是我们在外部看不出来调用的细节。
  1. 通过配置文件加载配置信息
  2. 通过建造者模式创建SqlSessionFactory实例对象
  3. 通过SqlSessionFactory实例对象的OpenSession方法来创建SqlSession的实例对象
  4. 通过SqlSession.Mapper方法进行动态代理Dao层接口的方法,实现方法的加强。
    在这里插入图片描述

三.实现自定义mybatis框架

  • 1.工具类
    • 解析器类,解析xml文件
    • Excutor类,执行sql语句
      -DataSourceUtil类,获取连接对象
  • 2.配置文件信息类
  • 配置信息有,数据库的连接信息,数据库的映射信息。
  • 数据库的连接信息,数据库加载的四大要素 username,password,数据库驱动,url地址
  • 数据库的映射信息, <mappers>中对应的<mapper>信息。<mapper></mapper>中的信息可以通过全限域名和id进行唯一区分mapper。
  • Config类,其中有数据库加载的四大要素信息,和mappers<String,mapper>
  • Mapper类,属性有执行的sql语句,和要封装的返回类型的全限名称
  • 3.SqlSessionFactory接口
    SqlSessionFactory 实现类
  • 4 SqlSessionBuilder类
  • 5 SqlSession接口
    SqlSession 实现类
  • 6 代理类(mybatis的代理是基于接口的动态代理)

在这里插入图片描述
源码如下,亲测成功运行,包结构
在这里插入图片描述


public class Configuration {
    private Map<String, Mapper> mappers = new HashMap<String, Mapper>();
    public String getDriver() {
        return Driver;
    }

    public void setDriver(String driver) {
        Driver = driver;
    }

    private String Driver;

    @Override
    public String toString() {
        return "Configuration{" +
                "Driver='" + Driver + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", url='" + url + '\'' +
                ", mappers=" + mappers +
                '}';
    }

    private String username;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }


    public Map<String, Mapper> getMappers() {
        return mappers;
    }

    public void setMappers(Map<String, Mapper> mappers) {
        //
        this.mappers.putAll(mappers);
    }

    private String password;
    private String url;


}



public class Mapper {
    /**
     * QueryString  sql语句
     * ResultType   返回类型
     * @return
     */
    public String getQueryString() {
        return queryString;
    }

    public void setQueryString(String quertString) {
        this.queryString = quertString;
    }

    public String getResultType() {
        return ResultType;
    }

    public void setResultType(String resultType) {
        ResultType = resultType;
    }

    private String queryString;
    private String ResultType;
}


public class SqlSessionFactoryBuilder {
    public static SqlSessionFactory build(InputStream config){
        //加载配置信息
        Configuration configuration = XMLConfigBuilder.loadConfiguration(config);
        //
        System.out.println("configuration = " + configuration);
        return new DefaultSqlSessionFactory(configuration);
    }
}


public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private Configuration configuration;
    //得到配置信息
    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }
    //返回sqlSession实例对象
    public SqlSession openSession() {
        System.out.println("Facoryconfiguration = " + configuration);
        return new DefaultSqlSession(configuration);
    }
}


public interface SqlSessionFactory {
    SqlSession openSession();
}


public class Resources {
    /**
     *  获取配置信息资源
     */
    public static InputStream getResourceAsStream(String filepath){
        return Resources.class.getClassLoader().getResourceAsStream(filepath);
    }
}


public class Daoproxy implements InvocationHandler {
    private Map<String,Mapper> mappers;
    private Connection connection;
    public Daoproxy(Map<String, Mapper> mappers, Connection connection) {
        this.mappers = mappers;
        this.connection = connection;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //因为mybatis规定配置的id必须和方法名称一致
        String methodname = method.getName();
        //mappers的key是全域class+method
        String classname = method.getDeclaringClass().getName();
        System.out.println("classname = " + classname);
        System.out.println("methodname = " + methodname);
        System.out.println("connection = " + connection);
        //拼接key
        String key = classname+"."+methodname;
        Mapper mapper = mappers.get(key);
        return new Executor().selectList(mapper,connection);
    }
}


public class DefaultSqlSession implements SqlSession {
    private Configuration configuration;
    private Connection connection;
    public Connection getConnection(){
        Connection connection = DataSourceutils.getConnection(configuration);
        this.connection = connection;
        return connection;
    }
    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    public <T> T getMapper(Class<T> daoInterfaceClass) {
        //进行代理 代理类Daoproxy
        //怎么进行代理?
        //在mappers中找到对应要执行的方法
        //怎么找?
        //当前类的名称,调用的方法名称
        //调用返回的T的对象的方法,实际上是在调用DaoProxy的invoke方法
        return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),
                new Class[]{daoInterfaceClass},new Daoproxy(configuration.getMappers(),this.getConnection()));
    }

    public void close()  {
        if(connection!=null){
            try {
                connection.close();
            } catch (SQLException e) {

            }
        }
    }
}


public interface SqlSession {
    public <T> T getMapper(Class<T> daoInterfaceClass);

    void close();
    public Connection getConnection();
}


public class DataSourceutils {
    public static Connection getConnection(Configuration cfg){
        try {
            Class.forName(cfg.getDriver());
            return DriverManager.getConnection(cfg.getUrl(), cfg.getUsername(), cfg.getPassword());
        }catch(Exception e){
            e.printStackTrace();
            throw new RuntimeException();
        }
    }
}


public class Executor {

    public <E> List<E> selectList(Mapper mapper, Connection conn) {
        PreparedStatement pstm = null;
        ResultSet rs = null;
        try {
            //1.取出mapper中的数据
            String queryString = mapper.getQueryString();//select * from user
            String resultType = mapper.getResultType();//com.itheima.domain.User
            Class domainClass = Class.forName(resultType);
            //2.获取PreparedStatement对象
            pstm = conn.prepareStatement(queryString);
            //3.执行SQL语句,获取结果集
            rs = pstm.executeQuery();
            //4.封装结果集
            List<E> list = new ArrayList<E>();//定义返回值
            while(rs.next()) {
                //实例化要封装的实体类对象
                E obj = (E)domainClass.newInstance();

                //取出结果集的元信息:ResultSetMetaData
                ResultSetMetaData rsmd = rs.getMetaData();
                //取出总列数
                int columnCount = rsmd.getColumnCount();
                //遍历总列数
                for (int i = 1; i <= columnCount; i++) {
                    //获取每列的名称,列名的序号是从1开始的
                    String columnName = rsmd.getColumnName(i);
                    //根据得到列名,获取每列的值
                    Object columnValue = rs.getObject(columnName);
                    //给obj赋值:使用Java内省机制(借助PropertyDescriptor实现属性的封装)
                    PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:实体类的属性和数据库表的列名保持一种
                    //获取它的写入方法
                    Method writeMethod = pd.getWriteMethod();
                    //把获取的列的值,给对象赋值
                    writeMethod.invoke(obj,columnValue);
                }
                //把赋好值的对象加入到集合中
                list.add(obj);
            }
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            release(pstm,rs);
        }
    }


    private void release(PreparedStatement pstm,ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }

        if(pstm != null){
            try {
                pstm.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

调用流程图
源码会发到github上在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现MyBatis框架可以分为以下几个步骤: 1. 创建Maven工程并引入需要的依赖坐标。\[3\] 2. 编写Mybatis-config.xml配置文件,配置数据库连接信息、映射文件路径等。\[3\] 3. 创建XMLConfigBuilder类,用于解析Mybatis-config.xml配置文件。\[3\] 4. 创建Executor类,用于执行SQL语句并返回结果。\[3\] 5. 创建Mapper接口和对应的Mapper.xml文件,定义SQL语句和映射关系。\[3\] 6. 创建自定义的Mybatis类,包括读取配置文件、创建SqlSessionFactory、获取SqlSession等功能。\[3\] 7. 创建SqlSessionFactory接口和实现类,用于创建SqlSession对象。\[3\] 8. 创建SqlSession接口和实现类,用于执行SQL语句并返回结果。\[3\] 9. 创建Dao接口代理对象的类,用于动态生成Dao接口的实现类。\[3\] 10. 创建DataSourceUtil类,用于获取数据库连接。\[3\] 11. 编写测试类,测试自定义的Mybatis框架是否能够正常执行SQL语句并返回结果。\[3\] 通过以上步骤,我们可以实现一个简易的MyBatis框架,用于实现对数据库的操作。这个框架本质上是对JDBC进行了封装,并使用各种优化来解决使用JDBC的一些痛点问题。\[2\] #### 引用[.reference_title] - *1* *2* [二、MyBatis简易版框架](https://blog.csdn.net/weixin_36091079/article/details/129798863)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [史上最全的自定义mybatismybatis框架](https://blog.csdn.net/weixin_43570367/article/details/103244430)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值