手动实现一个简单的 MyBatis

手动实现一个简单的 MyBatis,第1张

手动实现一个简单的 MyBatis

 

(一)、创建 SqlSessionFactory 实例。

(二)、实例化过程,加载配置文件创建 Configuration 对象。

(三)、通过 factory 创建 SqlSession。

(四)、通过 SqlSession 获取 mapper 接口动态代理。

(五)、动态代理回调 SqlSession 中某查询方法。

(六)、SqlSession 将查询方法转发给 Executor。

(七)、Executor 基于 JDBC 访问数据库获取数据。

(八)、Executor 通过反射将数据转换成 POJO并返回给 SqlSession。

(九)、将数据返回给调用者。

项目整体使用 Maven 构建,mybatis-demo 是脱离 Spring 的 MyBatis 使用的例子。paul-mybatis 是我们自己实现的 mybatis 框架。



 

首先按照我们以前的使用 mybatis 代码时的流程,创建 mapper 接口,xml 文件,和 POJO以及集一些配置文件。

1、接口:TUserMapper

 

2、xml 文件:

3、实体类,属性应该与数据库想匹配:

 

4、数据库连接配置文件、db.properties:


 

关注 xml 文件,mapper 文件里的 namespace,id,resultType 和 sql 语句都要存储起来,我们定义一个 POJO 来存储这些信息。 

创建一个 Configuration 类,用来保存所有配置文件和 xml 文件里的信息:

 

有了配置类之后,我们可以通过这个配置类构建一个 SqlSessionFactory 了。

5、 SqlSessionFactory 抽象模版



 

Default 实现类主要完成了两个功能,加载配置信息到 Configuration 对象里,实现创建 SqlSession 的功能。

  package com.paul.mybatis.factory;
  import com.paul.mybatis.confiuration.Configuration;
  import com.paul.mybatis.confiuration.MappedStatement;
  import com.paul.mybatis.sqlsession.DefaultSqlSession;
  import com.paul.mybatis.sqlsession.SqlSession;
  import org.dom4j.document;
  import org.dom4j.documentException;
  import org.dom4j.Element;
  import org.dom4j.io.SAXReader;
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStream;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Properties;
  
  public class DefaultSqlSessionFactory implements SqlSessionFactory{
      //希望Configuration 是单例子并且唯一的
      private final Configuration configuration = new Configuration();
      // xml 文件存放的位置
      private static final String MAPPER_CONFIG_LOCATION = "mappers";
      // 数据库信息存放的位置
      private static final String DB_CONFIG_FILE = "db.properties";
      public DefaultSqlSessionFactory() {
          loadDBInfo();
          loadMapperInfo();
      }
      private void loadDBInfo() {
          InputStream db = this.getClass().getClassLoader().getResourceAsStream(DB_CONFIG_FILE);
          Properties p = new Properties();
          try {
              p.load(db);
          } catch (IOException e) {
              e.printStackTrace();
          }
          //将配置信息写入Configuration 对象
          configuration.setJdbcDriver(p.get("jdbc.driver").toString());
          configuration.setJdbcUrl(p.get("jdbc.url").toString());
          configuration.setJdbcUsername(p.get("jdbc.username").toString());
          configuration.setJdbcPassword(p.get("jdbc.password").toString());
      }
      //解析并加载xml文件
      private void loadMapperInfo(){
          URL resources = null;
          resources = this.getClass().getClassLoader().getResource(MAPPER_CONFIG_LOCATION);
          File mappers = new File(resources.getFile());
          //读取文件夹下面的文件信息
          if(mappers.isDirectory()){
              File[] files = mappers.listFiles();
              for(File file:files){
                  loadMapperInfo(file);
              }
          }
      }
      private void loadMapperInfo(File file){
          SAXReader reader = new SAXReader();
          //通过read方法读取一个文件转换成document 对象
          document document = null;
          try {
              document = reader.read(file);
          } catch (documentException e) {
              e.printStackTrace();
          }
          //获取根结点元素对象
          Element e = document.getRootElement();
          //获取命名空间namespace
          String namespace = e.attribute("namespace").getData().toString();
          //获取select,insert,update,delete子节点列表
          List selects = e.elements("select");
          List inserts = e.elements("select");
          List updates = e.elements("select");
          List deletes = e.elements("select");
          List all = new ArrayList<>();
          all.addAll(selects);
          all.addAll(inserts);
          all.addAll(updates);
          all.addAll(deletes);
          //遍历节点,组装成 MappedStatement 然后放入到configuration 对象中
          for(Element ele:all){
              MappedStatement mappedStatement = new MappedStatement();
              String id = ele.attribute("id").getData().toString();
              String resultType = ele.attribute("resultType").getData().toString();
              String sql = ele.getData().toString();
              mappedStatement.setId(namespace+"."+id);
              mappedStatement.setResultType(resultType);
              mappedStatement.setNamespace(namespace);
              mappedStatement.setSql(sql);
              configuration.getMappedStatement().put(namespace+"."+id,mappedStatement);
          }
      }
      @Override
      public SqlSession openSession() {
          return new DefaultSqlSession(configuration);
      }
  }

在 SqlSessionFactory 里创建了 DefaultSqlSession,我们看看它的具体实现。SqlSession里面应该封装了所有数据库的具体 *** 作和一些获取 mapper 实现类的方法。使用动态代理生成一个加强类。这里面最终还是把数据库的相关 *** 作转给 SqlSession,使用 mapper 能使编程更加优雅。

SqlSession 接口,定义模版方法

 

Default 的 SqlSession 实现类。里面需要传入 Executor,这个 Executor 里面封装了 JDBC *** 作数据库的流程。我们重点关注 getMapper 方法。

动态代理的 InvocationHandler

 

最后来看我们的测试类

 

                                                                         需要更多教程,微信扫码即可

                                                                              

                                                                                         

                                                        别忘了扫码领资料哦【高清Java学习路线图】

                                                                     和【全套学习视频及配套资料】
  
 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5438486.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-11
下一篇 2022-12-11

发表评论

登录后才能评论

评论列表(0条)

保存